@curless/hotel-storefront 0.47.9 → 0.48.1

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":"AACA,OAAO,EACL,KAAK,mBAAmB,EAExB,KAAK,YAAY,EASlB,MAAM,eAAe,CAAC;AAavB,OAAO,EAEL,KAAK,aAAa,EAKnB,MAAM,cAAc,CAAC;AAEtB,OAAO,KAAK,EAAE,QAAQ,EAAiC,MAAM,yBAAyB,CAAC;AACvF,OAAO,EAGL,KAAK,eAAe,EAIrB,MAAM,yBAAyB,CAAC;AAGjC,OAAO,KAAK,EAuBV,KAAK,EAML,UAAU,EAIX,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,KAAK,YAAY,EAIjB,KAAK,UAAU,EAEf,KAAK,aAAa,EAGnB,MAAM,eAAe,CAAC;AAcvB,OAAO,EAAE,KAAK,OAAO,EAAoD,MAAM,cAAc,CAAC;AAC9F,OAAO,EAAE,KAAK,YAAY,EAAmD,MAAM,aAAa,CAAC;AAiBjG,OAAO,EAQL,KAAK,WAAW,EAGhB,KAAK,WAAW,EAMjB,MAAM,WAAW,CAAC;AAsBnB,MAAM,MAAM,mBAAmB,GAAG;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,kFAAkF;IAClF,MAAM,CAAC,EAAE,OAAO,CAAC,aAAa,CAAC,CAAC;IAChC,MAAM,EAAE,YAAY,CAAC;IACrB,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,YAAY,CAAC;IACpB,UAAU,EAAE,UAAU,CAAC;IACvB,gGAAgG;IAChG,WAAW,EAAE,OAAO,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC;IACnD,QAAQ,EAAE,IAAI,GAAG,IAAI,CAAC;IACtB,+EAA+E;IAC/E,EAAE,EAAE,MAAM,CAAC;IACX,iFAAiF;IACjF,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,wEAAwE;IACxE,8FAA8F;IAC9F,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;;OAGG;IACH,OAAO,CAAC,EAAE;QACR,QAAQ,EAAE,QAAQ,CAAC;QACnB;;;;WAIG;QACH,QAAQ,CAAC,EAAE,eAAe,CAAC;QAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;KACtB,CAAC;IACF;;;;;OAKG;IACH,SAAS,CAAC,EAAE;QAAE,UAAU,CAAC,EAAE,MAAM,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC3D,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,iIAAiI;IACjI,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,uCAAuC;IACvC,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACvC,CAAC;AAEF,eAAO,MAAM,mBAAmB,EAAE,mBAqBjC,CAAC;AAEF,0GAA0G;AAC1G,eAAO,MAAM,OAAO,GAAI,KAAK,MAAM,GAAG,SAAS,KAAG,OAGjD,CAAC;AAaF,eAAO,MAAM,aAAa,GAAI,OAAO,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,KAAG,MAKhE,CAAC;AAKF;;;;;GAKG;AACH,eAAO,MAAM,SAAS,KAAK,CAAC;AAC5B,sFAAsF;AACtF,eAAO,MAAM,WAAW,KAAK,CAAC;AAkS9B;;;GAGG;AACH,eAAO,MAAM,UAAU,GAAI,GAAG,WAAW,EAAE,OAAO,MAAM,KAAG,OAiB1D,CAAC;AA2BF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,aAAa,GAAI,QAAQ,WAAW,EAAE,EAAE,KAAK,MAAM,KAAG,WAAW,GAAG,SAGhF,CAAC;AAEF,2FAA2F;AAC3F,eAAO,MAAM,WAAW,GAAI,QAAQ,WAAW,EAAE,EAAE,KAAK,MAAM,KAAG,WAAW,EAK3E,CAAC;AAUF,oFAAoF;AACpF,eAAO,MAAM,WAAW,GAAI,QAAQ,WAAW,EAAE,EAAE,KAAK,MAAM,KAAG,WAAW,EAO3E,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,UAAU,GAAI,SAAS,MAAM,EAAE,IAAI,MAAM,EAAE,WAAU,IAAI,GAAG,IAAW,KAAG,MAKtF,CAAC;AAiCF;;;GAGG;AACH,eAAO,MAAM,UAAU,GACrB,OAAO,UAAU,GAAG,SAAS,EAC7B,GAAG,KAAK;AACR,kGAAkG;AAClG,eAAY,KACX,MAAM,GAAG,SAwBX,CAAC;AAgCF,eAAO,MAAM,kBAAkB,GAC7B,MAAM,mBAAmB,KACxB,YAAY,GAAG;IAChB,YAAY,EAAE,mBAAmB,CAAC;IAClC,YAAY,IAAI,MAAM,EAAE,CAAC;IACzB,MAAM,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,OAAO,CAAC,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS,CAAC;IACnE,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAutI9B,CAAC"}
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,34 @@ 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
+ // and requests are the first asking again; the guest's part is a digest,
4080
+ // so the key holds nobody's name.
4081
+ // ⚠️ NOT THE NOTE (酒店对接, 2026-10-27): it is free text, and a retry
4082
+ // says it again in other words - 「需要安静的房间」 then 「希望房间安静」 -
4083
+ // which booked a second room, or opened a second payment. The requests
4084
+ // are a closed list and the arrival a time; the name and the phone are
4085
+ // the same person written two ways (full-width, spacing) made one.
4086
+ const same = (t) => t.normalize('NFKC').trim().replace(/\s+/g, ' ').toLowerCase();
4087
+ const whoFor = createHash('sha256')
4088
+ .update(JSON.stringify([
4089
+ same(guest.name),
4090
+ (guest.phone ?? '').normalize('NFKC').replace(/\D/g, ''),
4091
+ [...(requests?.kinds ?? [])].sort(),
4092
+ requests?.arrivingAt ?? '',
4093
+ ]))
4094
+ .digest('hex')
4095
+ .slice(0, 16);
4096
+ const tripKey = `${lines
4074
4097
  .map((l) => l.kind === 'room'
4075
- ? `${l.hotelId}|${l.roomId}|${l.stay.checkIn}|${l.stay.checkOut}|${l.stay.guests}|${l.rooms ?? 1}`
4098
+ ? `${l.hotelId}|${l.roomId}|${l.stay.checkIn}|${l.stay.checkOut}|${l.stay.guests}|${l.rooms ?? 1}|${l.hourly?.start ?? ''}`
4076
4099
  : '')
4077
4100
  .sort()
4078
- .join(';');
4101
+ .join(';')}#${whoFor}`;
4079
4102
  // The caller and, from the card, the conversation whose trip it is.
4080
4103
  const keys = [...new Set([who.sessionId, who.tripOwner ?? who.sessionId])].map((k) => `s:${k}`);
4081
4104
  return single([...keys, `e:${email}`], () => book(lines, trip.shown, tripKey, email, guest, contact, who, requests));