@curless/hotel-storefront 0.34.0 → 0.35.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.
Files changed (55) hide show
  1. package/dist/aggregate/make.d.ts.map +1 -1
  2. package/dist/aggregate/make.js +7 -1
  3. package/dist/aggregate/make.js.map +1 -1
  4. package/dist/backend.d.ts +2 -2
  5. package/dist/backend.d.ts.map +1 -1
  6. package/dist/card.generated.d.ts +1 -1
  7. package/dist/card.generated.js +2 -2
  8. package/dist/card.generated.js.map +1 -1
  9. package/dist/config.d.ts +24 -4
  10. package/dist/config.d.ts.map +1 -1
  11. package/dist/config.js.map +1 -1
  12. package/dist/huazhu/backend.d.ts +2 -2
  13. package/dist/huazhu/backend.d.ts.map +1 -1
  14. package/dist/huazhu/backend.js +119 -10
  15. package/dist/huazhu/backend.js.map +1 -1
  16. package/dist/huazhu/bookings.d.ts +2 -0
  17. package/dist/huazhu/bookings.d.ts.map +1 -1
  18. package/dist/huazhu/bookings.js.map +1 -1
  19. package/dist/huazhu/catalog.d.ts +7 -1
  20. package/dist/huazhu/catalog.d.ts.map +1 -1
  21. package/dist/huazhu/catalog.js +8 -4
  22. package/dist/huazhu/catalog.js.map +1 -1
  23. package/dist/huazhu/make.d.ts.map +1 -1
  24. package/dist/huazhu/make.js +44 -13
  25. package/dist/huazhu/make.js.map +1 -1
  26. package/dist/legal.d.ts.map +1 -1
  27. package/dist/legal.js +131 -83
  28. package/dist/legal.js.map +1 -1
  29. package/dist/payments/checkout.d.ts +49 -1
  30. package/dist/payments/checkout.d.ts.map +1 -1
  31. package/dist/payments/checkout.js +9 -0
  32. package/dist/payments/checkout.js.map +1 -1
  33. package/dist/payments/fake-stripe.d.ts +37 -0
  34. package/dist/payments/fake-stripe.d.ts.map +1 -0
  35. package/dist/payments/fake-stripe.js +123 -0
  36. package/dist/payments/fake-stripe.js.map +1 -0
  37. package/dist/payments/stripe-checkout.d.ts +44 -0
  38. package/dist/payments/stripe-checkout.d.ts.map +1 -0
  39. package/dist/payments/stripe-checkout.js +166 -0
  40. package/dist/payments/stripe-checkout.js.map +1 -0
  41. package/dist/server.d.ts +2 -2
  42. package/dist/server.js +2 -2
  43. package/dist/surface.d.ts.map +1 -1
  44. package/dist/surface.js +34 -20
  45. package/dist/surface.js.map +1 -1
  46. package/dist/tools.d.ts.map +1 -1
  47. package/dist/tools.js +45 -23
  48. package/dist/tools.js.map +1 -1
  49. package/dist/types.d.ts +17 -0
  50. package/dist/types.d.ts.map +1 -1
  51. package/dist/vocab.d.ts +20 -0
  52. package/dist/vocab.d.ts.map +1 -1
  53. package/dist/vocab.js +25 -2
  54. package/dist/vocab.js.map +1 -1
  55. package/package.json +1 -1
package/dist/tools.js CHANGED
@@ -8,7 +8,7 @@ import { withTraditional } from './names.js';
8
8
  import { MAX_COMPARED, MAX_CONS, MAX_DAYS, MAX_ITEMS_PER_DAY, MAX_MOMENTS, MAX_PROS, buildItinerary, candidatesFor, compareHotels, namesMoreThanOne, } from './present.js';
9
9
  import { regionOf } from './regions.js';
10
10
  import { MAX_EXTRA_QUANTITY, MAX_ROOMS_PER_LINE, TRIP_TTL_MS, clampCount, shownOf, tripKey, } from './trip.js';
11
- import { DEMO_NOTE, branded, catalogue, upFirst } from './vocab.js';
11
+ import { DEMO_NOTE, branded, catalogue, noteFor, upFirst } from './vocab.js';
12
12
  // The eleven things a guest can do: find hotels, look at one on dates, see what
13
13
  // else it offers, build a trip, ask for the rooms, and deal with a booking they
14
14
  // already have.
