@curless/hotel-storefront 0.34.0 → 0.35.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 (44) 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 +13 -0
  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 +38 -1
  15. package/dist/huazhu/backend.js.map +1 -1
  16. package/dist/legal.d.ts.map +1 -1
  17. package/dist/legal.js +118 -82
  18. package/dist/legal.js.map +1 -1
  19. package/dist/payments/checkout.d.ts +24 -1
  20. package/dist/payments/checkout.d.ts.map +1 -1
  21. package/dist/payments/checkout.js.map +1 -1
  22. package/dist/payments/fake-stripe.d.ts +34 -0
  23. package/dist/payments/fake-stripe.d.ts.map +1 -0
  24. package/dist/payments/fake-stripe.js +93 -0
  25. package/dist/payments/fake-stripe.js.map +1 -0
  26. package/dist/payments/stripe-checkout.d.ts +36 -0
  27. package/dist/payments/stripe-checkout.d.ts.map +1 -0
  28. package/dist/payments/stripe-checkout.js +126 -0
  29. package/dist/payments/stripe-checkout.js.map +1 -0
  30. package/dist/server.d.ts +2 -2
  31. package/dist/server.js +2 -2
  32. package/dist/surface.d.ts.map +1 -1
  33. package/dist/surface.js +34 -20
  34. package/dist/surface.js.map +1 -1
  35. package/dist/tools.d.ts.map +1 -1
  36. package/dist/tools.js +42 -23
  37. package/dist/tools.js.map +1 -1
  38. package/dist/types.d.ts +17 -0
  39. package/dist/types.d.ts.map +1 -1
  40. package/dist/vocab.d.ts +13 -0
  41. package/dist/vocab.d.ts.map +1 -1
  42. package/dist/vocab.js +22 -2
  43. package/dist/vocab.js.map +1 -1
  44. 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,8 @@ 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
+ ...(deps.vocab.test ? { test: true } : {}),
1416
1428
  ...(caps.programme ? { programme: caps.programme } : {}),
1417
1429
  ...(caps.codePrefix ? { codePrefix: caps.codePrefix } : {}),
1418
1430
  // Sells rooms by the hour: the card draws its Overnight / By the hour
@@ -1424,7 +1436,7 @@ const common = (deps, key) => {
1424
1436
  ? { market: { name: deps.vocab.brand, suppliers: deps.backend.suppliers() } }
1425
1437
  : {}),
1426
1438
  vocab: { noun: deps.vocab.noun, nounPlural: deps.vocab.nounPlural },
1427
- ...(deps.vocab.demo ? { demo: true, demoNote: DEMO_NOTE } : { demo: false }),
1439
+ ...(deps.vocab.demo ? { demo: true, demoNote: noteFor(deps.vocab) } : { demo: false }),
1428
1440
  ...(key ? memberBadge(deps, key) : {}),
1429
1441
  ...(langOf(deps, key) ? { lang: langOf(deps, key) } : {}),
1430
1442
  };
@@ -1463,6 +1475,7 @@ const quoteForCard = (quote, groupOf) => ({
1463
1475
  ...(l.bedTypes ? { bedTypes: l.bedTypes } : {}),
1464
1476
  ...(l.window ? { window: l.window } : {}),
1465
1477
  ...(l.meal ? { meal: l.meal } : {}),
1478
+ ...(l.rate ? { rate: l.rate } : {}),
1466
1479
  stay: l.stay,
1467
1480
  nights: l.nights,
1468
1481
  ...(l.hourly ? { hourly: l.hourly } : {}),
@@ -2067,7 +2080,11 @@ const currentTrip = async (deps, key, who) => {
2067
2080
  ...needs,
2068
2081
  count: lines.length,
2069
2082
  },
2070
- text: [tripText(only, false), orphans.line, deps.vocab.demo && lines.length ? DEMO_NOTE : '']
2083
+ text: [
2084
+ tripText(only, false),
2085
+ orphans.line,
2086
+ deps.vocab.demo && lines.length ? noteFor(deps.vocab) : '',
2087
+ ]
2071
2088
  .filter(Boolean)
2072
2089
  .join('\n'),
2073
2090
  };
@@ -2108,7 +2125,7 @@ const currentTrip = async (deps, key, who) => {
2108
2125
  `This trip is in ${parts.length} currencies, priced separately and not converted - each hotel is booked and paid in its own.`,
2109
2126
  ...parts.map((q) => tripText(q, false)),
2110
2127
  orphans.line,
2111
- deps.vocab.demo ? DEMO_NOTE : '',
2128
+ deps.vocab.demo ? noteFor(deps.vocab) : '',
2112
2129
  ]
2113
2130
  .filter(Boolean)
2114
2131
  .join('\n\n'),
@@ -2921,7 +2938,7 @@ const answerTool = async (deps, name, rawArgs, ctx) => {
2921
2938
  : `**${h.name}** · ${offerCount(enough)} for ${stay.checkIn} to ${stay.checkOut}, ${stay.guests} guests${roomsWanted > 1 ? `, ${roomsWanted} rooms` : ''}.`,
2922
2939
  ...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
2940
  flex.text,
2924
- deps.vocab.demo ? DEMO_NOTE : '',
2941
+ deps.vocab.demo ? noteFor(deps.vocab) : '',
2925
2942
  ]
2926
2943
  .filter(Boolean)
2927
2944
  .join('\n'),
@@ -3054,7 +3071,7 @@ const answerTool = async (deps, name, rawArgs, ctx) => {
3054
3071
  // Only tools this deployment offers: a name it withholds is one the
3055
3072
  // model would try (2026-10-05 audit, P2 - Huazhu has no add-ons).
3056
3073
  [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 : '',
3074
+ deps.vocab.demo ? noteFor(deps.vocab) : '',
3058
3075
  ]
3059
3076
  .filter(Boolean)
3060
3077
  .join('\n\n'),
@@ -3138,7 +3155,7 @@ const answerTool = async (deps, name, rawArgs, ctx) => {
3138
3155
  text: extras.length
3139
3156
  ? extras
3140
3157
  .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] : [])
3158
+ .concat(deps.vocab.demo ? [noteFor(deps.vocab)] : [])
3142
3159
  .join('\n')
3143
3160
  : `${detail.hotel.name} has no add-ons listed here.`,
3144
3161
  };
