@curless/hotel-storefront 0.47.9 → 0.48.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.
@@ -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,87jjCAA87jjC,EAC97jjC,QAAQ,CACT,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;AAEnB,6GAA6G;AAC7G,MAAM,CAAC,MAAM,qBAAqB,GAChC,kEAAkE,CAAC;AAErE,0GAA0G;AAC1G,MAAM,CAAC,MAAM,qBAAqB,GAChC,iLAAiL,CAAC"}
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,sl1jCAAsl1jC,EACtl1jC,QAAQ,CACT,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;AAEnB,6GAA6G;AAC7G,MAAM,CAAC,MAAM,qBAAqB,GAChC,kEAAkE,CAAC;AAErE,0GAA0G;AAC1G,MAAM,CAAC,MAAM,qBAAqB,GAChC,iLAAiL,CAAC"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * The guest's own details - name, email, phone and the note they wrote - kept
3
+ * out of what the model reads (2026-10-27 audit, F3).
4
+ *
5
+ * ⚠️ A BOOKING MADE ON THE CARD WAS TYPED ON THE CARD. The model never saw the
6
+ * name, email or phone the guest put on the form, and was then handed all of
7
+ * them in `structuredContent` with every booking answer and every list of
8
+ * bookings. The card needs them - the email finds a booking again by its code
9
+ * (`emailByCode`), the confirmation names the guest - so they travel BESIDE the
10
+ * answer, in `_meta`, which the card reads and the model does not (robin's
11
+ * desktop, 2026-10-21: the model sees no `_meta`). The card puts each back on
12
+ * its booking by id before drawing it.
13
+ *
14
+ * Every object of the answer that carries a `guest` is a booking; its `guest`
15
+ * and its `requests.note` leave, filed under its `id` (or, failing that, its
16
+ * confirmation code).
17
+ */
18
+ export declare const GUESTS_META_KEY = "curless/guests";
19
+ export type KeptGuest = {
20
+ guest: unknown;
21
+ note?: string;
22
+ };
23
+ export declare const guestsApart: (structured: Record<string, unknown>) => {
24
+ shown: Record<string, unknown>;
25
+ guests: Record<string, KeptGuest>;
26
+ };
27
+ //# sourceMappingURL=guests.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"guests.d.ts","sourceRoot":"","sources":["../src/guests.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,eAAe,mBAAmB,CAAC;AAEhD,MAAM,MAAM,SAAS,GAAG;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAE1D,eAAO,MAAM,WAAW,GACtB,YAAY,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAClC;IAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAA;CA2BrE,CAAC"}
package/dist/guests.js ADDED
@@ -0,0 +1,51 @@
1
+ /**
2
+ * The guest's own details - name, email, phone and the note they wrote - kept
3
+ * out of what the model reads (2026-10-27 audit, F3).
4
+ *
5
+ * ⚠️ A BOOKING MADE ON THE CARD WAS TYPED ON THE CARD. The model never saw the
6
+ * name, email or phone the guest put on the form, and was then handed all of
7
+ * them in `structuredContent` with every booking answer and every list of
8
+ * bookings. The card needs them - the email finds a booking again by its code
9
+ * (`emailByCode`), the confirmation names the guest - so they travel BESIDE the
10
+ * answer, in `_meta`, which the card reads and the model does not (robin's
11
+ * desktop, 2026-10-21: the model sees no `_meta`). The card puts each back on
12
+ * its booking by id before drawing it.
13
+ *
14
+ * Every object of the answer that carries a `guest` is a booking; its `guest`
15
+ * and its `requests.note` leave, filed under its `id` (or, failing that, its
16
+ * confirmation code).
17
+ */
18
+ export const GUESTS_META_KEY = 'curless/guests';
19
+ export const guestsApart = (structured) => {
20
+ const guests = {};
21
+ const walk = (v) => {
22
+ if (Array.isArray(v))
23
+ return v.map(walk);
24
+ if (!v || typeof v !== 'object')
25
+ return v;
26
+ const o = v;
27
+ const out = {};
28
+ for (const [k, x] of Object.entries(o))
29
+ if (k !== 'guest')
30
+ out[k] = walk(x);
31
+ if (!('guest' in o))
32
+ return out;
33
+ const ref = typeof o.id === 'string' && o.id
34
+ ? o.id
35
+ : typeof o.confirmationCode === 'string'
36
+ ? o.confirmationCode
37
+ : '';
38
+ const asked = out.requests;
39
+ let note;
40
+ if (asked && typeof asked === 'object' && typeof asked.note === 'string') {
41
+ const { note: kept, ...rest } = asked;
42
+ note = kept;
43
+ out.requests = rest;
44
+ }
45
+ if (ref && o.guest && typeof o.guest === 'object')
46
+ guests[ref] = { guest: o.guest, ...(note !== undefined ? { note } : {}) };
47
+ return out;
48
+ };
49
+ return { shown: walk(structured), guests };
50
+ };
51
+ //# sourceMappingURL=guests.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"guests.js","sourceRoot":"","sources":["../src/guests.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,gBAAgB,CAAC;AAIhD,MAAM,CAAC,MAAM,WAAW,GAAG,CACzB,UAAmC,EACoC,EAAE;IACzE,MAAM,MAAM,GAA8B,EAAE,CAAC;IAC7C,MAAM,IAAI,GAAG,CAAC,CAAU,EAAW,EAAE;QACnC,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;YAAE,OAAO,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACzC,IAAI,CAAC,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ;YAAE,OAAO,CAAC,CAAC;QAC1C,MAAM,CAAC,GAAG,CAA4B,CAAC;QACvC,MAAM,GAAG,GAA4B,EAAE,CAAC;QACxC,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;YAAE,IAAI,CAAC,KAAK,OAAO;gBAAE,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QAC5E,IAAI,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC;YAAE,OAAO,GAAG,CAAC;QAChC,MAAM,GAAG,GACP,OAAO,CAAC,CAAC,EAAE,KAAK,QAAQ,IAAI,CAAC,CAAC,EAAE;YAC9B,CAAC,CAAC,CAAC,CAAC,EAAE;YACN,CAAC,CAAC,OAAO,CAAC,CAAC,gBAAgB,KAAK,QAAQ;gBACtC,CAAC,CAAC,CAAC,CAAC,gBAAgB;gBACpB,CAAC,CAAC,EAAE,CAAC;QACX,MAAM,KAAK,GAAG,GAAG,CAAC,QAA+C,CAAC;QAClE,IAAI,IAAwB,CAAC;QAC7B,IAAI,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YACzE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,IAAI,EAAE,GAAG,KAAK,CAAC;YACtC,IAAI,GAAG,IAAc,CAAC;YACtB,GAAG,CAAC,QAAQ,GAAG,IAAI,CAAC;QACtB,CAAC;QACD,IAAI,GAAG,IAAI,CAAC,CAAC,KAAK,IAAI,OAAO,CAAC,CAAC,KAAK,KAAK,QAAQ;YAC/C,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,GAAG,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;QAC5E,OAAO,GAAG,CAAC;IACb,CAAC,CAAC;IACF,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,UAAU,CAA4B,EAAE,MAAM,EAAE,CAAC;AACxE,CAAC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"backend.d.ts","sourceRoot":"","sources":["../../src/huazhu/backend.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"backend.d.ts","sourceRoot":"","sources":["../../src/huazhu/backend.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { randomBytes, randomUUID } from 'node:crypto';
1
+ import { createHash, randomBytes, randomUUID } from 'node:crypto';
2
2
  import { DEFAULT_PAGE, MAX_PAGE, REQUEST_KINDS, REQUEST_NOTE_MAX, guestTooLong, partyNeedsOf, } from '../backend.js';
