@curless/hotel-storefront 0.45.1 → 0.46.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.
- 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/config.d.ts +36 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +8 -0
- package/dist/config.js.map +1 -1
- package/dist/data.d.ts +2 -0
- package/dist/data.d.ts.map +1 -1
- package/dist/data.js +281 -19
- package/dist/data.js.map +1 -1
- package/dist/demo-hotels.d.ts +52 -1
- package/dist/demo-hotels.d.ts.map +1 -1
- package/dist/demo-hotels.js +45 -11
- package/dist/demo-hotels.js.map +1 -1
- package/dist/huazhu/backend.d.ts.map +1 -1
- package/dist/huazhu/backend.js +9 -5
- package/dist/huazhu/backend.js.map +1 -1
- package/dist/legal.d.ts.map +1 -1
- package/dist/legal.js +38 -9
- package/dist/legal.js.map +1 -1
- package/dist/lib.d.ts +2 -2
- package/dist/lib.d.ts.map +1 -1
- package/dist/lib.js +1 -1
- package/dist/lib.js.map +1 -1
- package/dist/room-filters.d.ts +76 -0
- package/dist/room-filters.d.ts.map +1 -0
- package/dist/room-filters.js +191 -0
- package/dist/room-filters.js.map +1 -0
- package/dist/server.d.ts +2 -2
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +8 -4
- package/dist/server.js.map +1 -1
- package/dist/supplier.d.ts +1 -0
- package/dist/supplier.d.ts.map +1 -1
- package/dist/supplier.js +5 -1
- package/dist/supplier.js.map +1 -1
- package/dist/surface.d.ts.map +1 -1
- package/dist/surface.js +5 -1
- package/dist/surface.js.map +1 -1
- package/dist/tools.d.ts +20 -2
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +158 -9
- package/dist/tools.js.map +1 -1
- package/dist/types.d.ts +36 -4
- package/dist/types.d.ts.map +1 -1
- package/dist/vocab.d.ts +19 -0
- package/dist/vocab.d.ts.map +1 -1
- package/dist/vocab.js +48 -2
- package/dist/vocab.js.map +1 -1
- package/package.json +1 -1
package/dist/demo-hotels.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { HotelBackend } from './backend.js';
|
|
2
|
+
import type { DemoCapabilities } from './config.js';
|
|
2
3
|
import type { StateFile } from './state.js';
|
|
3
|
-
import type { Booking, ExtraKind, HotelNames, HotelPolicyTopic, Names, TripLine } from './types.js';
|
|
4
|
+
import type { BedType, Booking, ExtraKind, HotelNames, HotelPolicyTopic, MealPlan, Names, RateLabel, TripLine, WindowKind } from './types.js';
|
|
4
5
|
/**
|
|
5
6
|
* The hotels, out of a JSON file, priced by us.
|
|
6
7
|
*
|
|
@@ -17,23 +18,63 @@ import type { Booking, ExtraKind, HotelNames, HotelPolicyTopic, Names, TripLine
|
|
|
17
18
|
* - a date nobody gave: no stay, no price,
|
|
18
19
|
* - a cancellation fee: previewed with its rule before anything is cancelled.
|
|
19
20
|
*/
|
|
21
|
+
/**
|
|
22
|
+
* One thing that can be sold: a room (the one-level file, one price) or ONE
|
|
23
|
+
* RATE of a room type (the two-level file, `rates` - see data.ts). The reader
|
|
24
|
+
* (`parseHotelData`) turns the second into the first: a type with three rates
|
|
25
|
+
* is three of these, each carrying the type in `roomType`, so everything below
|
|
26
|
+
* sells, holds and prices a `RawRoom` and never asks which kind of file it came
|
|
27
|
+
* from.
|
|
28
|
+
*/
|
|
20
29
|
export type RawRoom = {
|
|
30
|
+
/** A room's id; a rate's id, unique in the hotel - what a trip holds. */
|
|
21
31
|
id: string;
|
|
32
|
+
/** A room's name; a rate's is the whole offer's - "Deluxe King · Flexible". */
|
|
22
33
|
name: string;
|
|
23
34
|
/** `name` in the card's languages - see `Room.names`. */
|
|
24
35
|
names?: Names;
|
|
25
36
|
sleeps: number;
|
|
26
37
|
sizeSqm?: number;
|
|
27
38
|
beds?: string;
|
|
39
|
+
/** What `beds` says, as codes - see `Room.bedTypes`. */
|
|
40
|
+
bedTypes?: BedType[];
|
|
28
41
|
view?: string;
|
|
42
|
+
/** The window `view` is about, as a code - see `Room.window`. */
|
|
43
|
+
window?: WindowKind;
|
|
44
|
+
/** Whether the rate includes breakfast: a rate's is "one or more". */
|
|
29
45
|
breakfast: boolean;
|
|
30
46
|
imageUrl?: string;
|
|
47
|
+
/** Every photograph, `imageUrl` first - see `Room.images`. */
|
|
48
|
+
images?: string[];
|
|
49
|
+
/** Which floors - see `Room.floor`. */
|
|
50
|
+
floor?: string;
|
|
31
51
|
/** Written the way a human writes it - 3800, not 380000. */
|
|
32
52
|
nightly: number;
|
|
33
53
|
roomsLeft: number;
|
|
34
54
|
refundable: boolean;
|
|
35
55
|
/** Hours before arrival that cancelling is still free. Absent on a non-refundable rate. */
|
|
36
56
|
freeCancelHours?: number;
|
|
57
|
+
/** A rate only: the room type it is a rate of - see `Room.roomType`. */
|
|
58
|
+
roomType?: {
|
|
59
|
+
id: string;
|
|
60
|
+
name: string;
|
|
61
|
+
names?: Names;
|
|
62
|
+
};
|
|
63
|
+
/** A rate only: its own id and name, apart from the type's - see `Room.rate`. */
|
|
64
|
+
rate?: {
|
|
65
|
+
id: string;
|
|
66
|
+
name: string;
|
|
67
|
+
names?: Names;
|
|
68
|
+
};
|
|
69
|
+
/** A rate only, and only where it says how many breakfasts: the meal, as a code. */
|
|
70
|
+
meal?: MealPlan;
|
|
71
|
+
/** A rate only: the meal's label, then the offers it says it is (early booking …). */
|
|
72
|
+
labels?: RateLabel[];
|
|
73
|
+
/**
|
|
74
|
+
* Said by the file: 'at-hotel'. There is no 'prepaid' - this backend takes no
|
|
75
|
+
* money, and `parseHotelData` refuses a rate that says it does.
|
|
76
|
+
*/
|
|
77
|
+
payment?: 'at-hotel';
|
|
37
78
|
};
|
|
38
79
|
export type RawExtra = {
|
|
39
80
|
id: string;
|
|
@@ -54,6 +95,11 @@ export type RawHotel = {
|
|
|
54
95
|
/** ISO 3166-1 alpha-2 - see `Hotel.countryCode`. */
|
|
55
96
|
countryCode?: string;
|
|
56
97
|
area?: string;
|
|
98
|
+
/** Where it is, in decimal degrees - see `Hotel.geo`. */
|
|
99
|
+
geo?: {
|
|
100
|
+
lat: number;
|
|
101
|
+
lng: number;
|
|
102
|
+
};
|
|
57
103
|
/** Its names in the card's languages, as its group publishes them - see `Names`. */
|
|
58
104
|
names?: HotelNames;
|
|
59
105
|
summary: string;
|
|
@@ -149,6 +195,11 @@ export type DemoHotelsOptions = {
|
|
|
149
195
|
* two demonstration suppliers tells their codes apart by it.
|
|
150
196
|
*/
|
|
151
197
|
codePrefix?: string;
|
|
198
|
+
/**
|
|
199
|
+
* What to switch off (or on) in `FULL_CAPABILITIES` - the config's
|
|
200
|
+
* `data.capabilities`. Absent, the demonstration offers everything.
|
|
201
|
+
*/
|
|
202
|
+
capabilities?: DemoCapabilities;
|
|
152
203
|
};
|
|
153
204
|
/**
|
|
154
205
|
* A booking as this file keeps it: the booking, and the conversations that may
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"demo-hotels.d.ts","sourceRoot":"","sources":["../src/demo-hotels.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"demo-hotels.d.ts","sourceRoot":"","sources":["../src/demo-hotels.ts"],"names":[],"mappings":"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"}
|
package/dist/demo-hotels.js
CHANGED
|
@@ -4,6 +4,7 @@ import { addDays, arrivalInstant, atHotelIso, atHotelTime, eachNight, isWeekendN
|
|
|
4
4
|
import { FACILITIES, hasFacility } from './facilities.js';
|
|
5
5
|
import { addMoney, formatMoney, minorFactor, money, multiplyMoney, taxOn, toMinor, wholeUnits, } from './money.js';
|
|
6
6
|
import { roomNamesOf } from './names.js';
|
|
7
|
+
import { bedKindsOf } from './room-filters.js';
|
|
7
8
|
import { MAX_EXTRA_QUANTITY, MAX_ROOMS_PER_LINE } from './trip.js';
|
|
8
9
|
export const demoError = (code, message) => Object.assign(new Error(message), { code });
|
|
9
10
|
const POLICY_TITLES = {
|
|
@@ -115,6 +116,12 @@ const publicBooking = (b) => {
|
|
|
115
116
|
* it would otherwise ride along on another hotel's booking.
|
|
116
117
|
*/
|
|
117
118
|
/** A room too small for the party, as its code - see `partyNeedsOf`. */
|
|
119
|
+
/** What a trip line says of a room's breakfast - a rate that says how many says it. */
|
|
120
|
+
const breakfastWords = (r) => r.meal?.plan === 'breakfast' && r.meal.count
|
|
121
|
+
? `Breakfast for ${r.meal.count}`
|
|
122
|
+
: r.breakfast
|
|
123
|
+
? 'Breakfast included'
|
|
124
|
+
: undefined;
|
|
118
125
|
const partyCode = (guests, sleeps) => {
|
|
119
126
|
const { code, args } = partyNeedsOf(guests, sleeps);
|
|
120
127
|
return { code, args };
|
|
@@ -481,9 +488,14 @@ export const createDemoHotels = (opts) => {
|
|
|
481
488
|
sleeps: r.sleeps,
|
|
482
489
|
sizeSqm: r.sizeSqm,
|
|
483
490
|
beds: r.beds,
|
|
491
|
+
...(r.bedTypes ? { bedTypes: r.bedTypes } : {}),
|
|
484
492
|
view: r.view,
|
|
493
|
+
...(r.window ? { window: r.window } : {}),
|
|
485
494
|
breakfast: r.breakfast,
|
|
495
|
+
...(r.meal ? { meal: r.meal } : {}),
|
|
486
496
|
imageUrl: r.imageUrl,
|
|
497
|
+
...(r.images ? { images: r.images } : {}),
|
|
498
|
+
...(r.floor ? { floor: r.floor } : {}),
|
|
487
499
|
// The average, because a Friday costs more than a Tuesday. `stayTotal` is
|
|
488
500
|
// the number to compare hotels on, and it is exact.
|
|
489
501
|
nightly: wholeUnits(money(roomSubtotal.minor / perNight.length, h.currency)),
|
|
@@ -507,6 +519,13 @@ export const createDemoHotels = (opts) => {
|
|
|
507
519
|
? { freeCancellationLocal: atHotelTime(Date.parse(freeUntil), offsetOf(h)) }
|
|
508
520
|
: {}),
|
|
509
521
|
roomsLeft: Math.max(0, r.roomsLeft - heldOn(h.id, r.id, stay, ignore)),
|
|
522
|
+
// A rate of a room type, the way Huazhu's are: the type and the rate
|
|
523
|
+
// beside the whole offer's name, so the card and the tools fold a type's
|
|
524
|
+
// rates into one row.
|
|
525
|
+
...(r.roomType ? { roomType: r.roomType } : {}),
|
|
526
|
+
...(r.rate ? { rate: r.rate } : {}),
|
|
527
|
+
...(r.labels ? { labels: r.labels } : {}),
|
|
528
|
+
...(r.payment ? { payment: r.payment } : {}),
|
|
510
529
|
};
|
|
511
530
|
};
|
|
512
531
|
const toHotel = (h) => {
|
|
@@ -521,6 +540,7 @@ export const createDemoHotels = (opts) => {
|
|
|
521
540
|
country: h.country,
|
|
522
541
|
...(h.countryCode ? { countryCode: h.countryCode } : {}),
|
|
523
542
|
area: h.area,
|
|
543
|
+
...(h.geo ? { geo: h.geo } : {}),
|
|
524
544
|
...(h.names ? { names: h.names } : {}),
|
|
525
545
|
summary: h.summary,
|
|
526
546
|
amenities: h.amenities,
|
|
@@ -643,9 +663,16 @@ export const createDemoHotels = (opts) => {
|
|
|
643
663
|
/** A room type by its id or the name the hotel prints. */
|
|
644
664
|
const roomOf = (h, ref) => {
|
|
645
665
|
const wanted = ref.trim().toLowerCase();
|
|
646
|
-
|
|
647
|
-
h.rooms.find((r) => r.name.toLowerCase() === wanted)
|
|
648
|
-
|
|
666
|
+
const exact = h.rooms.find((r) => r.id.toLowerCase() === wanted) ??
|
|
667
|
+
h.rooms.find((r) => r.name.toLowerCase() === wanted);
|
|
668
|
+
if (exact || wanted.length < 4)
|
|
669
|
+
return exact;
|
|
670
|
+
const part = h.rooms.filter((r) => r.name.toLowerCase().includes(wanted));
|
|
671
|
+
// ⚠️ A WORD THAT FITS SEVERAL RATES OF A TYPE PICKS NONE OF THEM. "Deluxe
|
|
672
|
+
// King" is the first of its rates by the file's order - which may be the
|
|
673
|
+
// one that cannot be cancelled. A room that is its own type keeps the old
|
|
674
|
+
// reading: the first.
|
|
675
|
+
return part.length > 1 && part.some((r) => r.rate) ? undefined : part[0];
|
|
649
676
|
};
|
|
650
677
|
const quoteTrip = async (trip, who, ignore) => {
|
|
651
678
|
sweep();
|
|
@@ -745,9 +772,13 @@ export const createDemoHotels = (opts) => {
|
|
|
745
772
|
roomRef: raw.id,
|
|
746
773
|
title: raw.name,
|
|
747
774
|
...(raw.names ? { titleNames: raw.names } : {}),
|
|
748
|
-
detail: [raw.beds, raw.view, raw.
|
|
749
|
-
|
|
750
|
-
|
|
775
|
+
detail: [raw.beds, raw.view, breakfastWords(raw)].filter(Boolean).join(' · '),
|
|
776
|
+
// A rate's rate name, window and meal as codes, for a card in another
|
|
777
|
+
// language - the parts of `detail`, as Huazhu's lines carry them.
|
|
778
|
+
...(raw.rate ? { rate: { name: raw.rate.name } } : {}),
|
|
779
|
+
...(raw.bedTypes ? { bedTypes: raw.bedTypes } : {}),
|
|
780
|
+
...(raw.window ? { window: raw.window } : {}),
|
|
781
|
+
...(raw.meal ? { meal: raw.meal } : {}),
|
|
751
782
|
stay: line.stay,
|
|
752
783
|
nights: room.nights,
|
|
753
784
|
quantity: wanted,
|
|
@@ -1194,6 +1225,7 @@ export const createDemoHotels = (opts) => {
|
|
|
1194
1225
|
brand: opts.data.brand,
|
|
1195
1226
|
capabilities: {
|
|
1196
1227
|
...FULL_CAPABILITIES,
|
|
1228
|
+
...opts.capabilities,
|
|
1197
1229
|
...(opts.data.membership ? { programme: opts.data.membership.programme } : {}),
|
|
1198
1230
|
codePrefix,
|
|
1199
1231
|
},
|
|
@@ -1258,8 +1290,8 @@ export const createDemoHotels = (opts) => {
|
|
|
1258
1290
|
// ⚠️ THE FILTERS, and what each would leave with the others applied - the
|
|
1259
1291
|
// count a chip shows before it is pressed (robin, 2026-09-28: 「筛选可以,
|
|
1260
1292
|
// 加上」). A brand is one at a time; facilities and room terms all apply.
|
|
1261
|
-
// A room term is about a ROOM: a hotel passes when it has one
|
|
1262
|
-
// dated search that is the room that gets priced.
|
|
1293
|
+
// A room term is about a ROOM: a hotel passes when it has one that meets
|
|
1294
|
+
// ALL of them, and on a dated search that is the room that gets priced.
|
|
1263
1295
|
const f = q.filters ?? {};
|
|
1264
1296
|
const stayAsked = q.stay;
|
|
1265
1297
|
const wantRooms = Math.max(1, Math.min(MAX_ROOMS_PER_LINE, Math.floor(q.rooms ?? 1)));
|
|
@@ -1267,10 +1299,12 @@ export const createDemoHotels = (opts) => {
|
|
|
1267
1299
|
// free cancellation is a window that has not closed yet, not a rate that
|
|
1268
1300
|
// once had one.
|
|
1269
1301
|
const roomOk = (h, r, over) => (!over.freeCancellation || (stayAsked ? !!freeUntilOf(h, r, stayAsked) : r.refundable)) &&
|
|
1270
|
-
(!over.breakfast || !!r.breakfast)
|
|
1302
|
+
(!over.breakfast || !!r.breakfast) &&
|
|
1303
|
+
(!over.bed || bedKindsOf(r)[over.bed]);
|
|
1271
1304
|
const passes = (h, over) => (!over.brand || h.brand.toLowerCase() === over.brand.toLowerCase()) &&
|
|
1272
1305
|
(over.facilities ?? []).every((k) => hasFacility(h.amenities, k)) &&
|
|
1273
|
-
(!(over.freeCancellation || over.breakfast
|
|
1306
|
+
(!(over.freeCancellation || over.breakfast || over.bed) ||
|
|
1307
|
+
h.rooms.some((r) => roomOk(h, r, over)));
|
|
1274
1308
|
const matches = placed.filter((h) => passes(h, f));
|
|
1275
1309
|
// ⚠️ NAMED NIGHTS ARE PRICED, EVERY HOTEL (robin, 2026-09-28, with Trip.com
|
|
1276
1310
|
// and the group's own site as the reference: a place, then the dates,
|
|
@@ -1287,7 +1321,7 @@ export const createDemoHotels = (opts) => {
|
|
|
1287
1321
|
const bestFor = (h, over) => {
|
|
1288
1322
|
if (!stayAsked)
|
|
1289
1323
|
return null;
|
|
1290
|
-
const k = `${h.id}|${!!over.freeCancellation}|${!!over.breakfast}`;
|
|
1324
|
+
const k = `${h.id}|${!!over.freeCancellation}|${!!over.breakfast}|${over.bed ?? ''}`;
|
|
1291
1325
|
const known = best.get(k);
|
|
1292
1326
|
if (known !== undefined)
|
|
1293
1327
|
return known;
|