@@ -3579,7 +3596,7 @@ const answerTool = async (deps, name, rawArgs, ctx) => {
3579
3596
  ...failedText,
3580
3597
  remembered?.line ?? '',
3581
3598
  others ? `The trip's other stays are still in it, not booked:\n${rest?.text ?? ''}` : '',
3582
- deps.vocab.demo ? DEMO_NOTE : '',
3599
+ deps.vocab.demo ? noteFor(deps.vocab) : '',
3583
3600
  ]
3584
3601
  .filter(Boolean)
3585
3602
  .join('\n'),
@@ -3723,7 +3740,7 @@ const answerTool = async (deps, name, rawArgs, ctx) => {
3723
3740
  preview.rule,
3724
3741
  `Say the word and it moves (${t.changeBooking} with \`confirmed\`).`,
3725
3742
  demoBookingNote(deps, preview.supplier),
3726
- deps.vocab.demo ? DEMO_NOTE : '',
3743
+ deps.vocab.demo ? noteFor(deps.vocab) : '',
3727
3744
  ]
3728
3745
  .filter(Boolean)
3729
3746
  .join(' ')
@@ -3786,7 +3803,7 @@ const answerTool = async (deps, name, rawArgs, ctx) => {
3786
3803
  `${changed.confirmationCode} now holds ${changed.stay.checkIn} to ${changed.stay.checkOut}, ${formatMoney(changed.quote.total)} all in.`,
3787
3804
  'The confirmation code has not changed. **Nothing has been charged.**',
3788
3805
  demoBookingNote(deps, changed.supplier),
3789
- deps.vocab.demo ? DEMO_NOTE : '',
3806
+ deps.vocab.demo ? noteFor(deps.vocab) : '',
3790
3807
  ]
3791
3808
  .filter(Boolean)
3792
3809
  .join(' '),
@@ -3834,7 +3851,7 @@ const answerTool = async (deps, name, rawArgs, ctx) => {
3834
3851
  ? `Say the word and it is cancelled (${t.cancelBooking} with \`confirmed\`).`
3835
3852
  : '',
3836
3853
  demoBookingNote(deps, preview.supplier),
3837
- deps.vocab.demo ? DEMO_NOTE : '',
3854
+ deps.vocab.demo ? noteFor(deps.vocab) : '',
3838
3855
  ]
3839
3856
  .filter(Boolean)
3840
3857
  .join(' '),
@@ -3886,12 +3903,14 @@ const answerTool = async (deps, name, rawArgs, ctx) => {
3886
3903
  : h.sandbox
3887
3904
  ? // A test booking cancelled in the simulation: no hotel, no charge (2026-10-05 audit, P2).
3888
3905
  `${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)}.`}`,
3906
+ : h.test
3907
+ ? `${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.`}`
3908
+ : h.demo
3909
+ ? // Nobody charges a demonstration anything (2026-10-05 audit, P2).
3910
+ `${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.`}`
3911
+ : `${cancelled.confirmationCode} is cancelled. ${charged.minor === 0 ? 'No fee.' : `The hotel charges ${formatMoney(charged)}.`}`,
3893
3912
  demoBookingNote(deps, cancelled.supplier),
3894
- deps.vocab.demo ? DEMO_NOTE : '',
3913
+ deps.vocab.demo ? noteFor(deps.vocab) : '',
3895
3914
  ]
3896
3915
  .filter(Boolean)
3897
3916
  .join(' '),
@@ -3955,7 +3974,7 @@ const answerTool = async (deps, name, rawArgs, ctx) => {
3955
3974
  : '',
3956
3975
  ...comparison.dropped.map((d) => `Left out: ${d.name} — ${d.why}`),
3957
3976
  ...fitsManyOf(deps, comparison.dropped),
3958
- deps.vocab.demo ? DEMO_NOTE : '',
3977
+ deps.vocab.demo ? noteFor(deps.vocab) : '',
3959
3978
  ]
3960
3979
  .filter(Boolean)
3961
3980
  .join('\n'),
@@ -3999,7 +4018,7 @@ const answerTool = async (deps, name, rawArgs, ctx) => {
3999
4018
  stays.length ? `Stays, priced on: ${stays.join('; ')}.` : '',
4000
4019
  ...plan.dropped.map((d) => `Left out: ${d.name} — ${d.why}`),
4001
4020
  ...fitsManyOf(deps, plan.dropped),
4002
- deps.vocab.demo ? DEMO_NOTE : '',
4021
+ deps.vocab.demo ? noteFor(deps.vocab) : '',
4003
4022
  ]
4004
4023
  .filter(Boolean)
4005
4024
  .join('\n'),
@@ -4048,7 +4067,7 @@ const answerTool = async (deps, name, rawArgs, ctx) => {
4048
4067
  member.preferences?.length
4049
4068
  ? `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
4069
  : '',
4051
- deps.vocab.demo ? DEMO_NOTE : '',
4070
+ deps.vocab.demo ? noteFor(deps.vocab) : '',
4052
4071
  ]
4053
4072
  .filter(Boolean)
4054
4073
  .join(' '),