@curless/hotel-storefront 0.43.1 → 0.45.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.
Files changed (48) hide show
  1. package/dist/card.generated.d.ts +1 -1
  2. package/dist/card.generated.js +2 -2
  3. package/dist/card.generated.js.map +1 -1
  4. package/dist/config.d.ts +18 -0
  5. package/dist/config.d.ts.map +1 -1
  6. package/dist/config.js.map +1 -1
  7. package/dist/demo-hotels.js +1 -1
  8. package/dist/demo-hotels.js.map +1 -1
  9. package/dist/huazhu/backend.d.ts +10 -0
  10. package/dist/huazhu/backend.d.ts.map +1 -1
  11. package/dist/huazhu/backend.js +575 -75
  12. package/dist/huazhu/backend.js.map +1 -1
  13. package/dist/huazhu/bookings.d.ts +15 -0
  14. package/dist/huazhu/bookings.d.ts.map +1 -1
  15. package/dist/huazhu/bookings.js.map +1 -1
  16. package/dist/huazhu/client.d.ts +84 -0
  17. package/dist/huazhu/client.d.ts.map +1 -1
  18. package/dist/huazhu/client.js +147 -1
  19. package/dist/huazhu/client.js.map +1 -1
  20. package/dist/huazhu/make.d.ts.map +1 -1
  21. package/dist/huazhu/make.js +48 -1
  22. package/dist/huazhu/make.js.map +1 -1
  23. package/dist/limits.d.ts +63 -6
  24. package/dist/limits.d.ts.map +1 -1
  25. package/dist/limits.js +15 -4
  26. package/dist/limits.js.map +1 -1
  27. package/dist/payments/checkout.d.ts +11 -0
  28. package/dist/payments/checkout.d.ts.map +1 -1
  29. package/dist/payments/checkout.js.map +1 -1
  30. package/dist/payments/fake-stripe.d.ts +4 -0
  31. package/dist/payments/fake-stripe.d.ts.map +1 -1
  32. package/dist/payments/fake-stripe.js +21 -1
  33. package/dist/payments/fake-stripe.js.map +1 -1
  34. package/dist/payments/fake-unionpay.d.ts +2 -0
  35. package/dist/payments/fake-unionpay.d.ts.map +1 -1
  36. package/dist/payments/fake-unionpay.js +7 -1
  37. package/dist/payments/fake-unionpay.js.map +1 -1
  38. package/dist/payments/stripe-checkout.d.ts +1 -0
  39. package/dist/payments/stripe-checkout.d.ts.map +1 -1
  40. package/dist/payments/stripe-checkout.js +4 -2
  41. package/dist/payments/stripe-checkout.js.map +1 -1
  42. package/dist/server.d.ts +2 -2
  43. package/dist/server.js +2 -2
  44. package/dist/tools.d.ts +30 -1
  45. package/dist/tools.d.ts.map +1 -1
  46. package/dist/tools.js +72 -11
  47. package/dist/tools.js.map +1 -1
  48. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,mBAAmB,EASzB,MAAM,cAAc,CAAC;AACtB,OAAO,KAAK,EAAa,YAAY,EAAgB,MAAM,cAAc,CAAC;AAC1E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAK7C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAmBhD,OAAO,EAGL,KAAK,SAAS,EAIf,MAAM,WAAW,CAAC;AACnB,OAAO,KAAK,EAEV,cAAc,EAkBd,eAAe,EAKhB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAa,KAAK,UAAU,EAAwC,MAAM,YAAY,CAAC;AAuF9F,+GAA+G;AAC/G,eAAO,MAAM,WAAW,IAAI,CAAC;AAE7B;;;;GAIG;AACH,eAAO,MAAM,UAAU,GACrB,GAAG,SAAS,EACZ,GAAG,UAAU,EACb,OAAM,mBAAuC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IACC,CAAC;AAEjD,mEAAmE;AACnE;;;;;GAKG;AACH,eAAO,MAAM,eAAe,GAAI,CAAC,SAAS;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,EAC7E,GAAG,CAAC,EACJ,OAAO;IAAE,KAAK,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,UAAU,CAAA;CAAE,KAC7C,CAWF,CAAC;AAEF,eAAO,MAAM,aAAa,GAAI,GAAG,SAAS,EAAE,MAAM,mBAAmB,KAAG,GAAG,CAAC,MAAM,CAO9E,CAAC;AAo3BL;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB,GAC3B,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,MAAM,mBAAmB,EACzB,OAAO,IAAI,EACX,OAAM;IACJ,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,SAAS,CAAC,EAAE,OAAO,CAAC;CAChB,KACL,MAAM,CAAC,MAAM,EAAE,OAAO,CAgDxB,CAAC;AAqBF,eAAO,MAAM,kBAAkB,GAAI,GAAG,SAAS,KAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAyPtE,CAAC;AAEH,qDAAqD;AACrD,MAAM,MAAM,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAE/B;;;;GAIG;AACH,MAAM,MAAM,UAAU,GAAG;IAAE,GAAG,EAAE,MAAM,EAAE,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAE9F,MAAM,MAAM,QAAQ,GAAG;IACrB,OAAO,EAAE,YAAY,CAAC;IACtB;;;OAGG;IACH,OAAO,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC9B,KAAK,EAAE,SAAS,CAAC;IACjB,OAAO,EAAE,WAAW,CAAC;IACrB,KAAK,EAAE,SAAS,CAAC;IACjB,KAAK,EAAE,UAAU,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,oFAAoF;IACpF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,4FAA4F;IAC5F,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,oFAAoF;IACpF,OAAO,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC5D,iGAAiG;IACjG,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,qFAAqF;IACrF,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;OAGG;IACH,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,0FAA0F;IAC1F,OAAO,EAAE,MAAM,CAAC;IAChB;;;;;;;OAOG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,4FAA4F;IAC5F,EAAE,CAAC,EAAE,MAAM,CAAC;CACb,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACvB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,4FAA4F;IAC5F,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC;AA0MF;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,MAAM,SAAS,GAAG;IACtB,MAAM,EAAE,cAAc,CAAC,QAAQ,CAAC,CAAC;IACjC,kEAAkE;IAClE,MAAM,EAAE,MAAM,CAAC;IACf,4CAA4C;IAC5C,MAAM,EAAE,KAAK,GAAG,MAAM,GAAG,MAAM,CAAC;IAChC,oEAAoE;IACpE,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,CAAC;AAwlCF;;;;GAIG;AACH,eAAO,MAAM,eAAe,IAAI,CAAC;AAyKjC;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,QAAQ,GAAI,MAAM,QAAQ,KAAG,UAOzC,CAAC;AAEF,eAAO,MAAM,UAAU,GACrB,MAAM,QAAQ,EACd,MAAM,MAAM,EACZ,SAAS,OAAO,EAChB,KAAK,WAAW,KACf,OAAO,CAAC,UAAU,CAkBpB,CAAC"}
