@curless/hotel-storefront 0.25.0 → 0.26.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.
- package/dist/__tests__/__helpers__/sessions.d.ts +10 -0
- package/dist/__tests__/__helpers__/sessions.d.ts.map +1 -0
- package/dist/__tests__/__helpers__/sessions.js +37 -0
- package/dist/__tests__/__helpers__/sessions.js.map +1 -0
- package/dist/__tests__/card-contract.test.js +20 -0
- package/dist/__tests__/card-contract.test.js.map +1 -1
- package/dist/__tests__/data.test.js +7 -1
- package/dist/__tests__/data.test.js.map +1 -1
- package/dist/__tests__/dates.test.js +13 -1
- package/dist/__tests__/dates.test.js.map +1 -1
- package/dist/__tests__/huazhu-audit.test.js +7 -1
- package/dist/__tests__/huazhu-audit.test.js.map +1 -1
- package/dist/__tests__/huazhu-backend.test.js +3 -1
- package/dist/__tests__/huazhu-backend.test.js.map +1 -1
- package/dist/__tests__/huazhu-sandbox.test.js +6 -1
- package/dist/__tests__/huazhu-sandbox.test.js.map +1 -1
- package/dist/__tests__/server.test.js +111 -2
- package/dist/__tests__/server.test.js.map +1 -1
- package/dist/__tests__/state.test.d.ts +2 -0
- package/dist/__tests__/state.test.d.ts.map +1 -0
- package/dist/__tests__/state.test.js +204 -0
- package/dist/__tests__/state.test.js.map +1 -0
- package/dist/__tests__/tools.test.js +18 -0
- package/dist/__tests__/tools.test.js.map +1 -1
- package/dist/card.generated.d.ts +1 -1
- package/dist/card.generated.js +2 -2
- package/dist/card.generated.js.map +1 -1
- package/dist/dates.d.ts +9 -0
- package/dist/dates.d.ts.map +1 -1
- package/dist/dates.js +32 -0
- package/dist/dates.js.map +1 -1
- package/dist/demo-hotels.d.ts +21 -1
- package/dist/demo-hotels.d.ts.map +1 -1
- package/dist/demo-hotels.js +83 -11
- package/dist/demo-hotels.js.map +1 -1
- package/dist/huazhu/backend.d.ts.map +1 -1
- package/dist/huazhu/backend.js +10 -2
- package/dist/huazhu/backend.js.map +1 -1
- package/dist/index.js +9 -1
- package/dist/index.js.map +1 -1
- package/dist/legal.d.ts +8 -1
- package/dist/legal.d.ts.map +1 -1
- package/dist/legal.js +22 -8
- package/dist/legal.js.map +1 -1
- package/dist/members.d.ts +3 -1
- package/dist/members.d.ts.map +1 -1
- package/dist/members.js +24 -6
- package/dist/members.js.map +1 -1
- package/dist/money.d.ts.map +1 -1
- package/dist/money.js +8 -1
- package/dist/money.js.map +1 -1
- package/dist/server.d.ts +12 -8
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +101 -36
- package/dist/server.js.map +1 -1
- package/dist/sessions.d.ts +19 -0
- package/dist/sessions.d.ts.map +1 -0
- package/dist/sessions.js +96 -0
- package/dist/sessions.js.map +1 -0
- package/dist/state.d.ts +32 -0
- package/dist/state.d.ts.map +1 -0
- package/dist/state.js +118 -0
- package/dist/state.js.map +1 -0
- package/dist/tools.d.ts +11 -0
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +34 -3
- package/dist/tools.js.map +1 -1
- package/dist/trip-ids.d.ts +8 -1
- package/dist/trip-ids.d.ts.map +1 -1
- package/dist/trip-ids.js +24 -1
- package/dist/trip-ids.js.map +1 -1
- package/dist/trip.d.ts +3 -1
- package/dist/trip.d.ts.map +1 -1
- package/dist/trip.js +23 -6
- package/dist/trip.js.map +1 -1
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/state.js
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { chmodSync, mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { dirname, join } from 'node:path';
|
|
3
|
+
// What survives a restart: one JSON file per store, in the deployment's state
|
|
4
|
+
// directory (`HOTEL_STATE_DIR`).
|
|
5
|
+
//
|
|
6
|
+
// ⚠️ A DEPLOY USED TO FORGET EVERY GUEST. Bookings, trips, sign-ins and the
|
|
7
|
+
// session ids themselves were in memory, and this connector ships several times
|
|
8
|
+
// a day: a guest told "held until 18:00, SL-7K2M4P" asked to cancel after a
|
|
9
|
+
// deploy and was told no booking matched - and the lookup with their email was
|
|
10
|
+
// then counted as a wrong guess (2026-10-05 audit). Now every store writes
|
|
11
|
+
// itself here, and reads itself back on boot.
|
|
12
|
+
//
|
|
13
|
+
// ⚠️ NO DIRECTORY, NO DISK. Without one the stores stay in memory exactly as
|
|
14
|
+
// before, so a local run or a test writes nothing anywhere. And the directory
|
|
15
|
+
// is never the brand's repository: that is mounted read-only on the box.
|
|
16
|
+
//
|
|
17
|
+
// Writes are coalesced - a trip id touched on every tool call would otherwise
|
|
18
|
+
// rewrite its file on every tool call - so the last half second before a crash
|
|
19
|
+
// can be lost. A clean stop (`flush`, on SIGTERM) loses nothing.
|
|
20
|
+
const WRITE_DELAY_MS = 500;
|
|
21
|
+
export class StateFile {
|
|
22
|
+
path;
|
|
23
|
+
timer;
|
|
24
|
+
snapshot;
|
|
25
|
+
constructor(path) {
|
|
26
|
+
this.path = path;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* What was written last time, or undefined when there is nothing usable.
|
|
30
|
+
*
|
|
31
|
+
* ⚠️ A FILE THAT DOES NOT PARSE IS SET ASIDE, NOT OVERWRITTEN. Starting
|
|
32
|
+
* empty and writing over it would destroy the only copy of somebody's
|
|
33
|
+
* booking; renamed, a person can still look at it.
|
|
34
|
+
*/
|
|
35
|
+
read() {
|
|
36
|
+
let text;
|
|
37
|
+
try {
|
|
38
|
+
text = readFileSync(this.path, 'utf8');
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return undefined; // none yet
|
|
42
|
+
}
|
|
43
|
+
try {
|
|
44
|
+
return JSON.parse(text);
|
|
45
|
+
}
|
|
46
|
+
catch (err) {
|
|
47
|
+
const aside = `${this.path}.corrupt-${Date.now()}`;
|
|
48
|
+
try {
|
|
49
|
+
renameSync(this.path, aside);
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
// Unreadable and unmovable: starting empty is all that is left.
|
|
53
|
+
}
|
|
54
|
+
console.error(`[state] ${this.path} did not parse (${err.message}); kept as ${aside}`);
|
|
55
|
+
return undefined;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/** What to write. The store hands over a function, so the write takes the state as it is THEN. */
|
|
59
|
+
bind(snapshot) {
|
|
60
|
+
this.snapshot = snapshot;
|
|
61
|
+
}
|
|
62
|
+
/** Something changed: write it soon. */
|
|
63
|
+
changed() {
|
|
64
|
+
if (this.timer)
|
|
65
|
+
return;
|
|
66
|
+
this.timer = setTimeout(() => this.flush(), WRITE_DELAY_MS);
|
|
67
|
+
this.timer.unref?.();
|
|
68
|
+
}
|
|
69
|
+
/** Write now if anything is waiting. */
|
|
70
|
+
flush() {
|
|
71
|
+
if (!this.timer)
|
|
72
|
+
return;
|
|
73
|
+
clearTimeout(this.timer);
|
|
74
|
+
this.timer = undefined;
|
|
75
|
+
this.write();
|
|
76
|
+
}
|
|
77
|
+
write() {
|
|
78
|
+
if (!this.snapshot)
|
|
79
|
+
return;
|
|
80
|
+
try {
|
|
81
|
+
mkdirSync(dirname(this.path), { recursive: true });
|
|
82
|
+
const tmp = `${this.path}.${process.pid}.tmp`;
|
|
83
|
+
// ⚠️ OWNER-ONLY. These files hold names, emails and phone numbers.
|
|
84
|
+
writeFileSync(tmp, JSON.stringify(this.snapshot()), { mode: 0o600 });
|
|
85
|
+
chmodSync(tmp, 0o600);
|
|
86
|
+
renameSync(tmp, this.path);
|
|
87
|
+
}
|
|
88
|
+
catch (err) {
|
|
89
|
+
// A full disk must not take the conversation down with it: the state is
|
|
90
|
+
// still right in memory, and the next change tries again.
|
|
91
|
+
console.error(`[state] could not write ${this.path}: ${err.message}`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/** Where the stores of one deployment keep their files - or nothing, when it has no directory. */
|
|
96
|
+
export class StateDir {
|
|
97
|
+
dir;
|
|
98
|
+
files = [];
|
|
99
|
+
constructor(dir) {
|
|
100
|
+
this.dir = dir;
|
|
101
|
+
}
|
|
102
|
+
get persisted() {
|
|
103
|
+
return this.dir !== undefined;
|
|
104
|
+
}
|
|
105
|
+
file(name) {
|
|
106
|
+
if (!this.dir)
|
|
107
|
+
return undefined;
|
|
108
|
+
const f = new StateFile(join(this.dir, name));
|
|
109
|
+
this.files.push(f);
|
|
110
|
+
return f;
|
|
111
|
+
}
|
|
112
|
+
/** Everything still waiting, written now - on a clean stop. */
|
|
113
|
+
flush() {
|
|
114
|
+
for (const f of this.files)
|
|
115
|
+
f.flush();
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
//# sourceMappingURL=state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state.js","sourceRoot":"","sources":["../src/state.ts"],"names":[],"mappings":"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"}
|
package/dist/tools.d.ts
CHANGED
|
@@ -787,5 +787,16 @@ export type ToolAnswer = {
|
|
|
787
787
|
* read better as hotels, grouped by city.
|
|
788
788
|
*/
|
|
789
789
|
export declare const CITY_PICKER_MIN = 4;
|
|
790
|
+
/**
|
|
791
|
+
* The answer when the card names a trip this connector no longer knows.
|
|
792
|
+
*
|
|
793
|
+
* ⚠️ SAID, NOT PAPERED OVER. An unknown id used to fall back to the card's own
|
|
794
|
+
* connection - a DIFFERENT trip from the conversation's - and the card took
|
|
795
|
+
* that trip's id as its own. Rooms added on the card then went into a trip the
|
|
796
|
+
* assistant could not see, and "book it" was answered "the trip is empty"
|
|
797
|
+
* (2026-10-05 audit). The way back is the conversation's own trip, which the
|
|
798
|
+
* assistant's next answer hands the card.
|
|
799
|
+
*/
|
|
800
|
+
export declare const tripGone: (deps: ToolDeps) => ToolAnswer;
|
|
790
801
|
export declare const handleTool: (deps: ToolDeps, name: string, rawArgs: unknown, ctx: ToolContext) => Promise<ToolAnswer>;
|
|
791
802
|
//# sourceMappingURL=tools.d.ts.map
|
package/dist/tools.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,mBAAmB,EAOzB,MAAM,cAAc,CAAC;AACtB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAI7C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAgBhD,OAAO,
|
|
1
|
+
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"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"}
|
package/dist/tools.js
CHANGED
|
@@ -5,7 +5,7 @@ import { DESCRIPTION_MAX, POLICY_MAX, hotelText } from './fence.js';
|
|
|
5
5
|
import { formatMoney, money, multiplyMoney, toMinor } from './money.js';
|
|
6
6
|
import { MAX_COMPARED, MAX_CONS, MAX_DAYS, MAX_ITEMS_PER_DAY, MAX_MOMENTS, MAX_PROS, buildItinerary, compareHotels, } from './present.js';
|
|
7
7
|
import { regionOf } from './regions.js';
|
|
8
|
-
import { MAX_EXTRA_QUANTITY, MAX_ROOMS_PER_LINE, clampCount, tripKey, } from './trip.js';
|
|
8
|
+
import { MAX_EXTRA_QUANTITY, MAX_ROOMS_PER_LINE, TRIP_TTL_MS, clampCount, tripKey, } from './trip.js';
|
|
9
9
|
import { DEMO_NOTE } from './vocab.js';
|
|
10
10
|
// The eleven things a guest can do: find hotels, look at one on dates, see what
|
|
11
11
|
// else it offers, build a trip, ask for the rooms, and deal with a booking they
|
|
@@ -1007,6 +1007,13 @@ const common = (deps, key) => ({
|
|
|
1007
1007
|
...(deps.vocab.demo ? { demo: true, demoNote: DEMO_NOTE } : { demo: false }),
|
|
1008
1008
|
...(key ? memberBadge(deps, key) : {}),
|
|
1009
1009
|
});
|
|
1010
|
+
/**
|
|
1011
|
+
* When a room stops being free to cancel, the way a guest reads it: the
|
|
1012
|
+
* hotel's own clock when the backend knows it, else the instant in UTC - never
|
|
1013
|
+
* the date alone (see `atHotelTime`).
|
|
1014
|
+
*/
|
|
1015
|
+
const freeUntilText = (r) => r.freeCancellationLocal ??
|
|
1016
|
+
`${(r.freeCancellationUntil ?? '').slice(0, 16).replace('T', ' ')} UTC`;
|
|
1010
1017
|
/** One line of a trip, flattened for the card. Prices are strings: the card never does arithmetic. */
|
|
1011
1018
|
const quoteForCard = (quote) => ({
|
|
1012
1019
|
lines: quote.lines.map((l) => ({
|
|
@@ -1704,6 +1711,23 @@ const suggestionsFor = async (deps, d) => {
|
|
|
1704
1711
|
})
|
|
1705
1712
|
.slice(0, 3);
|
|
1706
1713
|
};
|
|
1714
|
+
/**
|
|
1715
|
+
* The answer when the card names a trip this connector no longer knows.
|
|
1716
|
+
*
|
|
1717
|
+
* ⚠️ SAID, NOT PAPERED OVER. An unknown id used to fall back to the card's own
|
|
1718
|
+
* connection - a DIFFERENT trip from the conversation's - and the card took
|
|
1719
|
+
* that trip's id as its own. Rooms added on the card then went into a trip the
|
|
1720
|
+
* assistant could not see, and "book it" was answered "the trip is empty"
|
|
1721
|
+
* (2026-10-05 audit). The way back is the conversation's own trip, which the
|
|
1722
|
+
* assistant's next answer hands the card.
|
|
1723
|
+
*/
|
|
1724
|
+
export const tripGone = (deps) => {
|
|
1725
|
+
const detail = `The trip this card was showing is no longer kept - a trip is kept for ${TRIP_TTL_MS / 3_600_000} hours after its last change. Ask the assistant to show the trip again.`;
|
|
1726
|
+
return {
|
|
1727
|
+
structured: { view: 'error', ...common(deps), error: 'trip-gone', detail },
|
|
1728
|
+
text: detail,
|
|
1729
|
+
};
|
|
1730
|
+
};
|
|
1707
1731
|
export const handleTool = async (deps, name, rawArgs, ctx) => {
|
|
1708
1732
|
const answer = await answerTool(deps, name, rawArgs, ctx);
|
|
1709
1733
|
const suggestions = await suggestionsFor(deps, answer.structured).catch(() => []);
|
|
@@ -2188,6 +2212,13 @@ const answerTool = async (deps, name, rawArgs, ctx) => {
|
|
|
2188
2212
|
// string, not by anyone looking at it.
|
|
2189
2213
|
...(r.publicNightly ? { publicNightly: formatMoney(r.publicNightly) } : {}),
|
|
2190
2214
|
...(r.publicStayTotal ? { publicStayTotal: formatMoney(r.publicStayTotal) } : {}),
|
|
2215
|
+
// ⚠️ AND STRUCK THROUGH FOR AS MANY ROOMS AS THE PRICE BESIDE IT. One
|
|
2216
|
+
// room's public rate drawn over two rooms' member rate put a "was" price
|
|
2217
|
+
// BELOW the price a member pays (2026-10-05 audit).
|
|
2218
|
+
...(r.publicStayTotal && roomsWanted > 1
|
|
2219
|
+
? { publicRoomsTotal: formatMoney(multiplyMoney(r.publicStayTotal, roomsWanted)) }
|
|
2220
|
+
: {}),
|
|
2221
|
+
...(r.freeCancellationUntil ? { freeCancellationLocal: freeUntilText(r) } : {}),
|
|
2191
2222
|
}));
|
|
2192
2223
|
return {
|
|
2193
2224
|
structured: {
|
|
@@ -2200,7 +2231,7 @@ const answerTool = async (deps, name, rawArgs, ctx) => {
|
|
|
2200
2231
|
},
|
|
2201
2232
|
text: [
|
|
2202
2233
|
`**${h.name}** · ${enough.length} room ${enough.length === 1 ? 'type' : 'types'} for ${stay.checkIn} to ${stay.checkOut}, ${stay.guests} guests${roomsWanted > 1 ? `, ${roomsWanted} rooms` : ''}.`,
|
|
2203
|
-
...enough.map((r) => `- ${r.name} — ${formatMoney(r.nightly)} a night, ${formatMoney(r.stayTotal)} for the stay incl. taxes and fees${roomsWanted > 1 ? ` (${formatMoney(multiplyMoney(r.stayTotal, roomsWanted))} for ${roomsWanted} rooms)` : ''}${r.freeCancellationUntil ? `, free to cancel until ${r
|
|
2234
|
+
...enough.map((r) => `- ${r.name} — ${formatMoney(r.nightly)} a night, ${formatMoney(r.stayTotal)} for the stay incl. taxes and fees${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'}`),
|
|
2204
2235
|
flex.text,
|
|
2205
2236
|
deps.vocab.demo ? DEMO_NOTE : '',
|
|
2206
2237
|
]
|
|
@@ -2281,7 +2312,7 @@ const answerTool = async (deps, name, rawArgs, ctx) => {
|
|
|
2281
2312
|
: rooms.length
|
|
2282
2313
|
? [
|
|
2283
2314
|
`Rooms for ${stay.checkIn} to ${stay.checkOut}, ${stay.guests} guests${roomsWanted > 1 ? `, ${roomsWanted} rooms` : ''}:`,
|
|
2284
|
-
...rooms.map((r) => `- ${r.name} · sleeps ${r.sleeps}${r.breakfast ? ' · breakfast' : ''} · ${formatMoney(r.stayTotal)} for the stay incl. taxes and fees${r.freeCancellationUntil ? `, free to cancel until ${r
|
|
2315
|
+
...rooms.map((r) => `- ${r.name} · sleeps ${r.sleeps}${r.breakfast ? ' · breakfast' : ''} · ${formatMoney(r.stayTotal)} for the stay incl. taxes and fees${r.freeCancellationUntil ? `, free to cancel until ${freeUntilText(r)}` : r.refundable ? '' : ', non-refundable'}`),
|
|
2285
2316
|
].join('\n')
|
|
2286
2317
|
: (detail.noRooms ?? `No room type here has ${roomsWanted} left for those nights.`),
|
|
2287
2318
|
extras.length
|