3
3
  import { addDays, arrivalInstant, atHotelTime, nights as nightsBetween, stayProblem, stayProblemOf, todayAt, } from '../dates.js';
4
4
  import { byWords, splitByStay } from '../demo-hotels.js';
@@ -62,7 +62,7 @@ export const newPublicCode = (taken) => {
62
62
  return code;
63
63
  }
64
64
  };
65
- /** The same trip asked for again this soon, by the same conversation and email, is answered with the first. */
65
+ /** The same booking asked for again this soon - same conversation, email, stays and guest (`tripKey`) - is answered with the first. */
66
66
  const SAME_TRIP_MS = 10 * 60_000;
67
67
  /**
68
68
  * The most hotels priced for one search. A city can hold hundreds of Huazhu
@@ -1788,7 +1788,8 @@ export const createHuazhuHotels = (opts) => {
1788
1788
  earlier.length + (before?.failed?.length ?? 0) === lines.length &&
1789
1789
  earlier.every((b) => b.tripRef === ref) &&
1790
1790
  (before?.done ?? true)) {
1791
- return { booked: earlier.map(publicBooking), failed: before?.failed ?? [] };
1791
+ // ⚠️ SAID AS THE SAME BOOKING, never as a new one (F2): `repeat`.
1792
+ return { booked: earlier.map(publicBooking), failed: before?.failed ?? [], repeat: true };
1792
1793
  }
1793
1794
  // ⚠️ THE LIMITS - see `BookingLimits`. Each is said as what it is, and
1794
1795
  // none suggests a way round itself (another email, another chat).
@@ -4070,12 +4071,29 @@ export const createHuazhuHotels = (opts) => {
4070
4071
  const { groups } = splitByStay(trip.lines);
4071
4072
  const lines = groups.flat();
4072
4073
  const email = guest.email.trim().toLowerCase();
4073
- const tripKey = lines
4074
+ // ⚠️ THE WHOLE BOOKING, NOT ONLY ITS ROOMS (2026-10-27 audit, F2): a
4075
+ // guest booking the same room again for a friend - often with their own
4076
+ // email, for the confirmation - was answered with their own first
4077
+ // booking, and the friend had no room (robin, 2026-10-23: booking the
4078
+ // same room again must work). Only the same rooms, hours, guest, phone,
4079
+ // note and requests are the first asking again; the guest's part is a
4080
+ // digest, so the key holds nobody's name.
4081
+ const whoFor = createHash('sha256')
4082
+ .update(JSON.stringify([
4083
+ guest.name.trim().replace(/\s+/g, ' ').toLowerCase(),
4084
+ (guest.phone ?? '').replace(/\D/g, ''),
4085
+ (guest.notes ?? '').trim(),
4086
+ [...(requests?.kinds ?? [])].sort(),
4087
+ requests?.arrivingAt ?? '',
4088
+ ]))
4089
+ .digest('hex')
4090
+ .slice(0, 16);
4091
+ const tripKey = `${lines
4074
4092
  .map((l) => l.kind === 'room'
4075
- ? `${l.hotelId}|${l.roomId}|${l.stay.checkIn}|${l.stay.checkOut}|${l.stay.guests}|${l.rooms ?? 1}`
4093
+ ? `${l.hotelId}|${l.roomId}|${l.stay.checkIn}|${l.stay.checkOut}|${l.stay.guests}|${l.rooms ?? 1}|${l.hourly?.start ?? ''}`
4076
4094
  : '')
4077
4095
  .sort()
4078
- .join(';');
4096
+ .join(';')}#${whoFor}`;
4079
4097
  // The caller and, from the card, the conversation whose trip it is.
4080
4098
  const keys = [...new Set([who.sessionId, who.tripOwner ?? who.sessionId])].map((k) => `s:${k}`);
4081
4099
  return single([...keys, `e:${email}`], () => book(lines, trip.shown, tripKey, email, guest, contact, who, requests));