package/dist/tools.js CHANGED
@@ -908,6 +908,20 @@ const CANCEL_SCHEMA = {
908
908
  rule: STR,
909
909
  freeUntil: STR,
910
910
  cancellable: BOOL,
911
+ /**
912
+ * What was paid here in advance and what cancelling now gives back of it -
913
+ * only on a booking paid by card or UnionPay that can be cancelled (`PaidFacts`).
914
+ */
915
+ payment: {
916
+ type: 'object',
917
+ properties: {
918
+ method: { enum: ['card', 'unionpay'] },
919
+ amount: STR,
920
+ refund: { enum: ['all', 'part', 'none'] },
921
+ refundAmount: STR,
922
+ },
923
+ required: ['method', 'amount', 'refund'],
924
+ },
911
925
  /** The terms as facts, for the card to say in the guest's language (`CancellationPreview`). */
912
926
  feeIsCap: BOOL,
913
927
  reason: STR,
@@ -1013,7 +1027,7 @@ const HOTEL_PAGE = {
1013
1027
  noRoomsArgs: OBJ,
1014
1028
  extras: ARR,
1015
1029
  policies: ARR,
1016
- /** Arrivals a few days either side, each re-quoted for the same nights. */
1030
+ /** Arrivals a few days either side, each re-quoted for the same nights - what the date picker prices its days from. */
1017
1031
  dates: ARR,
1018
1032
  };
1019
1033
  export const buildOutputSchemas = (n) => ({
@@ -1416,8 +1430,34 @@ const holderOf = (deps, supplier) => {
1416
1430
  const bookingForCard = (b) => ({
1417
1431
  ...b,
1418
1432
  quote: quoteForCard(b.quote),
1419
- ...(b.payment ? { payment: { ...b.payment, amount: formatMoney(b.payment.amount) } } : {}),
1433
+ ...(b.payment
1434
+ ? {
1435
+ payment: {
1436
+ ...b.payment,
1437
+ amount: formatMoney(b.payment.amount),
1438
+ // What goes back, where it is not the whole amount, as text too - the
1439
+ // cancelled page says "¥1,601 of the ¥3,202 paid" from it (robin,
1440
+ // 2026-10-22). `refundAmount` stays the money it was.
1441
+ ...(b.payment.refundAmount
1442
+ ? { refundAmountText: formatMoney(b.payment.refundAmount) }
1443
+ : {}),
1444
+ },
1445
+ }
1446
+ : {}),
1420
1447
  });
1448
+ const paidFactsOf = (booked, preview) => {
1449
+ const p = booked?.payment;
1450
+ if (!p || p.status !== 'paid' || !preview.cancellable)
1451
+ return undefined;
1452
+ const back = preview.refund.minor;
1453
+ const refund = back <= 0 ? 'none' : back >= p.amount.minor ? 'all' : 'part';
1454
+ return {
1455
+ method: p.method,
1456
+ amount: formatMoney(p.amount),
1457
+ refund,
1458
+ ...(refund === 'part' ? { refundAmount: formatMoney(preview.refund) } : {}),
1459
+ };
1460
+ };
1421
1461
  /**
1422
1462
  * A booking paid in advance, as the guest is told it - where its money stands.
1423
1463
  * Undefined for any other booking.
@@ -1483,7 +1523,12 @@ const paidLine = (deps, b, fee) => {
1483
1523
  // and not read yet (audit, P0-D) - what is said is what is to be done.
1484
1524
  return unionpay
1485
1525
  ? `**Not booked yet.** ${amount} is to be confirmed on UnionPay's page by ${p.payByLocal ?? p.payBy} hotel time${p.url ? `: ${p.url}` : ''}. Once UnionPay issues its one-time card, the stay is booked with ${h.name}, who charges that card; not confirmed by then, nothing is booked or charged.`
1486
- : `**Not booked yet.** ${amount} is to be paid on the card page by ${p.payByLocal ?? p.payBy} hotel time: ${p.url}. Once it is paid the stay is booked with ${h.name}, and ${deps.tools.listBookings} shows it; unpaid by then, the page closes and nothing is booked or charged.`;
1526
+ : p.url
1527
+ ? `**Not booked yet.** ${amount} is to be paid on the card page by ${p.payByLocal ?? p.payBy} hotel time: ${p.url}. Once it is paid the stay is booked with ${h.name}, and ${deps.tools.listBookings} shows it; unpaid by then, the page closes and nothing is booked or charged.`
1528
+ : // ⚠️ NO LINK IS NOT "undefined" (2026-10-21 re-audit, T2-6): a card
1529
+ // page that was closed and could not be opened again leaves a stay
1530
+ // waiting with nowhere to pay it. Say so, and that it lapses.
1531
+ `**Not booked yet.** ${amount} is to be paid on the card page by ${p.payByLocal ?? p.payBy} hotel time, but the page's link is not available just now - ask again later (${deps.tools.listBookings} shows where it stands). Nothing is booked or charged until it is paid; if no page comes by then, the stay lapses and can be asked for again.`;
1487
1532
  case 'issued':
1488
1533
  return `Confirmed on UnionPay: ${h.name} is being asked to book it now, and ${deps.tools.listBookings} shows when it has. If ${h.name} does not take it, the one-time card is not charged.`;
1489
1534
  case 'paid':
@@ -1975,14 +2020,15 @@ const bookingLine = (b) => {
1975
2020
  * The window of nearby arrival dates, plus the one sentence worth saying about
1976
2021
  * it in prose.
1977
2022
  *
1978
- * ⚠️ THE SENTENCE IS SAID ONCE, OR NOT AT ALL. The strip belongs to the card,
1979
- * where a guest can read seven prices at a glance; repeating them in text costs
1980
- * the model's context and tells it nothing it can act on. What prose is good
2023
+ * ⚠️ THE SENTENCE IS SAID ONCE, OR NOT AT ALL. The window's prices belong to the
2024
+ * card - the date picker puts each under its day (the hotel page's own strip of
2025
+ * them was taken away, robin 2026-10-22); repeating them in text costs the
2026
+ * model's context and tells it nothing it can act on. What prose is good
1981
2027
  * for is the single fact a picture hides: that a date the guest did not ask
1982
2028
  * about is cheaper, or - when the asked-for nights are gone - that a date
1983
2029
  * nearby is free at all.
1984
2030
  *
1985
- * ⚠️ AND IT NAMES THE DATE. "The one before" or "the third chip" is meaningless
2031
+ * ⚠️ AND IT NAMES THE DATE. "The one before" or "the third day" is meaningless
1986
2032
  * the moment the card reflows or the guest is reading on a phone; a date is the
1987
2033
  * same fact wherever it is read.
1988
2034
  */
@@ -1991,8 +2037,8 @@ const dateFlex = (dates, stay) => {
1991
2037
  const others = dates.filter((d) => !d.chosen && d.from);
1992
2038
  // One date is not a window. A window with no prices on it IS still an answer,
1993
2039
  // and the one that matters most: a guest told "no type here has three left"
1994
- // wants to know whether another night would do, and seven chips reading Full
1995
- // say no in a way a missing strip does not.
2040
+ // wants to know whether another night would do, and seven days reading Full
2041
+ // say no in a way a missing window does not.
1996
2042
  if (dates.length < 2)
1997
2043
  return { structured: {}, text: '' };
1998
2044
  const nightCount = nightsBetween(stay.checkIn, stay.checkOut);
@@ -2004,7 +2050,7 @@ const dateFlex = (dates, stay) => {
2004
2050
  .slice()
2005
2051
  .sort((a, b) => (a.from?.minor ?? 0) - (b.from?.minor ?? 0) || shift(a) - shift(b))[0];
2006
2052
  // ⚠️ THE MARK IS EARNED AGAINST THE GUEST'S OWN DATES, not against the rest
2007
- // of the strip. Whatever is lowest is always lowest; badging it would put a
2053
+ // of the window. Whatever is lowest is always lowest; badging it would put a
2008
2054
  // bargain flag on the dates the guest already chose, or on a date four days
2009
2055
  // away that saves nothing.
2010
2056
  //
@@ -3940,8 +3986,18 @@ const answerTool = async (deps, name, rawArgs, ctx) => {
3940
3986
  // each call opens UnionPay's card page or its confirmation through the
3941
3987
  // gateway - fifty of them used up its key's limit for every guest. A
3942
3988
  // guest's own card takes a press, a "continue" and a reopen at most.
3989
+ //
3990
+ // ⚠️ THE STAY'S COUNT IS KEPT PER CODE AND EMAIL, NEVER PER CODE (2026-10-21
3991
+ // re-audit, T2-11). The count is made BEFORE the backend checks the email,
3992
+ // so with the code alone as its key anyone who knew only a code - one is
3993
+ // printed on a confirmation, or read over a shoulder - could ask five times
3994
+ // with any email and use up the stay's quarter of an hour, and the guest's
3995
+ // own press was refused as "too many". Keyed with the email asked, a wrong
3996
+ // email counts against itself: the guest's pair is the guest's alone. The
3997
+ // conversation and the address are counted as before. (The backend counts
3998
+ // a stay's asks too, but only once the pair has matched.)
3943
3999
  const tries = [
3944
- `unionpay|booking|${code.trim().toLowerCase()}`,
4000
+ `unionpay|booking|${code.trim().toLowerCase()}|${email.trim().toLowerCase()}`,
3945
4001
  `unionpay|session|${who.sessionId}`,
3946
4002
  ...(ctx.ip ? [`ip|unionpay|${ctx.ip}`] : []),
3947
4003
  ];
@@ -4201,6 +4257,8 @@ const answerTool = async (deps, name, rawArgs, ctx) => {
4201
4257
  ...(booked?.quote.lines.some((l) => l.kind === 'extra') ? { addOnsFree: true } : {}),
4202
4258
  }
4203
4259
  : {};
4260
+ // Money already paid here, and what comes back: see `paidFactsOf`.
4261
+ const paid = paidFactsOf(booked, preview);
4204
4262
  const asPreview = {
4205
4263
  structured: {
4206
4264
  view: 'cancellation-preview',
@@ -4236,6 +4294,9 @@ const answerTool = async (deps, name, rawArgs, ctx) => {
4236
4294
  rule: preview.rule,
4237
4295
  freeUntil: preview.freeUntil,
4238
4296
  cancellable: preview.cancellable,
4297
+ // What was paid and what comes back - the half of `rule` the card does
4298
+ // not show once it says the terms from the facts below.
4299
+ ...(paid ? { payment: paid } : {}),
4239
4300
  // The terms as facts, so the card can say them in the guest's language
4240
4301
  // instead of showing `rule` as the backend wrote it. Money becomes a
4241
4302
  // string here like `fee`; `free` is a step whose fee is nothing, so the