@curless/hotel-storefront 0.1.0 → 0.2.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/__tests__/card-contract.test.js +13 -0
- package/dist/__tests__/card-contract.test.js.map +1 -1
- package/dist/__tests__/data.test.js +28 -3
- package/dist/__tests__/data.test.js.map +1 -1
- package/dist/__tests__/demo-hotels.test.js +25 -11
- package/dist/__tests__/demo-hotels.test.js.map +1 -1
- package/dist/__tests__/present.test.d.ts +2 -0
- package/dist/__tests__/present.test.d.ts.map +1 -0
- package/dist/__tests__/present.test.js +140 -0
- package/dist/__tests__/present.test.js.map +1 -0
- package/dist/__tests__/server.test.js +63 -4
- package/dist/__tests__/server.test.js.map +1 -1
- package/dist/__tests__/tools.test.js +129 -0
- package/dist/__tests__/tools.test.js.map +1 -1
- package/dist/backend.d.ts +30 -6
- package/dist/backend.d.ts.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/config.d.ts +11 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +7 -0
- package/dist/config.js.map +1 -1
- package/dist/data.d.ts.map +1 -1
- package/dist/data.js +88 -13
- package/dist/data.js.map +1 -1
- package/dist/demo-hotels.d.ts +24 -1
- package/dist/demo-hotels.d.ts.map +1 -1
- package/dist/demo-hotels.js +136 -13
- package/dist/demo-hotels.js.map +1 -1
- package/dist/fence.d.ts +9 -3
- package/dist/fence.d.ts.map +1 -1
- package/dist/fence.js +0 -0
- package/dist/fence.js.map +1 -1
- package/dist/index.js +0 -0
- package/dist/members.d.ts +14 -0
- package/dist/members.d.ts.map +1 -0
- package/dist/members.js +56 -0
- package/dist/members.js.map +1 -0
- package/dist/present.d.ts +129 -0
- package/dist/present.d.ts.map +1 -0
- package/dist/present.js +188 -0
- package/dist/present.js.map +1 -0
- package/dist/server.d.ts +27 -10
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +27 -7
- package/dist/server.js.map +1 -1
- package/dist/surface.d.ts.map +1 -1
- package/dist/surface.js +27 -2
- package/dist/surface.js.map +1 -1
- package/dist/tools.d.ts +174 -1
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +464 -50
- package/dist/tools.js.map +1 -1
- package/dist/types.d.ts +57 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/vocab.d.ts +1 -1
- package/dist/vocab.d.ts.map +1 -1
- package/dist/vocab.js +1 -1
- package/dist/vocab.js.map +1 -1
- package/package.json +11 -9
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"card.generated.js","sourceRoot":"","sources":["../src/card.generated.ts"],"names":[],"mappings":"AAAA,6FAA6F;AAC7F,wEAAwE;AACxE,gFAAgF;AAChF,4DAA4D;AAC5D,MAAM,CAAC,MAAM,eAAe,GAAW,MAAM,CAAC,IAAI,CAChD,
|
|
1
|
+
{"version":3,"file":"card.generated.js","sourceRoot":"","sources":["../src/card.generated.ts"],"names":[],"mappings":"AAAA,6FAA6F;AAC7F,wEAAwE;AACxE,gFAAgF;AAChF,4DAA4D;AAC5D,MAAM,CAAC,MAAM,eAAe,GAAW,MAAM,CAAC,IAAI,CAChD,suqUAAsuqU,EACtuqU,QAAQ,CACT,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;AAEnB,2FAA2F;AAC3F,MAAM,CAAC,MAAM,qBAAqB,GAChC,kEAAkE,CAAC"}
|
package/dist/config.d.ts
CHANGED
|
@@ -57,6 +57,13 @@ export type HotelConfig = {
|
|
|
57
57
|
file?: string;
|
|
58
58
|
/** api only: where it answers. The key comes from the environment. */
|
|
59
59
|
baseUrl?: string;
|
|
60
|
+
/**
|
|
61
|
+
* demo only: how many minutes a booking request holds its rooms.
|
|
62
|
+
*
|
|
63
|
+
* Short on purpose - the demonstration inventory is shared by everyone who
|
|
64
|
+
* opens the connector, and a day-long hold empties it in an afternoon.
|
|
65
|
+
*/
|
|
66
|
+
holdMinutes?: number;
|
|
60
67
|
};
|
|
61
68
|
/**
|
|
62
69
|
* Taking money. Absent - and it IS absent today, by decision - the connector
|
|
@@ -117,6 +124,10 @@ export declare const toolNames: (config: Pick<HotelConfig, "naming">) => {
|
|
|
117
124
|
readonly previewCancellation: `preview_${string}cancellation`;
|
|
118
125
|
readonly cancelBooking: `cancel_${string}booking`;
|
|
119
126
|
readonly policies: `get_${string}${string}_policies`;
|
|
127
|
+
readonly compare: `compare_${string}${string}`;
|
|
128
|
+
readonly itinerary: `show_${string}itinerary`;
|
|
129
|
+
readonly signIn: `sign_in_${string}member`;
|
|
130
|
+
readonly signOut: `sign_out_${string}member`;
|
|
120
131
|
};
|
|
121
132
|
export type ToolNames = ReturnType<typeof toolNames>;
|
|
122
133
|
//# sourceMappingURL=config.d.ts.map
|
package/dist/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,MAAM,MAAM,WAAW,GAAG;IACxB,sFAAsF;IACtF,EAAE,EAAE,MAAM,CAAC;IAEX,oFAAoF;IACpF,KAAK,EAAE,MAAM,CAAC;IAEd,qDAAqD;IACrD,MAAM,EAAE;QACN,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,MAAM,CAAC;QACd,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB;;;;;;WAMG;QACH,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,CAAC;IAEF;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE;QACP,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,sFAAsF;QACtF,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,UAAU,CAAC,EAAE,MAAM,CAAC;KACrB,CAAC;IAEF,iCAAiC;IACjC,IAAI,EAAE;QACJ;;;;;;WAMG;QACH,MAAM,EAAE,MAAM,GAAG,KAAK,CAAC;QACvB,gFAAgF;QAChF,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,sEAAsE;QACtE,OAAO,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,MAAM,MAAM,WAAW,GAAG;IACxB,sFAAsF;IACtF,EAAE,EAAE,MAAM,CAAC;IAEX,oFAAoF;IACpF,KAAK,EAAE,MAAM,CAAC;IAEd,qDAAqD;IACrD,MAAM,EAAE;QACN,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,MAAM,CAAC;QACd,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB;;;;;;WAMG;QACH,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,CAAC;IAEF;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE;QACP,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,sFAAsF;QACtF,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,UAAU,CAAC,EAAE,MAAM,CAAC;KACrB,CAAC;IAEF,iCAAiC;IACjC,IAAI,EAAE;QACJ;;;;;;WAMG;QACH,MAAM,EAAE,MAAM,GAAG,KAAK,CAAC;QACvB,gFAAgF;QAChF,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,sEAAsE;QACtE,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB;;;;;WAKG;QACH,WAAW,CAAC,EAAE,MAAM,CAAC;KACtB,CAAC;IAEF;;;;;OAKG;IACH,OAAO,CAAC,EAAE;QACR,IAAI,EAAE,MAAM,GAAG,QAAQ,CAAC;KACzB,CAAC;IAEF,2GAA2G;IAC3G,IAAI,EAAE;QACJ,MAAM,EAAE,MAAM,CAAC;QACf,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,CAAC;IAEF;;;;;;;;OAQG;IACH,KAAK,CAAC,EAAE;QACN,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,6EAA6E;QAC7E,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB,CAAC;IAEF,KAAK,EAAE;QACL,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,eAAe,CAAC,EAAE,MAAM,CAAC;QACzB,mEAAmE;QACnE,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB,CAAC;CACH,CAAC;AAQF;;;;;;;GAOG;AACH,eAAO,MAAM,SAAS,GAAI,QAAQ,IAAI,CAAC,WAAW,EAAE,QAAQ,CAAC;;;;;;;;;;;;;;;;CAwB5D,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC"}
|
package/dist/config.js
CHANGED
|
@@ -37,6 +37,13 @@ export const toolNames = (config) => {
|
|
|
37
37
|
previewCancellation: `preview_${prefix}cancellation`,
|
|
38
38
|
cancelBooking: `cancel_${prefix}booking`,
|
|
39
39
|
policies: `get_${prefix}${noun}_policies`,
|
|
40
|
+
// The two that carry the MODEL's judgment rather than a tool's answer -
|
|
41
|
+
// which hotels sit beside each other, what the plan's days are. Every fact
|
|
42
|
+
// on the card is still joined by the server; see present.ts.
|
|
43
|
+
compare: `compare_${prefix}${plural}`,
|
|
44
|
+
itinerary: `show_${prefix}itinerary`,
|
|
45
|
+
signIn: `sign_in_${prefix}member`,
|
|
46
|
+
signOut: `sign_out_${prefix}member`,
|
|
40
47
|
};
|
|
41
48
|
};
|
|
42
49
|
//# sourceMappingURL=config.js.map
|
package/dist/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AA0GH,MAAM,IAAI,GAAG,CAAC,CAAS,EAAU,EAAE,CACjC,CAAC;KACE,WAAW,EAAE;KACb,OAAO,CAAC,aAAa,EAAE,GAAG,CAAC;KAC3B,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;AAE3B;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,MAAmC,EAAE,EAAE;IAC/D,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;IAC7E,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,IAAI,IAAI,OAAO,CAAC,CAAC;IAClD,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,UAAU,IAAI,GAAG,IAAI,GAAG,CAAC,CAAC;IAC7D,OAAO;QACL,UAAU,EAAE,QAAQ,MAAM,GAAG,MAAM,EAAE;QACrC,QAAQ,EAAE,OAAO,MAAM,GAAG,IAAI,EAAE;QAChC,UAAU,EAAE,QAAQ,MAAM,QAAQ;QAClC,SAAS,EAAE,UAAU,MAAM,MAAM;QACjC,QAAQ,EAAE,QAAQ,MAAM,MAAM;QAC9B,SAAS,EAAE,SAAS,MAAM,MAAM;QAChC,cAAc,EAAE,WAAW,MAAM,SAAS;QAC1C,YAAY,EAAE,WAAW,MAAM,UAAU;QACzC,mBAAmB,EAAE,WAAW,MAAM,cAAc;QACpD,aAAa,EAAE,UAAU,MAAM,SAAS;QACxC,QAAQ,EAAE,OAAO,MAAM,GAAG,IAAI,WAAW;QACzC,wEAAwE;QACxE,2EAA2E;QAC3E,6DAA6D;QAC7D,OAAO,EAAE,WAAW,MAAM,GAAG,MAAM,EAAE;QACrC,SAAS,EAAE,QAAQ,MAAM,WAAW;QACpC,MAAM,EAAE,WAAW,MAAM,QAAQ;QACjC,OAAO,EAAE,YAAY,MAAM,QAAQ;KAC3B,CAAC;AACb,CAAC,CAAC"}
|
package/dist/data.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"data.d.ts","sourceRoot":"","sources":["../src/data.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAsB,YAAY,
|
|
1
|
+
{"version":3,"file":"data.d.ts","sourceRoot":"","sources":["../src/data.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAsB,YAAY,EAA0B,MAAM,kBAAkB,CAAC;AAkPjG,eAAO,MAAM,cAAc,GAAI,KAAK,OAAO,KAAG,YAc7C,CAAC;AAEF,6EAA6E;AAC7E,eAAO,MAAM,aAAa,GAAI,MAAM,MAAM,EAAE,OAAO,MAAM,KAAG,YAS3D,CAAC"}
|
package/dist/data.js
CHANGED
|
@@ -10,6 +10,25 @@ import { dirname, resolve } from 'node:path';
|
|
|
10
10
|
* it prints a plausible number rather than an error.
|
|
11
11
|
*/
|
|
12
12
|
const KINDS = ['transfer', 'dining', 'experience', 'family', 'wellness'];
|
|
13
|
+
/**
|
|
14
|
+
* The largest rate a currency can plausibly write for one night, used to catch
|
|
15
|
+
* a price typed in MINOR units by mistake - 380000 for 3800 - which would look
|
|
16
|
+
* like a real, if eye-watering, number on a card.
|
|
17
|
+
*
|
|
18
|
+
* It has to be per currency. 2,400,000 is an ordinary room in Jakarta and a
|
|
19
|
+
* typo in Hong Kong, and a single cap either lets the typo through everywhere
|
|
20
|
+
* or refuses every hotel in Indonesia, Mongolia and Sri Lanka.
|
|
21
|
+
*/
|
|
22
|
+
const BIG_MONEY = {
|
|
23
|
+
IDR: 50_000_000,
|
|
24
|
+
VND: 500_000_000,
|
|
25
|
+
KRW: 50_000_000,
|
|
26
|
+
MNT: 20_000_000,
|
|
27
|
+
MMK: 20_000_000,
|
|
28
|
+
LKR: 5_000_000,
|
|
29
|
+
JPY: 5_000_000,
|
|
30
|
+
};
|
|
31
|
+
const maxPrice = (currency) => BIG_MONEY[currency.toUpperCase()] ?? 200_000;
|
|
13
32
|
const TOPICS = [
|
|
14
33
|
'cancellation',
|
|
15
34
|
'check-in',
|
|
@@ -44,7 +63,7 @@ const time = (v, where, what) => {
|
|
|
44
63
|
throw new DataError(where, `${what} must look like 15:00`);
|
|
45
64
|
return s;
|
|
46
65
|
};
|
|
47
|
-
const parseRoom = (raw, where) => {
|
|
66
|
+
const parseRoom = (raw, where, currency) => {
|
|
48
67
|
const r = raw;
|
|
49
68
|
const id = str(r.id, where, 'room id');
|
|
50
69
|
const at = `${where} room ${id}`;
|
|
@@ -61,16 +80,16 @@ const parseRoom = (raw, where) => {
|
|
|
61
80
|
beds: r.beds === undefined ? undefined : str(r.beds, at, 'beds'),
|
|
62
81
|
view: r.view === undefined ? undefined : str(r.view, at, 'view'),
|
|
63
82
|
breakfast: r.breakfast === true,
|
|
64
|
-
|
|
65
|
-
//
|
|
66
|
-
//
|
|
67
|
-
nightly: num(r.nightly, at, 'nightly', 1,
|
|
83
|
+
imageUrl: r.imageUrl === undefined ? undefined : str(r.imageUrl, at, 'imageUrl'),
|
|
84
|
+
// A rate is in whole currency units, the way the hotel writes it - see
|
|
85
|
+
// BIG_MONEY for why the ceiling is the currency's and not one number.
|
|
86
|
+
nightly: num(r.nightly, at, 'nightly', 1, maxPrice(currency)),
|
|
68
87
|
roomsLeft: num(r.roomsLeft, at, 'roomsLeft', 0, 500),
|
|
69
88
|
refundable,
|
|
70
89
|
freeCancelHours: refundable ? num(r.freeCancelHours, at, 'freeCancelHours', 0, 720) : undefined,
|
|
71
90
|
};
|
|
72
91
|
};
|
|
73
|
-
const parseExtra = (raw, where) => {
|
|
92
|
+
const parseExtra = (raw, where, currency) => {
|
|
74
93
|
const e = raw;
|
|
75
94
|
const id = str(e.id, where, 'extra id');
|
|
76
95
|
const at = `${where} extra ${id}`;
|
|
@@ -88,7 +107,7 @@ const parseExtra = (raw, where) => {
|
|
|
88
107
|
name: str(e.name, at, 'name'),
|
|
89
108
|
kind,
|
|
90
109
|
description: str(e.description, at, 'description'),
|
|
91
|
-
price: num(e.price, at, 'price', 1,
|
|
110
|
+
price: num(e.price, at, 'price', 1, maxPrice(currency)),
|
|
92
111
|
per,
|
|
93
112
|
when,
|
|
94
113
|
};
|
|
@@ -98,7 +117,11 @@ const parseHotel = (raw, index) => {
|
|
|
98
117
|
const where = `hotel[${index}]`;
|
|
99
118
|
const id = str(h.id, where, 'id');
|
|
100
119
|
const at = `hotel ${id}`;
|
|
101
|
-
|
|
120
|
+
// Read before the rooms are: a room's ceiling is its hotel's currency.
|
|
121
|
+
const currency = str(h.currency, at, 'currency');
|
|
122
|
+
if (!/^[A-Z]{3}$/.test(currency))
|
|
123
|
+
throw new DataError(at, `currency ${currency} must be a 3-letter code like HKD`);
|
|
124
|
+
const rooms = Array.isArray(h.rooms) ? h.rooms.map((r) => parseRoom(r, at, currency)) : [];
|
|
102
125
|
if (rooms.length === 0)
|
|
103
126
|
throw new DataError(at, 'a hotel with no room types cannot be booked; leave it out instead');
|
|
104
127
|
const seen = new Set();
|
|
@@ -114,15 +137,13 @@ const parseHotel = (raw, index) => {
|
|
|
114
137
|
throw new DataError(at, `policy topic ${topic} is not one we show`);
|
|
115
138
|
policies[topic] = str(text, at, `policy ${topic}`);
|
|
116
139
|
}
|
|
117
|
-
const currency = str(h.currency, at, 'currency');
|
|
118
|
-
if (!/^[A-Z]{3}$/.test(currency))
|
|
119
|
-
throw new DataError(at, `currency ${currency} must be a 3-letter code like HKD`);
|
|
120
140
|
return {
|
|
121
141
|
id,
|
|
122
142
|
name: str(h.name, at, 'name'),
|
|
123
143
|
brand: str(h.brand, at, 'brand'),
|
|
124
144
|
city: str(h.city, at, 'city'),
|
|
125
145
|
cityId: str(h.cityId, at, 'cityId'),
|
|
146
|
+
country: h.country === undefined ? undefined : str(h.country, at, 'country'),
|
|
126
147
|
area: h.area === undefined ? undefined : str(h.area, at, 'area'),
|
|
127
148
|
summary: str(h.summary, at, 'summary'),
|
|
128
149
|
amenities: Array.isArray(h.amenities)
|
|
@@ -141,7 +162,7 @@ const parseHotel = (raw, index) => {
|
|
|
141
162
|
utcOffset: h.utcOffset === undefined ? undefined : str(h.utcOffset, at, 'utcOffset'),
|
|
142
163
|
rating: h.rating === undefined ? undefined : parseRating(h.rating, at),
|
|
143
164
|
rooms,
|
|
144
|
-
extras: Array.isArray(h.extras) ? h.extras.map((e) => parseExtra(e, at)) : [],
|
|
165
|
+
extras: Array.isArray(h.extras) ? h.extras.map((e) => parseExtra(e, at, currency)) : [],
|
|
145
166
|
policies,
|
|
146
167
|
};
|
|
147
168
|
};
|
|
@@ -155,6 +176,56 @@ const parseRating = (raw, where) => {
|
|
|
155
176
|
count: num(r.count, where, 'rating.count', 1, 10_000_000),
|
|
156
177
|
};
|
|
157
178
|
};
|
|
179
|
+
/**
|
|
180
|
+
* The loyalty programme, validated like everything else that can print a
|
|
181
|
+
* number: a tier's discount is a FRACTION, and a member without a tier that
|
|
182
|
+
* exists would silently price at the public rate while calling itself a member.
|
|
183
|
+
*/
|
|
184
|
+
const parseMembership = (raw) => {
|
|
185
|
+
const m = raw;
|
|
186
|
+
const where = 'membership';
|
|
187
|
+
const tiers = Array.isArray(m.tiers)
|
|
188
|
+
? m.tiers.map((t) => {
|
|
189
|
+
const x = t;
|
|
190
|
+
const id = str(x.id, where, 'tier id');
|
|
191
|
+
return {
|
|
192
|
+
id,
|
|
193
|
+
name: str(x.name, `${where} ${id}`, 'name'),
|
|
194
|
+
// 0.1, not 10 - the same mistake as taxRate, and it would take 1000%
|
|
195
|
+
// off a rate and still render.
|
|
196
|
+
discount: num(x.discount, `${where} ${id}`, 'discount', 0, 0.9),
|
|
197
|
+
pointsPerUnit: num(x.pointsPerUnit, `${where} ${id}`, 'pointsPerUnit', 0, 100),
|
|
198
|
+
};
|
|
199
|
+
})
|
|
200
|
+
: [];
|
|
201
|
+
if (tiers.length === 0)
|
|
202
|
+
throw new DataError(where, 'a programme with no tiers cannot price anything');
|
|
203
|
+
const ids = new Set(tiers.map((t) => t.id));
|
|
204
|
+
const members = Array.isArray(m.members)
|
|
205
|
+
? m.members.map((r) => {
|
|
206
|
+
const x = r;
|
|
207
|
+
const number = str(x.number, where, 'member number');
|
|
208
|
+
const at = `${where} ${number}`;
|
|
209
|
+
const tier = str(x.tier, at, 'tier');
|
|
210
|
+
if (!ids.has(tier))
|
|
211
|
+
throw new DataError(at, `tier ${tier} is not one this programme has`);
|
|
212
|
+
return {
|
|
213
|
+
number,
|
|
214
|
+
surname: str(x.surname, at, 'surname'),
|
|
215
|
+
given: str(x.given, at, 'given'),
|
|
216
|
+
tier,
|
|
217
|
+
points: num(x.points, at, 'points', 0, 100_000_000),
|
|
218
|
+
};
|
|
219
|
+
})
|
|
220
|
+
: [];
|
|
221
|
+
const seen = new Set();
|
|
222
|
+
for (const x of members) {
|
|
223
|
+
if (seen.has(x.number))
|
|
224
|
+
throw new DataError(where, `two members share the number ${x.number}`);
|
|
225
|
+
seen.add(x.number);
|
|
226
|
+
}
|
|
227
|
+
return { programme: str(m.programme, where, 'programme'), tiers, members };
|
|
228
|
+
};
|
|
158
229
|
export const parseHotelData = (raw) => {
|
|
159
230
|
const d = raw;
|
|
160
231
|
const hotels = Array.isArray(d.hotels) ? d.hotels.map(parseHotel) : [];
|
|
@@ -166,7 +237,11 @@ export const parseHotelData = (raw) => {
|
|
|
166
237
|
throw new DataError('hotels file', `two hotels share the id ${h.id}`);
|
|
167
238
|
ids.add(h.id);
|
|
168
239
|
}
|
|
169
|
-
return {
|
|
240
|
+
return {
|
|
241
|
+
brand: str(d.brand, 'hotels file', 'brand'),
|
|
242
|
+
hotels,
|
|
243
|
+
...(d.membership ? { membership: parseMembership(d.membership) } : {}),
|
|
244
|
+
};
|
|
170
245
|
};
|
|
171
246
|
/** Read a brand's hotels file. `from` is the config file it was named in. */
|
|
172
247
|
export const loadHotelData = (file, from) => {
|
package/dist/data.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"data.js","sourceRoot":"","sources":["../src/data.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"data.js","sourceRoot":"","sources":["../src/data.ts"],"names":[],"mappings":"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"}
|
package/dist/demo-hotels.d.ts
CHANGED
|
@@ -24,6 +24,7 @@ export type RawRoom = {
|
|
|
24
24
|
beds?: string;
|
|
25
25
|
view?: string;
|
|
26
26
|
breakfast: boolean;
|
|
27
|
+
imageUrl?: string;
|
|
27
28
|
/** Written the way a human writes it - 3800, not 380000. */
|
|
28
29
|
nightly: number;
|
|
29
30
|
roomsLeft: number;
|
|
@@ -46,6 +47,7 @@ export type RawHotel = {
|
|
|
46
47
|
brand: string;
|
|
47
48
|
city: string;
|
|
48
49
|
cityId: string;
|
|
50
|
+
country?: string;
|
|
49
51
|
area?: string;
|
|
50
52
|
summary: string;
|
|
51
53
|
amenities: string[];
|
|
@@ -70,16 +72,37 @@ export type RawHotel = {
|
|
|
70
72
|
extras?: RawExtra[];
|
|
71
73
|
policies?: Partial<Record<HotelPolicyTopic, string>>;
|
|
72
74
|
};
|
|
75
|
+
export type RawTier = {
|
|
76
|
+
id: string;
|
|
77
|
+
name: string;
|
|
78
|
+
discount: number;
|
|
79
|
+
pointsPerUnit: number;
|
|
80
|
+
};
|
|
81
|
+
export type RawMember = {
|
|
82
|
+
number: string;
|
|
83
|
+
surname: string;
|
|
84
|
+
given: string;
|
|
85
|
+
tier: string;
|
|
86
|
+
points: number;
|
|
87
|
+
};
|
|
88
|
+
export type RawMembership = {
|
|
89
|
+
programme: string;
|
|
90
|
+
tiers: RawTier[];
|
|
91
|
+
members: RawMember[];
|
|
92
|
+
};
|
|
73
93
|
export type RawHotelData = {
|
|
74
94
|
brand: string;
|
|
75
95
|
hotels: RawHotel[];
|
|
96
|
+
membership?: RawMembership;
|
|
76
97
|
};
|
|
77
98
|
export type DemoError = Error & {
|
|
78
|
-
code: 'not-found' | 'bad-stay' | 'unavailable' | 'mixed-currency' | 'empty-trip';
|
|
99
|
+
code: 'not-found' | 'bad-stay' | 'unavailable' | 'mixed-currency' | 'empty-trip' | 'no-guest';
|
|
79
100
|
};
|
|
80
101
|
export declare const demoError: (code: DemoError["code"], message: string) => DemoError;
|
|
81
102
|
export type DemoHotelsOptions = {
|
|
82
103
|
data: RawHotelData;
|
|
104
|
+
/** How long a booking request holds its rooms. Default two hours - see HOLD_MS_DEFAULT. */
|
|
105
|
+
holdMs?: number;
|
|
83
106
|
/** Injected so tests do not depend on the day they run. */
|
|
84
107
|
now?: () => number;
|
|
85
108
|
rng?: () => number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"demo-hotels.d.ts","sourceRoot":"","sources":["../src/demo-hotels.ts"],"names":[],"mappings":"AAAA,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
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { arrivalInstant, eachNight, isWeekendNight, nights as nightsBetween, stayProblem, todayAt, } from './dates.js';
|
|
2
|
-
import { addMoney, formatMoney, money, multiplyMoney, taxOn, toMinor } from './money.js';
|
|
2
|
+
import { addMoney, formatMoney, minorFactor, money, multiplyMoney, taxOn, toMinor, } from './money.js';
|
|
3
3
|
export const demoError = (code, message) => Object.assign(new Error(message), { code });
|
|
4
4
|
const POLICY_TITLES = {
|
|
5
5
|
cancellation: 'Cancellation',
|
|
@@ -10,7 +10,19 @@ const POLICY_TITLES = {
|
|
|
10
10
|
accessibility: 'Accessibility',
|
|
11
11
|
};
|
|
12
12
|
const DEFAULT_OFFSET = '+08:00';
|
|
13
|
-
|
|
13
|
+
/**
|
|
14
|
+
* How long a request holds the rooms.
|
|
15
|
+
*
|
|
16
|
+
* ⚠️ TWO HOURS, NOT THE TWENTY-FOUR A REAL DESK WOULD GIVE, and the reason is
|
|
17
|
+
* that this backend's inventory is a demonstration everyone shares. One
|
|
18
|
+
* afternoon of evals and demos booked the Island Shangri-La's Deluxe City View
|
|
19
|
+
* six times, each hold lasting a day, and the room simply vanished from the
|
|
20
|
+
* card - a colleague opening the demo was told it was sold out, by us, about a
|
|
21
|
+
* room that does not exist (2026-09-18). Inventory exhaustion is silent and
|
|
22
|
+
* hits everybody; a hold lapsing is loud, local, and printed on the booking.
|
|
23
|
+
* A real backend sets its own window.
|
|
24
|
+
*/
|
|
25
|
+
const HOLD_MS_DEFAULT = 2 * 3_600_000;
|
|
14
26
|
/** A hold never runs past arrival - a room held for a guest who never arrives is a room nobody got. */
|
|
15
27
|
const HOLD_STOPS_BEFORE_ARRIVAL_MS = 2 * 3_600_000;
|
|
16
28
|
const CODE_ALPHABET = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789';
|
|
@@ -20,6 +32,7 @@ export const createDemoHotels = (opts) => {
|
|
|
20
32
|
const hotels = opts.data.hotels;
|
|
21
33
|
const now = opts.now ?? (() => Date.now());
|
|
22
34
|
const rng = opts.rng ?? Math.random;
|
|
35
|
+
const holdMs = opts.holdMs ?? HOLD_MS_DEFAULT;
|
|
23
36
|
const bookings = new Map();
|
|
24
37
|
let seq = 0;
|
|
25
38
|
const byId = (id) => {
|
|
@@ -28,6 +41,43 @@ export const createDemoHotels = (opts) => {
|
|
|
28
41
|
hotels.find((h) => h.name.toLowerCase() === wanted) ??
|
|
29
42
|
hotels.find((h) => h.name.toLowerCase().includes(wanted) && wanted.length >= 6));
|
|
30
43
|
};
|
|
44
|
+
/**
|
|
45
|
+
* The member, by number and the surname on it.
|
|
46
|
+
*
|
|
47
|
+
* ⚠️ BOTH, AND CASE-FOLDED ONLY. A number alone would let anyone who can
|
|
48
|
+
* guess a format read somebody's tier and point balance; the surname is the
|
|
49
|
+
* second half of what a desk checks. It is not a password and this is not
|
|
50
|
+
* authentication - which is exactly why the tier only ever buys a DISCOUNT
|
|
51
|
+
* here and never reaches a booking somebody else made.
|
|
52
|
+
*/
|
|
53
|
+
const memberFor = (number, surname) => {
|
|
54
|
+
const m = opts.data.membership;
|
|
55
|
+
if (!m)
|
|
56
|
+
return null;
|
|
57
|
+
const wanted = number.replace(/\s+/g, '').toUpperCase();
|
|
58
|
+
const hit = m.members.find((x) => x.number.replace(/\s+/g, '').toUpperCase() === wanted &&
|
|
59
|
+
x.surname.trim().toLowerCase() === surname.trim().toLowerCase());
|
|
60
|
+
if (!hit)
|
|
61
|
+
return null;
|
|
62
|
+
const tier = m.tiers.find((t) => t.id === hit.tier);
|
|
63
|
+
if (!tier)
|
|
64
|
+
return null;
|
|
65
|
+
return {
|
|
66
|
+
number: hit.number,
|
|
67
|
+
name: `${hit.given} ${hit.surname}`,
|
|
68
|
+
surname: hit.surname,
|
|
69
|
+
programme: m.programme,
|
|
70
|
+
tier: {
|
|
71
|
+
id: tier.id,
|
|
72
|
+
name: tier.name,
|
|
73
|
+
discount: tier.discount,
|
|
74
|
+
pointsPerUnit: tier.pointsPerUnit,
|
|
75
|
+
},
|
|
76
|
+
points: hit.points,
|
|
77
|
+
};
|
|
78
|
+
};
|
|
79
|
+
/** The tier's discount, or none. One place, so a rate cannot be discounted twice. */
|
|
80
|
+
const discountOf = (who) => who?.member?.tier.discount ?? 0;
|
|
31
81
|
const offsetOf = (h) => h.utcOffset ?? DEFAULT_OFFSET;
|
|
32
82
|
const today = (hotelId) => {
|
|
33
83
|
const h = hotelId ? byId(hotelId) : undefined;
|
|
@@ -54,10 +104,18 @@ export const createDemoHotels = (opts) => {
|
|
|
54
104
|
b.status = 'expired';
|
|
55
105
|
}
|
|
56
106
|
};
|
|
57
|
-
const priceRoom = (h, r, stay) => {
|
|
107
|
+
const priceRoom = (h, r, stay, who) => {
|
|
58
108
|
const perNight = eachNight(stay.checkIn, stay.checkOut).map((day) => money(toMinor(r.nightly, h.currency) * (isWeekendNight(day) ? 1 + h.weekendUplift : 1), h.currency));
|
|
59
|
-
const
|
|
109
|
+
const publicSubtotal = perNight.reduce((a, b) => addMoney(a, b), money(0, h.currency));
|
|
110
|
+
// ⚠️ THE DISCOUNT COMES OFF THE ROOM, NOT OFF THE TAX. Tax is charged on
|
|
111
|
+
// what is actually paid, so it is computed after - the other order quietly
|
|
112
|
+
// hands the member a second, invisible discount on the government's share.
|
|
113
|
+
const discount = discountOf(who);
|
|
114
|
+
const roomSubtotal = discount
|
|
115
|
+
? money(publicSubtotal.minor * (1 - discount), h.currency)
|
|
116
|
+
: publicSubtotal;
|
|
60
117
|
const taxes = taxOn(roomSubtotal, h.taxRate);
|
|
118
|
+
const publicTaxes = taxOn(publicSubtotal, h.taxRate);
|
|
61
119
|
const freeUntil = r.refundable && r.freeCancelHours !== undefined
|
|
62
120
|
? new Date(arrivalInstant(stay.checkIn, h.checkInFrom ?? '15:00', offsetOf(h)) -
|
|
63
121
|
r.freeCancelHours * 3_600_000).toISOString()
|
|
@@ -70,11 +128,23 @@ export const createDemoHotels = (opts) => {
|
|
|
70
128
|
beds: r.beds,
|
|
71
129
|
view: r.view,
|
|
72
130
|
breakfast: r.breakfast,
|
|
131
|
+
imageUrl: r.imageUrl,
|
|
73
132
|
// The average, because a Friday costs more than a Tuesday. `stayTotal` is
|
|
74
133
|
// the number to compare hotels on, and it is exact.
|
|
75
134
|
nightly: money(roomSubtotal.minor / perNight.length, h.currency),
|
|
76
135
|
stayTotal: addMoney(roomSubtotal, taxes),
|
|
77
136
|
taxesAndFees: taxes,
|
|
137
|
+
...(discount
|
|
138
|
+
? {
|
|
139
|
+
publicNightly: money(publicSubtotal.minor / perNight.length, h.currency),
|
|
140
|
+
publicStayTotal: addMoney(publicSubtotal, publicTaxes),
|
|
141
|
+
memberRate: {
|
|
142
|
+
programme: who?.member?.programme ?? '',
|
|
143
|
+
tier: who?.member?.tier.name ?? '',
|
|
144
|
+
discount,
|
|
145
|
+
},
|
|
146
|
+
}
|
|
147
|
+
: {}),
|
|
78
148
|
nights: perNight.length,
|
|
79
149
|
refundable: r.refundable,
|
|
80
150
|
freeCancellationUntil: freeUntil,
|
|
@@ -89,6 +159,7 @@ export const createDemoHotels = (opts) => {
|
|
|
89
159
|
brand: h.brand,
|
|
90
160
|
city: h.city,
|
|
91
161
|
cityId: h.cityId,
|
|
162
|
+
country: h.country,
|
|
92
163
|
area: h.area,
|
|
93
164
|
summary: h.summary,
|
|
94
165
|
amenities: h.amenities,
|
|
@@ -126,13 +197,16 @@ export const createDemoHotels = (opts) => {
|
|
|
126
197
|
};
|
|
127
198
|
const mine = (b, who) => b.sessionId === who.sessionId ||
|
|
128
199
|
(!!who.email && b.guest.email.toLowerCase() === who.email.toLowerCase());
|
|
129
|
-
const quoteTrip = async (trip) => {
|
|
200
|
+
const quoteTrip = async (trip, who) => {
|
|
130
201
|
sweep();
|
|
131
202
|
const lines = [];
|
|
132
203
|
const dropped = [];
|
|
133
204
|
let currency;
|
|
134
205
|
let subtotal = 0;
|
|
135
206
|
let taxes = 0;
|
|
207
|
+
// What the tier took off, added up as we go - so the card can say what
|
|
208
|
+
// membership was worth on THIS trip rather than quoting a percentage.
|
|
209
|
+
let saved = 0;
|
|
136
210
|
for (const line of trip.lines) {
|
|
137
211
|
const h = byId(line.hotelId);
|
|
138
212
|
if (!h) {
|
|
@@ -162,7 +236,7 @@ export const createDemoHotels = (opts) => {
|
|
|
162
236
|
});
|
|
163
237
|
continue;
|
|
164
238
|
}
|
|
165
|
-
const room = priceRoom(h, raw, line.stay);
|
|
239
|
+
const room = priceRoom(h, raw, line.stay, who);
|
|
166
240
|
const wanted = Math.max(1, Math.floor(line.rooms ?? 1));
|
|
167
241
|
if (room.roomsLeft < wanted) {
|
|
168
242
|
dropped.push({
|
|
@@ -183,6 +257,11 @@ export const createDemoHotels = (opts) => {
|
|
|
183
257
|
const preTax = money((room.stayTotal.minor - room.taxesAndFees.minor) * wanted, h.currency);
|
|
184
258
|
subtotal += preTax.minor;
|
|
185
259
|
taxes += room.taxesAndFees.minor * wanted;
|
|
260
|
+
// What the tier took off this line, all in - so the trip can say what
|
|
261
|
+
// membership was worth in money rather than quoting a percentage.
|
|
262
|
+
if (room.publicStayTotal) {
|
|
263
|
+
saved += (room.publicStayTotal.minor - room.stayTotal.minor) * wanted;
|
|
264
|
+
}
|
|
186
265
|
lines.push({
|
|
187
266
|
kind: 'room',
|
|
188
267
|
hotelId: h.id,
|
|
@@ -247,6 +326,7 @@ export const createDemoHotels = (opts) => {
|
|
|
247
326
|
guests: Math.max(...roomStays.map((s) => s.guests)),
|
|
248
327
|
}
|
|
249
328
|
: undefined;
|
|
329
|
+
const member = who?.member;
|
|
250
330
|
return {
|
|
251
331
|
lines,
|
|
252
332
|
subtotal: money(subtotal, cur),
|
|
@@ -254,6 +334,17 @@ export const createDemoHotels = (opts) => {
|
|
|
254
334
|
total: money(subtotal + taxes, cur),
|
|
255
335
|
stay,
|
|
256
336
|
dropped: dropped.length ? dropped : undefined,
|
|
337
|
+
...(member && lines.length
|
|
338
|
+
? {
|
|
339
|
+
member: {
|
|
340
|
+
programme: member.programme,
|
|
341
|
+
tier: member.tier.name,
|
|
342
|
+
saved: money(saved, cur),
|
|
343
|
+
// Points on what is actually paid, in whole currency units.
|
|
344
|
+
points: Math.round(((subtotal + taxes) / minorFactor(cur)) * member.tier.pointsPerUnit),
|
|
345
|
+
},
|
|
346
|
+
}
|
|
347
|
+
: {}),
|
|
257
348
|
};
|
|
258
349
|
};
|
|
259
350
|
/** What cancelling this booking costs, line by line, in the hotel's own terms. */
|
|
@@ -299,18 +390,37 @@ export const createDemoHotels = (opts) => {
|
|
|
299
390
|
.filter((t) => t.length > 1);
|
|
300
391
|
const city = q.city?.trim().toLowerCase();
|
|
301
392
|
const matches = hotels.filter((h) => {
|
|
302
|
-
|
|
393
|
+
// A country counts as a city here: "Japan" and "Malaysia" are how a
|
|
394
|
+
// guest narrows a group that runs hotels in twenty-odd countries, and
|
|
395
|
+
// refusing it would answer "no hotels" for a place with five.
|
|
396
|
+
if (city &&
|
|
397
|
+
h.cityId.toLowerCase() !== city &&
|
|
398
|
+
h.city.toLowerCase() !== city &&
|
|
399
|
+
(h.country ?? '').toLowerCase() !== city)
|
|
303
400
|
return false;
|
|
304
401
|
if (terms.length === 0)
|
|
305
402
|
return true;
|
|
306
|
-
const hay = [
|
|
403
|
+
const hay = [
|
|
404
|
+
h.name,
|
|
405
|
+
h.brand,
|
|
406
|
+
h.city,
|
|
407
|
+
h.cityId,
|
|
408
|
+
h.country,
|
|
409
|
+
h.area,
|
|
410
|
+
h.summary,
|
|
411
|
+
...h.amenities,
|
|
412
|
+
]
|
|
307
413
|
.join(' ')
|
|
308
414
|
.toLowerCase();
|
|
309
415
|
return terms.every((t) => hay.includes(t));
|
|
310
416
|
});
|
|
311
|
-
|
|
417
|
+
// `total` is what MATCHED, not what is being returned. They were the same
|
|
418
|
+
// number while the file held nine hotels; with ninety-six they are not,
|
|
419
|
+
// and a card that says "20 hotels" about a group with forty in China is
|
|
420
|
+
// wrong in the one direction a booking connector cannot be wrong in.
|
|
421
|
+
return { hotels: matches.slice(0, q.limit ?? 20).map(toHotel), total: matches.length };
|
|
312
422
|
},
|
|
313
|
-
async getHotel(id, stay) {
|
|
423
|
+
async getHotel(id, stay, who) {
|
|
314
424
|
const h = byId(id);
|
|
315
425
|
if (!h)
|
|
316
426
|
return null;
|
|
@@ -329,7 +439,7 @@ export const createDemoHotels = (opts) => {
|
|
|
329
439
|
noRooms: `No room here sleeps ${stay.guests}. The largest is ${Math.max(...h.rooms.map((r) => r.sleeps))}; two rooms would work.`,
|
|
330
440
|
};
|
|
331
441
|
}
|
|
332
|
-
const rooms = fits.map((r) => priceRoom(h, r, stay)).filter((r) => r.roomsLeft > 0);
|
|
442
|
+
const rooms = fits.map((r) => priceRoom(h, r, stay, who)).filter((r) => r.roomsLeft > 0);
|
|
333
443
|
return {
|
|
334
444
|
...detail,
|
|
335
445
|
stay,
|
|
@@ -345,8 +455,21 @@ export const createDemoHotels = (opts) => {
|
|
|
345
455
|
return (h.extras ?? []).map((e) => toExtra(h, e));
|
|
346
456
|
},
|
|
347
457
|
quote: quoteTrip,
|
|
458
|
+
async signInMember(number, surname) {
|
|
459
|
+
return memberFor(number, surname);
|
|
460
|
+
},
|
|
348
461
|
async requestBooking(trip, guest, who) {
|
|
349
|
-
const quote = await quoteTrip(trip);
|
|
462
|
+
const quote = await quoteTrip(trip, who);
|
|
463
|
+
// ⚠️ A ROOM HELD FOR NOBODY. The tool's schema says name and email are
|
|
464
|
+
// required, but a schema is what the model is TOLD, not what it must do -
|
|
465
|
+
// `request_booking {}` came back with a confirmation code and
|
|
466
|
+
// `guest: { name: '', email: '' }` on the live connector (2026-09-18).
|
|
467
|
+
// The hotel would be holding a room for nobody, and the guest could never
|
|
468
|
+
// find the booking by email. Refused at the seam the real API sits behind
|
|
469
|
+
// as well as in the tool.
|
|
470
|
+
if (!guest.name.trim() || !guest.email.includes('@')) {
|
|
471
|
+
throw demoError('no-guest', "A booking needs the guest's name and an email to send it to.");
|
|
472
|
+
}
|
|
350
473
|
const [firstRoom] = quote.lines.filter((l) => l.kind === 'room');
|
|
351
474
|
const first = firstRoom ? byId(firstRoom.hotelId) : undefined;
|
|
352
475
|
const stay = quote.stay;
|
|
@@ -356,7 +479,7 @@ export const createDemoHotels = (opts) => {
|
|
|
356
479
|
}
|
|
357
480
|
const arrival = arrivalInstant(stay.checkIn, first.checkInFrom ?? '15:00', offsetOf(first));
|
|
358
481
|
const t = now();
|
|
359
|
-
const holdUntil = new Date(Math.max(t + 3_600_000, Math.min(t +
|
|
482
|
+
const holdUntil = new Date(Math.max(t + 3_600_000, Math.min(t + holdMs, arrival - HOLD_STOPS_BEFORE_ARRIVAL_MS))).toISOString();
|
|
360
483
|
seq += 1;
|
|
361
484
|
const booking = {
|
|
362
485
|
id: `bk_${Date.now().toString(36)}_${seq}`,
|