@@ -208,11 +208,15 @@ const forCapabilities = (tools, n, v, caps) => {
208
208
  // ⚠️ A SANDBOX BOOKS NOTHING REAL, and its description says so
209
209
  // itself: the instructions carried "test environment" while this
210
210
  // line still promised "the hotel's own reservation" (2026-09-30).
211
- description: v.sandbox
212
- ? `Makes a booking in a simulated copy of ${v.brand}'s booking system, for testing: each stay gets its own confirmation code there, and none of it is a ${v.brand} reservation or holds a real room. ${caps.prepaid
211
+ description: v.test
212
+ ? `Makes a test booking in ${v.brand}'s own test system, for testing: each stay gets its own confirmation code there, and none of it holds a room for anyone or is a stay anyone goes to. ${caps.prepaid
213
213
  ? 'A rate marked paid in advance is booked on its own, and not at once: the answer gives a card page in test mode and the time it is paid by, and the stay is booked once it is paid - no card is collected here. Every other rate is paid by nobody.'
214
214
  : 'No payment is taken and no card is collected.'} All of the trip is booked or none of it. Preferences - a bed type, a floor, a late arrival - ride along on the booking as requests.`
215
- : `Books the trip's rooms with ${v.brand} for a named guest: each stay becomes the hotel's own reservation, with its own confirmation code, paid at the hotel. No payment is taken here and no card is collected. All of the trip is booked or none of it. Preferences - a bed type, a floor, a late arrival - ride along on the booking as requests the desk tries to meet.`,
215
+ : v.sandbox
216
+ ? `Makes a booking in a simulated copy of ${v.brand}'s booking system, for testing: each stay gets its own confirmation code there, and none of it is a ${v.brand} reservation or holds a real room. ${caps.prepaid
217
+ ? 'A rate marked paid in advance is booked on its own, and not at once: the answer gives a card page in test mode and the time it is paid by, and the stay is booked once it is paid - no card is collected here. Every other rate is paid by nobody.'
218
+ : 'No payment is taken and no card is collected.'} All of the trip is booked or none of it. Preferences - a bed type, a floor, a late arrival - ride along on the booking as requests.`
219
+ : `Books the trip's rooms with ${v.brand} for a named guest: each stay becomes the hotel's own reservation, with its own confirmation code, paid at the hotel. No payment is taken here and no card is collected. All of the trip is booked or none of it. Preferences - a bed type, a floor, a late arrival - ride along on the booking as requests the desk tries to meet.`,
216
220
  };
217
221
  }
218
222
  }
@@ -1292,12 +1296,14 @@ const holderOf = (deps, supplier) => {
1292
1296
  caps: s.capabilities,
1293
1297
  demo: s.mode !== 'live',
1294
1298
  sandbox: s.mode === 'sandbox',
1299
+ test: s.mode === 'test',
1295
1300
  };
1296
1301
  return {
1297
1302
  name: deps.vocab.brand,
1298
1303
  caps: deps.backend.capabilities ?? FULL_CAPABILITIES,
1299
1304
  demo: deps.vocab.demo,
1300
1305
  sandbox: !!deps.vocab.sandbox,
1306
+ test: !!deps.vocab.test,
1301
1307
  };
1302
1308
  };
1303
1309
  /**
@@ -1332,7 +1338,9 @@ const paidLine = (deps, b) => {
1332
1338
  const amount = formatMoney(p.amount);
1333
1339
  const test = h.sandbox
1334
1340
  ? ' This is a test: the card page is in test mode, no real money moves, and the booking is in the simulated system.'
1335
- : '';
1341
+ : h.test
1342
+ ? ` This is a test: the card page is in test mode, no real money moves, and the booking is a test booking in ${h.name}'s test system.`
1343
+ : '';
1336
1344
  switch (p.status) {
1337
1345
  case 'awaiting':
1338
1346
  return `**Not booked yet, and nothing paid.** ${amount} is 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.${test}`;
@@ -1372,6 +1380,8 @@ const bookedLine = (deps, b) => {
1372
1380
  if (h.caps.booking === 'pay-at-hotel') {
1373
1381
  if (h.sandbox)
1374
1382
  return `${total} in the simulated system. **This is a test booking** - not a ${h.name} reservation: no room is held and nothing is paid, now or at any hotel.`;
1383
+ if (h.test)
1384
+ return `${total} in ${h.name}'s test system. **This is a test booking** - it holds no room for anyone, nothing is paid, now or at any hotel, and nobody needs to go there.`;
1375
1385
  return b.status === 'requested'
1376
1386
  ? `${total} all in, to be paid at the hotel. ${h.name} has not confirmed this booking yet - that takes up to eight minutes, and ${deps.tools.listBookings} shows when it has. Nothing has been charged.`
1377
1387
  : `${total} all in, paid at the hotel. **Nothing has been charged here** - this is ${h.name}'s own reservation, kept until it is cancelled, and ${b.guest.name} pays at check-in.`;
@@ -1413,6 +1423,11 @@ const common = (deps, key) => {
1413
1423
  // In a market each group says whether it needs a phone (`market.suppliers`).
1414
1424
  ...(caps.requiresPhone && !deps.backend.suppliers ? { phoneRequired: true } : {}),
1415
1425
  ...(deps.vocab.sandbox ? { sandbox: true } : {}),
1426
+ // The group's own test system: the card says "test", never "simulated" -
1427
+ // and where it sells rates paid in advance, that those are paid with a
1428
+ // test card, never that "nothing is paid" (华住, 2026-10-15).
1429
+ ...(deps.vocab.test ? { test: true } : {}),
1430
+ ...(deps.vocab.test && caps.prepaid ? { testPays: true } : {}),
1416
1431
  ...(caps.programme ? { programme: caps.programme } : {}),
1417
1432
  ...(caps.codePrefix ? { codePrefix: caps.codePrefix } : {}),
1418
1433
  // Sells rooms by the hour: the card draws its Overnight / By the hour
@@ -1424,7 +1439,7 @@ const common = (deps, key) => {
1424
1439
  ? { market: { name: deps.vocab.brand, suppliers: deps.backend.suppliers() } }
1425
1440
  : {}),
1426
1441
  vocab: { noun: deps.vocab.noun, nounPlural: deps.vocab.nounPlural },
1427
- ...(deps.vocab.demo ? { demo: true, demoNote: DEMO_NOTE } : { demo: false }),
1442
+ ...(deps.vocab.demo ? { demo: true, demoNote: noteFor(deps.vocab) } : { demo: false }),
1428
1443
  ...(key ? memberBadge(deps, key) : {}),
1429
1444
  ...(langOf(deps, key) ? { lang: langOf(deps, key) } : {}),
1430
1445
  };
@@ -1463,6 +1478,7 @@ const quoteForCard = (quote, groupOf) => ({
1463
1478
  ...(l.bedTypes ? { bedTypes: l.bedTypes } : {}),
1464
1479
  ...(l.window ? { window: l.window } : {}),
1465
1480
  ...(l.meal ? { meal: l.meal } : {}),
1481
+ ...(l.rate ? { rate: l.rate } : {}),
1466
1482
  stay: l.stay,
1467
1483
  nights: l.nights,
1468
1484
  ...(l.hourly ? { hourly: l.hourly } : {}),
@@ -2067,7 +2083,11 @@ const currentTrip = async (deps, key, who) => {
2067
2083
  ...needs,
2068
2084
  count: lines.length,
2069
2085
  },
2070
- text: [tripText(only, false), orphans.line, deps.vocab.demo && lines.length ? DEMO_NOTE : '']
2086
+ text: [
2087
+ tripText(only, false),
2088
+ orphans.line,
2089
+ deps.vocab.demo && lines.length ? noteFor(deps.vocab) : '',
2090
+ ]
2071
2091
  .filter(Boolean)
2072
2092
  .join('\n'),
2073
2093
  };
@@ -2108,7 +2128,7 @@ const currentTrip = async (deps, key, who) => {
2108
2128
  `This trip is in ${parts.length} currencies, priced separately and not converted - each hotel is booked and paid in its own.`,
2109
2129
  ...parts.map((q) => tripText(q, false)),
2110
2130
  orphans.line,
2111
- deps.vocab.demo ? DEMO_NOTE : '',
2131
+ deps.vocab.demo ? noteFor(deps.vocab) : '',
2112
2132
  ]
2113
2133
  .filter(Boolean)
2114
2134
  .join('\n\n'),
@@ -2921,7 +2941,7 @@ const answerTool = async (deps, name, rawArgs, ctx) => {
2921
2941
  : `**${h.name}** · ${offerCount(enough)} for ${stay.checkIn} to ${stay.checkOut}, ${stay.guests} guests${roomsWanted > 1 ? `, ${roomsWanted} rooms` : ''}.`,
2922
2942
  ...enough.map((r) => `- ${r.name}${labelWords(r)} — ${priceWords(r)}${roomsWanted > 1 ? ` (${formatMoney(multiplyMoney(r.stayTotal, roomsWanted))} for ${roomsWanted} rooms)` : ''}${r.freeCancellationUntil ? `, free to cancel until ${freeUntilText(r)}` : r.refundable ? ', the free cancellation window has closed - cancelling now has a fee' : ', non-refundable'}`),
2923
2943
  flex.text,
2924
- deps.vocab.demo ? DEMO_NOTE : '',
2944
+ deps.vocab.demo ? noteFor(deps.vocab) : '',
2925
2945
  ]
2926
2946
  .filter(Boolean)
2927
2947
  .join('\n'),
@@ -3054,7 +3074,7 @@ const answerTool = async (deps, name, rawArgs, ctx) => {
3054
3074
  // Only tools this deployment offers: a name it withholds is one the
3055
3075
  // model would try (2026-10-05 audit, P2 - Huazhu has no add-ons).
3056
3076
  [t.listHotels, t.getHotel, t.listExtras].filter((n) => !deps.offered || deps.offered.has(n)))} would each draw a card of their own.`,
3057
- deps.vocab.demo ? DEMO_NOTE : '',
3077
+ deps.vocab.demo ? noteFor(deps.vocab) : '',
3058
3078
  ]
3059
3079
  .filter(Boolean)
3060
3080
  .join('\n\n'),
@@ -3138,7 +3158,7 @@ const answerTool = async (deps, name, rawArgs, ctx) => {
3138
3158
  text: extras.length
3139
3159
  ? extras
3140
3160
  .map((e) => `- ${e.name} — ${formatMoney(e.price)} ${unitWords(e.per)}${held[e.id] ? ` (${booked?.confirmationCode} holds ${held[e.id]})` : ''}`)
3141
- .concat(deps.vocab.demo ? [DEMO_NOTE] : [])
3161
+ .concat(deps.vocab.demo ? [noteFor(deps.vocab)] : [])
3142
3162
  .join('\n')
3143
3163
  : `${detail.hotel.name} has no add-ons listed here.`,
3144
3164
  };
@@ -3579,7 +3599,7 @@ const answerTool = async (deps, name, rawArgs, ctx) => {
3579
3599
  ...failedText,
3580
3600
  remembered?.line ?? '',
3581
3601
  others ? `The trip's other stays are still in it, not booked:\n${rest?.text ?? ''}` : '',
3582
- deps.vocab.demo ? DEMO_NOTE : '',
3602
+ deps.vocab.demo ? noteFor(deps.vocab) : '',
3583
3603
  ]
3584
3604
  .filter(Boolean)
3585
3605
  .join('\n'),
@@ -3723,7 +3743,7 @@ const answerTool = async (deps, name, rawArgs, ctx) => {
3723
3743
  preview.rule,
3724
3744
  `Say the word and it moves (${t.changeBooking} with \`confirmed\`).`,
3725
3745
  demoBookingNote(deps, preview.supplier),
3726
- deps.vocab.demo ? DEMO_NOTE : '',
3746
+ deps.vocab.demo ? noteFor(deps.vocab) : '',
3727
3747
  ]
3728
3748
  .filter(Boolean)
3729
3749
  .join(' ')
@@ -3786,7 +3806,7 @@ const answerTool = async (deps, name, rawArgs, ctx) => {
3786
3806
  `${changed.confirmationCode} now holds ${changed.stay.checkIn} to ${changed.stay.checkOut}, ${formatMoney(changed.quote.total)} all in.`,
3787
3807
  'The confirmation code has not changed. **Nothing has been charged.**',
3788
3808
  demoBookingNote(deps, changed.supplier),
3789
- deps.vocab.demo ? DEMO_NOTE : '',
3809
+ deps.vocab.demo ? noteFor(deps.vocab) : '',
3790
3810
  ]
3791
3811
  .filter(Boolean)
3792
3812
  .join(' '),
@@ -3834,7 +3854,7 @@ const answerTool = async (deps, name, rawArgs, ctx) => {
3834
3854
  ? `Say the word and it is cancelled (${t.cancelBooking} with \`confirmed\`).`
3835
3855
  : '',
3836
3856
  demoBookingNote(deps, preview.supplier),
3837
- deps.vocab.demo ? DEMO_NOTE : '',
3857
+ deps.vocab.demo ? noteFor(deps.vocab) : '',
3838
3858
  ]
3839
3859
  .filter(Boolean)
3840
3860
  .join(' '),
@@ -3886,12 +3906,14 @@ const answerTool = async (deps, name, rawArgs, ctx) => {
3886
3906
  : h.sandbox
3887
3907
  ? // A test booking cancelled in the simulation: no hotel, no charge (2026-10-05 audit, P2).
3888
3908
  `${cancelled.confirmationCode} is cancelled in the simulated system. ${charged.minor === 0 ? 'No fee.' : `A real booking on this rate would be charged ${formatMoney(charged)}; this one is a test, and nothing is charged.`}`
3889
- : h.demo
3890
- ? // Nobody charges a demonstration anything (2026-10-05 audit, P2).
3891
- `${cancelled.confirmationCode} is cancelled. ${charged.minor === 0 ? 'No fee.' : `A real booking on this rate would be charged ${formatMoney(charged)}; this is a demonstration, and nothing is charged.`}`
3892
- : `${cancelled.confirmationCode} is cancelled. ${charged.minor === 0 ? 'No fee.' : `The hotel charges ${formatMoney(charged)}.`}`,
3909
+ : h.test
3910
+ ? `${cancelled.confirmationCode} is cancelled in ${h.name}'s test system. ${charged.minor === 0 ? 'No fee.' : `A live booking on this rate would be charged ${formatMoney(charged)}; this one is a test, and nothing is charged.`}`
3911
+ : h.demo
3912
+ ? // Nobody charges a demonstration anything (2026-10-05 audit, P2).
3913
+ `${cancelled.confirmationCode} is cancelled. ${charged.minor === 0 ? 'No fee.' : `A real booking on this rate would be charged ${formatMoney(charged)}; this is a demonstration, and nothing is charged.`}`
3914
+ : `${cancelled.confirmationCode} is cancelled. ${charged.minor === 0 ? 'No fee.' : `The hotel charges ${formatMoney(charged)}.`}`,
3893
3915
  demoBookingNote(deps, cancelled.supplier),
3894
- deps.vocab.demo ? DEMO_NOTE : '',
3916
+ deps.vocab.demo ? noteFor(deps.vocab) : '',
3895
3917
  ]
3896
3918
  .filter(Boolean)
3897
3919
  .join(' '),
@@ -3955,7 +3977,7 @@ const answerTool = async (deps, name, rawArgs, ctx) => {
3955
3977
  : '',
3956
3978
  ...comparison.dropped.map((d) => `Left out: ${d.name} — ${d.why}`),
3957
3979
  ...fitsManyOf(deps, comparison.dropped),
3958
- deps.vocab.demo ? DEMO_NOTE : '',
3980
+ deps.vocab.demo ? noteFor(deps.vocab) : '',
3959
3981
  ]
3960
3982
  .filter(Boolean)
3961
3983
  .join('\n'),
@@ -3999,7 +4021,7 @@ const answerTool = async (deps, name, rawArgs, ctx) => {
3999
4021
  stays.length ? `Stays, priced on: ${stays.join('; ')}.` : '',
4000
4022
  ...plan.dropped.map((d) => `Left out: ${d.name} — ${d.why}`),
4001
4023
  ...fitsManyOf(deps, plan.dropped),
4002
- deps.vocab.demo ? DEMO_NOTE : '',
4024
+ deps.vocab.demo ? noteFor(deps.vocab) : '',
4003
4025
  ]
4004
4026
  .filter(Boolean)
4005
4027
  .join('\n'),
@@ -4048,7 +4070,7 @@ const answerTool = async (deps, name, rawArgs, ctx) => {
4048
4070
  member.preferences?.length
4049
4071
  ? `Saved preferences on this membership: ${member.preferences.map((k) => REQUEST_WORDS[k]).join(', ')}. They are ticked on the booking form; mention them, and ask before booking with them.`
4050
4072
  : '',
4051
- deps.vocab.demo ? DEMO_NOTE : '',
4073
+ deps.vocab.demo ? noteFor(deps.vocab) : '',
4052
4074
  ]
4053
4075
  .filter(Boolean)
4054
4076
  .join(' '),