@curless/hotel-storefront 0.23.2 → 0.24.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 (91) hide show
  1. package/README.md +28 -11
  2. package/dist/__tests__/__fixtures__/huazhu-fake.d.ts +54 -0
  3. package/dist/__tests__/__fixtures__/huazhu-fake.d.ts.map +1 -0
  4. package/dist/__tests__/__fixtures__/huazhu-fake.js +340 -0
  5. package/dist/__tests__/__fixtures__/huazhu-fake.js.map +1 -0
  6. package/dist/__tests__/huazhu-backend.test.d.ts +2 -0
  7. package/dist/__tests__/huazhu-backend.test.d.ts.map +1 -0
  8. package/dist/__tests__/huazhu-backend.test.js +434 -0
  9. package/dist/__tests__/huazhu-backend.test.js.map +1 -0
  10. package/dist/__tests__/huazhu-cancel-policy.test.d.ts +2 -0
  11. package/dist/__tests__/huazhu-cancel-policy.test.d.ts.map +1 -0
  12. package/dist/__tests__/huazhu-cancel-policy.test.js +82 -0
  13. package/dist/__tests__/huazhu-cancel-policy.test.js.map +1 -0
  14. package/dist/__tests__/huazhu-sellable.test.d.ts +2 -0
  15. package/dist/__tests__/huazhu-sellable.test.d.ts.map +1 -0
  16. package/dist/__tests__/huazhu-sellable.test.js +192 -0
  17. package/dist/__tests__/huazhu-sellable.test.js.map +1 -0
  18. package/dist/__tests__/server.test.js +129 -4
  19. package/dist/__tests__/server.test.js.map +1 -1
  20. package/dist/__tests__/tool-descriptions.test.js +57 -4
  21. package/dist/__tests__/tool-descriptions.test.js.map +1 -1
  22. package/dist/backend.d.ts +27 -0
  23. package/dist/backend.d.ts.map +1 -1
  24. package/dist/backend.js +7 -0
  25. package/dist/backend.js.map +1 -1
  26. package/dist/card.generated.d.ts +1 -1
  27. package/dist/card.generated.js +2 -2
  28. package/dist/card.generated.js.map +1 -1
  29. package/dist/config.d.ts +52 -4
  30. package/dist/config.d.ts.map +1 -1
  31. package/dist/config.js.map +1 -1
  32. package/dist/facilities.d.ts.map +1 -1
  33. package/dist/facilities.js +7 -5
  34. package/dist/facilities.js.map +1 -1
  35. package/dist/huazhu/backend.d.ts +40 -0
  36. package/dist/huazhu/backend.d.ts.map +1 -0
  37. package/dist/huazhu/backend.js +1318 -0
  38. package/dist/huazhu/backend.js.map +1 -0
  39. package/dist/huazhu/bookings.d.ts +81 -0
  40. package/dist/huazhu/bookings.d.ts.map +1 -0
  41. package/dist/huazhu/bookings.js +189 -0
  42. package/dist/huazhu/bookings.js.map +1 -0
  43. package/dist/huazhu/cancel-policy.d.ts +69 -0
  44. package/dist/huazhu/cancel-policy.d.ts.map +1 -0
  45. package/dist/huazhu/cancel-policy.js +201 -0
  46. package/dist/huazhu/cancel-policy.js.map +1 -0
  47. package/dist/huazhu/catalog.d.ts +75 -0
  48. package/dist/huazhu/catalog.d.ts.map +1 -0
  49. package/dist/huazhu/catalog.js +251 -0
  50. package/dist/huazhu/catalog.js.map +1 -0
  51. package/dist/huazhu/check.d.ts +5 -0
  52. package/dist/huazhu/check.d.ts.map +1 -0
  53. package/dist/huazhu/check.js +170 -0
  54. package/dist/huazhu/check.js.map +1 -0
  55. package/dist/huazhu/client.d.ts +100 -0
  56. package/dist/huazhu/client.d.ts.map +1 -0
  57. package/dist/huazhu/client.js +232 -0
  58. package/dist/huazhu/client.js.map +1 -0
  59. package/dist/huazhu/sellable.d.ts +81 -0
  60. package/dist/huazhu/sellable.d.ts.map +1 -0
  61. package/dist/huazhu/sellable.js +219 -0
  62. package/dist/huazhu/sellable.js.map +1 -0
  63. package/dist/huazhu/wire.d.ts +389 -0
  64. package/dist/huazhu/wire.d.ts.map +1 -0
  65. package/dist/huazhu/wire.js +72 -0
  66. package/dist/huazhu/wire.js.map +1 -0
  67. package/dist/index.js +18 -4
  68. package/dist/index.js.map +1 -1
  69. package/dist/legal.d.ts +4 -3
  70. package/dist/legal.d.ts.map +1 -1
  71. package/dist/legal.js +33 -18
  72. package/dist/legal.js.map +1 -1
  73. package/dist/lib.d.ts +8 -3
  74. package/dist/lib.d.ts.map +1 -1
  75. package/dist/lib.js +8 -1
  76. package/dist/lib.js.map +1 -1
  77. package/dist/server.d.ts +20 -8
  78. package/dist/server.d.ts.map +1 -1
  79. package/dist/server.js +132 -28
  80. package/dist/server.js.map +1 -1
  81. package/dist/surface.d.ts +2 -1
  82. package/dist/surface.d.ts.map +1 -1
  83. package/dist/surface.js +37 -10
  84. package/dist/surface.js.map +1 -1
  85. package/dist/tools.d.ts +33 -20
  86. package/dist/tools.d.ts.map +1 -1
  87. package/dist/tools.js +122 -11
  88. package/dist/tools.js.map +1 -1
  89. package/dist/types.d.ts +21 -2
  90. package/dist/types.d.ts.map +1 -1
  91. package/package.json +1 -1
@@ -0,0 +1,201 @@
1
+ import { addDays, arrivalInstant } from '../dates.js';
2
+ import { formatMoney, money, toMinor } from '../money.js';
3
+ const STEP = /^(?:(\d{1,4})(AM|PM))?(AD|\d{1,3}D)(.+)$/;
4
+ const PENALTY = /^(\d+(?:\.\d+)?)([PN]?)$/;
5
+ const readPenalty = (raw) => {
6
+ if (raw === 'F')
7
+ return { kind: 'forbidden' };
8
+ const m = PENALTY.exec(raw);
9
+ if (!m)
10
+ return null;
11
+ const value = Number(m[1]);
12
+ if (!Number.isFinite(value))
13
+ return null;
14
+ if (value === 0)
15
+ return { kind: 'none' };
16
+ if (m[2] === 'P')
17
+ return value > 100 ? null : { kind: 'percent', value };
18
+ if (m[2] === 'N')
19
+ return Number.isInteger(value) ? { kind: 'nights', value } : null;
20
+ return { kind: 'amount', value };
21
+ };
22
+ /** 6PM → 18:00, 1159PM → 23:59, 630PM → 18:30, 12AM → 00:00. */
23
+ const readTime = (digits, half) => {
24
+ if (!digits)
25
+ return '00:00';
26
+ let h;
27
+ let m = 0;
28
+ if (digits.length <= 2)
29
+ h = Number(digits);
30
+ else {
31
+ h = Number(digits.slice(0, -2));
32
+ m = Number(digits.slice(-2));
33
+ }
34
+ if (!(h >= 1 && h <= 12) || !(m >= 0 && m <= 59))
35
+ return null;
36
+ const h24 = (h % 12) + (half === 'PM' ? 12 : 0);
37
+ return `${String(h24).padStart(2, '0')}:${String(m).padStart(2, '0')}`;
38
+ };
39
+ export const parseCancelPolicy = (code) => {
40
+ const parts = (code ?? '').trim().toUpperCase().split('_').filter(Boolean);
41
+ if (parts.length < 2)
42
+ return null;
43
+ const last = parts.pop();
44
+ const noShow = readPenalty(last);
45
+ if (!noShow)
46
+ return null;
47
+ const steps = [];
48
+ for (const part of parts) {
49
+ const m = STEP.exec(part);
50
+ if (!m)
51
+ return null;
52
+ const at = readTime(m[1], m[2]);
53
+ const penalty = readPenalty(m[4] ?? '');
54
+ if (!at || !penalty)
55
+ return null;
56
+ steps.push({
57
+ daysBefore: m[3] === 'AD' ? 'any' : Number((m[3] ?? '').slice(0, -1)),
58
+ at,
59
+ penalty,
60
+ });
61
+ }
62
+ return { code: (code ?? '').trim(), steps, noShow };
63
+ };
64
+ /** The instant a step starts, on the hotel's clock. AD is minus infinity. */
65
+ const startOf = (s, checkIn, tz) => s.daysBefore === 'any'
66
+ ? Number.NEGATIVE_INFINITY
67
+ : arrivalInstant(addDays(checkIn, -s.daysBefore), s.at, tz);
68
+ /** Midnight at the end of the arrival day: "夜审时间约定为入住日当天24点". */
69
+ export const nightAuditOf = (checkIn, tz) => arrivalInstant(addDays(checkIn, 1), '00:00', tz);
70
+ /** The policy as consecutive windows of time, first to last, the no-show window included. */
71
+ export const phasesOf = (p, checkIn, tz) => {
72
+ const audit = nightAuditOf(checkIn, tz);
73
+ const steps = [...p.steps]
74
+ .map((s) => ({ start: Math.min(startOf(s, checkIn, tz), audit), penalty: s.penalty }))
75
+ .sort((a, b) => a.start - b.start);
76
+ const out = [];
77
+ const first = steps[0];
78
+ if (!first || first.start > Number.NEGATIVE_INFINITY) {
79
+ out.push({
80
+ from: Number.NEGATIVE_INFINITY,
81
+ until: first?.start ?? audit,
82
+ penalty: { kind: 'none' },
83
+ });
84
+ }
85
+ steps.forEach((s, i) => {
86
+ const until = steps[i + 1]?.start ?? audit;
87
+ if (until > s.start)
88
+ out.push({ from: s.start, until, penalty: s.penalty });
89
+ });
90
+ out.push({ from: audit, until: Number.POSITIVE_INFINITY, penalty: p.noShow });
91
+ return out;
92
+ };
93
+ export const penaltyAt = (p, checkIn, tz, t) => phasesOf(p, checkIn, tz).find((ph) => t >= ph.from && t < ph.until)?.penalty ?? p.noShow;
94
+ /**
95
+ * Until when cancelling is free, counted from `now` - or undefined when it is
96
+ * not free now. A window that has already closed is no window.
97
+ */
98
+ export const freeUntilOf = (p, checkIn, tz, now) => {
99
+ // Cancelling is something done BEFORE the stay: once the arrival day is over
100
+ // it is a no-show, whatever that costs, and "free until forever" is not a
101
+ // window anybody can use. AD0_0 is free until the end of the arrival day.
102
+ const audit = nightAuditOf(checkIn, tz);
103
+ const phases = phasesOf(p, checkIn, tz).filter((ph) => ph.from < audit);
104
+ const i = phases.findIndex((ph) => now >= ph.from && now < ph.until);
105
+ if (i < 0 || phases[i]?.penalty.kind !== 'none')
106
+ return undefined;
107
+ let end = phases[i]?.until ?? now;
108
+ // Consecutive free windows are one window.
109
+ for (let j = i + 1; j < phases.length && phases[j]?.penalty.kind === 'none'; j += 1)
110
+ end = phases[j]?.until ?? end;
111
+ return Math.min(end, audit);
112
+ };
113
+ /** True unless every window from now on keeps the whole order. */
114
+ export const refundableFrom = (p, checkIn, tz, now) => phasesOf(p, checkIn, tz)
115
+ .filter((ph) => ph.until > now && ph.from < nightAuditOf(checkIn, tz))
116
+ .some((ph) => ph.penalty.kind === 'none' ||
117
+ ph.penalty.kind === 'nights' ||
118
+ ph.penalty.kind === 'amount' ||
119
+ (ph.penalty.kind === 'percent' && ph.penalty.value < 100));
120
+ /**
121
+ * The fee a penalty comes to, in minor units - or null for `forbidden`.
122
+ *
123
+ * `1N` is the FIRST night ("收取首晚的罚金"), for every room booked; a fixed
124
+ * amount is for the order, as the code writes it; a percentage is of the whole
125
+ * order. Never more than the order.
126
+ */
127
+ export const feeFor = (penalty, o) => {
128
+ const total = o.nightlyMinor.reduce((a, b) => a + b, 0) * o.rooms;
129
+ switch (penalty.kind) {
130
+ case 'none':
131
+ return 0;
132
+ case 'forbidden':
133
+ return null;
134
+ case 'percent':
135
+ return Math.min(total, Math.round((total * penalty.value) / 100));
136
+ case 'nights':
137
+ return Math.min(total, o.nightlyMinor.slice(0, penalty.value).reduce((a, b) => a + b, 0) * o.rooms);
138
+ case 'amount':
139
+ return Math.min(total, toMinor(penalty.value, o.currency));
140
+ }
141
+ };
142
+ const localTime = (t, tz) => {
143
+ try {
144
+ return new Intl.DateTimeFormat('en-CA', {
145
+ timeZone: tz,
146
+ year: 'numeric',
147
+ month: '2-digit',
148
+ day: '2-digit',
149
+ hour: '2-digit',
150
+ minute: '2-digit',
151
+ hourCycle: 'h23',
152
+ })
153
+ .format(t)
154
+ .replace(',', '');
155
+ }
156
+ catch {
157
+ return new Date(t).toISOString().slice(0, 16).replace('T', ' ');
158
+ }
159
+ };
160
+ const penaltyWords = (penalty, o) => {
161
+ const fee = feeFor(penalty, o);
162
+ const amount = fee === null ? '' : ` (${formatMoney(money(fee, o.currency))})`;
163
+ switch (penalty.kind) {
164
+ case 'none':
165
+ return 'free';
166
+ case 'forbidden':
167
+ return 'not possible';
168
+ case 'percent':
169
+ return penalty.value >= 100
170
+ ? `the whole stay${amount}`
171
+ : `${penalty.value}% of the stay${amount}`;
172
+ case 'nights':
173
+ return `${penalty.value === 1 ? 'the first night' : `the first ${penalty.value} nights`}${amount}`;
174
+ case 'amount':
175
+ return `a fee of ${formatMoney(money(toMinor(penalty.value, o.currency), o.currency))}`;
176
+ }
177
+ };
178
+ /**
179
+ * The terms in words, with the hotel's own times: "Free to cancel until
180
+ * 2026-10-05 18:00; from then until the end of the arrival day, the first night
181
+ * (¥328); after that, the whole stay (¥656). Times are the hotel's."
182
+ */
183
+ export const describePolicy = (p, checkIn, tz, o) => {
184
+ const phases = phasesOf(p, checkIn, tz);
185
+ const audit = nightAuditOf(checkIn, tz);
186
+ const parts = phases.map((ph, i) => {
187
+ const what = penaltyWords(ph.penalty, o);
188
+ const cancel = ph.penalty.kind === 'forbidden' ? 'cancelling is not possible' : `cancelling costs ${what}`;
189
+ if (ph.from >= audit)
190
+ return `after the arrival day, ${ph.penalty.kind === 'none' ? 'a no-show costs nothing' : `a no-show costs ${what}`}`;
191
+ const until = ph.until >= audit ? 'the end of the arrival day' : localTime(ph.until, tz);
192
+ if (i === 0 && ph.from === Number.NEGATIVE_INFINITY) {
193
+ return ph.penalty.kind === 'none'
194
+ ? `Free to cancel until ${until}`
195
+ : `From booking until ${until}, ${cancel}`;
196
+ }
197
+ return `from ${localTime(ph.from, tz)} until ${until}, ${ph.penalty.kind === 'none' ? 'cancelling is free' : cancel}`;
198
+ });
199
+ return `${parts.join('; ')}. Times are the hotel's (rate terms ${p.code}).`;
200
+ };
201
+ //# sourceMappingURL=cancel-policy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cancel-policy.js","sourceRoot":"","sources":["../../src/huazhu/cancel-policy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AA2C1D,MAAM,IAAI,GAAG,0CAA0C,CAAC;AACxD,MAAM,OAAO,GAAG,0BAA0B,CAAC;AAE3C,MAAM,WAAW,GAAG,CAAC,GAAW,EAAkB,EAAE;IAClD,IAAI,GAAG,KAAK,GAAG;QAAE,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC;IAC9C,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC5B,IAAI,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IACpB,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3B,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACzC,IAAI,KAAK,KAAK,CAAC;QAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IACzC,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG;QAAE,OAAO,KAAK,GAAG,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;IACzE,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG;QAAE,OAAO,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;IACpF,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;AACnC,CAAC,CAAC;AAEF,gEAAgE;AAChE,MAAM,QAAQ,GAAG,CAAC,MAA0B,EAAE,IAAwB,EAAiB,EAAE;IACvF,IAAI,CAAC,MAAM;QAAE,OAAO,OAAO,CAAC;IAC5B,IAAI,CAAS,CAAC;IACd,IAAI,CAAC,GAAG,CAAC,CAAC;IACV,IAAI,MAAM,CAAC,MAAM,IAAI,CAAC;QAAE,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;SACtC,CAAC;QACJ,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QAChC,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC/B,CAAC;IACD,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;QAAE,OAAO,IAAI,CAAC;IAC9D,MAAM,GAAG,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAChD,OAAO,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC;AACzE,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,IAAwB,EAAuB,EAAE;IACjF,MAAM,KAAK,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAC3E,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IAClC,MAAM,IAAI,GAAG,KAAK,CAAC,GAAG,EAAY,CAAC;IACnC,MAAM,MAAM,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC;IACjC,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IACzB,MAAM,KAAK,GAAiB,EAAE,CAAC;IAC/B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC1B,IAAI,CAAC,CAAC;YAAE,OAAO,IAAI,CAAC;QACpB,MAAM,EAAE,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAChC,MAAM,OAAO,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;QACxC,IAAI,CAAC,EAAE,IAAI,CAAC,OAAO;YAAE,OAAO,IAAI,CAAC;QACjC,KAAK,CAAC,IAAI,CAAC;YACT,UAAU,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;YACrE,EAAE;YACF,OAAO;SACR,CAAC,CAAC;IACL,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;AACtD,CAAC,CAAC;AAEF,6EAA6E;AAC7E,MAAM,OAAO,GAAG,CAAC,CAAa,EAAE,OAAe,EAAE,EAAU,EAAU,EAAE,CACrE,CAAC,CAAC,UAAU,KAAK,KAAK;IACpB,CAAC,CAAC,MAAM,CAAC,iBAAiB;IAC1B,CAAC,CAAC,cAAc,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;AAEhE,iEAAiE;AACjE,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,OAAe,EAAE,EAAU,EAAU,EAAE,CAClE,cAAc,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,EAAE,CAAC,CAAC;AAInD,6FAA6F;AAC7F,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,CAAe,EAAE,OAAe,EAAE,EAAU,EAAW,EAAE;IAChF,MAAM,KAAK,GAAG,YAAY,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;IACxC,MAAM,KAAK,GAAG,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC;SACvB,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,EAAE,OAAO,EAAE,EAAE,CAAC,EAAE,KAAK,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;SACrF,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC;IACrC,MAAM,GAAG,GAAY,EAAE,CAAC;IACxB,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IACvB,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,KAAK,GAAG,MAAM,CAAC,iBAAiB,EAAE,CAAC;QACrD,GAAG,CAAC,IAAI,CAAC;YACP,IAAI,EAAE,MAAM,CAAC,iBAAiB;YAC9B,KAAK,EAAE,KAAK,EAAE,KAAK,IAAI,KAAK;YAC5B,OAAO,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE;SAC1B,CAAC,CAAC;IACL,CAAC;IACD,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QACrB,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,IAAI,KAAK,CAAC;QAC3C,IAAI,KAAK,GAAG,CAAC,CAAC,KAAK;YAAE,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;IAC9E,CAAC,CAAC,CAAC;IACH,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,iBAAiB,EAAE,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;IAC9E,OAAO,GAAG,CAAC;AACb,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,CAAe,EAAE,OAAe,EAAE,EAAU,EAAE,CAAS,EAAW,EAAE,CAC5F,QAAQ,CAAC,CAAC,EAAE,OAAO,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,EAAE,OAAO,IAAI,CAAC,CAAC,MAAM,CAAC;AAE3F;;;GAGG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,CACzB,CAAe,EACf,OAAe,EACf,EAAU,EACV,GAAW,EACS,EAAE;IACtB,6EAA6E;IAC7E,0EAA0E;IAC1E,0EAA0E;IAC1E,MAAM,KAAK,GAAG,YAAY,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;IACxC,MAAM,MAAM,GAAG,QAAQ,CAAC,CAAC,EAAE,OAAO,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,GAAG,KAAK,CAAC,CAAC;IACxE,MAAM,CAAC,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,GAAG,IAAI,EAAE,CAAC,IAAI,IAAI,GAAG,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC;IACrE,IAAI,CAAC,GAAG,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,IAAI,KAAK,MAAM;QAAE,OAAO,SAAS,CAAC;IAClE,IAAI,GAAG,GAAG,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,IAAI,GAAG,CAAC;IAClC,2CAA2C;IAC3C,KAAK,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC,IAAI,CAAC;QACjF,GAAG,GAAG,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,IAAI,GAAG,CAAC;IAChC,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;AAC9B,CAAC,CAAC;AAEF,kEAAkE;AAClE,MAAM,CAAC,MAAM,cAAc,GAAG,CAC5B,CAAe,EACf,OAAe,EACf,EAAU,EACV,GAAW,EACF,EAAE,CACX,QAAQ,CAAC,CAAC,EAAE,OAAO,EAAE,EAAE,CAAC;KACrB,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,KAAK,GAAG,GAAG,IAAI,EAAE,CAAC,IAAI,GAAG,YAAY,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;KACrE,IAAI,CACH,CAAC,EAAE,EAAE,EAAE,CACL,EAAE,CAAC,OAAO,CAAC,IAAI,KAAK,MAAM;IAC1B,EAAE,CAAC,OAAO,CAAC,IAAI,KAAK,QAAQ;IAC5B,EAAE,CAAC,OAAO,CAAC,IAAI,KAAK,QAAQ;IAC5B,CAAC,EAAE,CAAC,OAAO,CAAC,IAAI,KAAK,SAAS,IAAI,EAAE,CAAC,OAAO,CAAC,KAAK,GAAG,GAAG,CAAC,CAC5D,CAAC;AAUN;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,OAAgB,EAAE,CAAe,EAAiB,EAAE;IACzE,MAAM,KAAK,GAAG,CAAC,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC;IAClE,QAAQ,OAAO,CAAC,IAAI,EAAE,CAAC;QACrB,KAAK,MAAM;YACT,OAAO,CAAC,CAAC;QACX,KAAK,WAAW;YACd,OAAO,IAAI,CAAC;QACd,KAAK,SAAS;YACZ,OAAO,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC;QACpE,KAAK,QAAQ;YACX,OAAO,IAAI,CAAC,GAAG,CACb,KAAK,EACL,CAAC,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,CAC5E,CAAC;QACJ,KAAK,QAAQ;YACX,OAAO,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC;IAC/D,CAAC;AACH,CAAC,CAAC;AAEF,MAAM,SAAS,GAAG,CAAC,CAAS,EAAE,EAAU,EAAU,EAAE;IAClD,IAAI,CAAC;QACH,OAAO,IAAI,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE;YACtC,QAAQ,EAAE,EAAE;YACZ,IAAI,EAAE,SAAS;YACf,KAAK,EAAE,SAAS;YAChB,GAAG,EAAE,SAAS;YACd,IAAI,EAAE,SAAS;YACf,MAAM,EAAE,SAAS;YACjB,SAAS,EAAE,KAAK;SACjB,CAAC;aACC,MAAM,CAAC,CAAC,CAAC;aACT,OAAO,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;IACtB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;IAClE,CAAC;AACH,CAAC,CAAC;AAEF,MAAM,YAAY,GAAG,CAAC,OAAgB,EAAE,CAAe,EAAU,EAAE;IACjE,MAAM,GAAG,GAAG,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;IAC/B,MAAM,MAAM,GAAG,GAAG,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,WAAW,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC;IAC/E,QAAQ,OAAO,CAAC,IAAI,EAAE,CAAC;QACrB,KAAK,MAAM;YACT,OAAO,MAAM,CAAC;QAChB,KAAK,WAAW;YACd,OAAO,cAAc,CAAC;QACxB,KAAK,SAAS;YACZ,OAAO,OAAO,CAAC,KAAK,IAAI,GAAG;gBACzB,CAAC,CAAC,iBAAiB,MAAM,EAAE;gBAC3B,CAAC,CAAC,GAAG,OAAO,CAAC,KAAK,gBAAgB,MAAM,EAAE,CAAC;QAC/C,KAAK,QAAQ;YACX,OAAO,GAAG,OAAO,CAAC,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,aAAa,OAAO,CAAC,KAAK,SAAS,GAAG,MAAM,EAAE,CAAC;QACrG,KAAK,QAAQ;YACX,OAAO,YAAY,WAAW,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC;IAC5F,CAAC;AACH,CAAC,CAAC;AAEF;;;;GAIG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,CAC5B,CAAe,EACf,OAAe,EACf,EAAU,EACV,CAAe,EACP,EAAE;IACV,MAAM,MAAM,GAAG,QAAQ,CAAC,CAAC,EAAE,OAAO,EAAE,EAAE,CAAC,CAAC;IACxC,MAAM,KAAK,GAAG,YAAY,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;IACxC,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,EAAE;QACjC,MAAM,IAAI,GAAG,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QACzC,MAAM,MAAM,GACV,EAAE,CAAC,OAAO,CAAC,IAAI,KAAK,WAAW,CAAC,CAAC,CAAC,4BAA4B,CAAC,CAAC,CAAC,oBAAoB,IAAI,EAAE,CAAC;QAC9F,IAAI,EAAE,CAAC,IAAI,IAAI,KAAK;YAClB,OAAO,0BAA0B,EAAE,CAAC,OAAO,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,yBAAyB,CAAC,CAAC,CAAC,mBAAmB,IAAI,EAAE,EAAE,CAAC;QACxH,MAAM,KAAK,GAAG,EAAE,CAAC,KAAK,IAAI,KAAK,CAAC,CAAC,CAAC,4BAA4B,CAAC,CAAC,CAAC,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QACzF,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,IAAI,KAAK,MAAM,CAAC,iBAAiB,EAAE,CAAC;YACpD,OAAO,EAAE,CAAC,OAAO,CAAC,IAAI,KAAK,MAAM;gBAC/B,CAAC,CAAC,wBAAwB,KAAK,EAAE;gBACjC,CAAC,CAAC,sBAAsB,KAAK,KAAK,MAAM,EAAE,CAAC;QAC/C,CAAC;QACD,OAAO,QAAQ,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,UAAU,KAAK,KAAK,EAAE,CAAC,OAAO,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,oBAAoB,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;IACxH,CAAC,CAAC,CAAC;IACH,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,uCAAuC,CAAC,CAAC,IAAI,IAAI,CAAC;AAC9E,CAAC,CAAC"}
@@ -0,0 +1,75 @@
1
+ import type { Hotel } from '../types.js';
2
+ import { type HuazhuClient } from './client.js';
3
+ import { type HotelStatic } from './wire.js';
4
+ export type CatalogOptions = {
5
+ client: HuazhuClient;
6
+ /** Where the copy lives. The directory must be writable. */
7
+ stateDir: string;
8
+ /** Only these hotel ids, when the deployment sells a subset. */
9
+ only?: string[];
10
+ /** How old a hotel's record may be before it is fetched again. */
11
+ maxAgeMs?: number;
12
+ now?: () => number;
13
+ log?: (line: string) => void;
14
+ };
15
+ export type CatalogState = {
16
+ hotels: number;
17
+ listedAt?: string;
18
+ completeAt?: string;
19
+ syncing: boolean;
20
+ /** Why the last sync stopped short, when it did. */
21
+ stoppedBecause?: string;
22
+ };
23
+ export declare class Catalog {
24
+ private readonly opts;
25
+ private file;
26
+ private syncing?;
27
+ private stoppedBecause?;
28
+ private readonly path;
29
+ constructor(opts: CatalogOptions);
30
+ private now;
31
+ private log;
32
+ /** Every hotel, in the order Huazhu lists them. */
33
+ all(): HotelStatic[];
34
+ get(id: string): HotelStatic | undefined;
35
+ get size(): number;
36
+ state(): CatalogState;
37
+ /** True when the list is older than a day, or the last sync did not reach its end. */
38
+ stale(): boolean;
39
+ /** One sync at a time; a second caller waits on the first. */
40
+ sync(): Promise<void>;
41
+ private save;
42
+ private run;
43
+ }
44
+ export type CatalogView = {
45
+ language: 'zh' | 'en';
46
+ tz: string;
47
+ };
48
+ /** "上海市" → "上海". A city is written without its administrative suffix on every booking site. */
49
+ export declare const cityOf: (h: HotelStatic) => string;
50
+ /**
51
+ * The country. Huazhu's record has no such field: `international: false` is
52
+ * China, and for an international hotel the province is the nearest thing the
53
+ * record has - to be checked against the first real international record.
54
+ */
55
+ export declare const countryOf: (h: HotelStatic) => string;
56
+ /**
57
+ * Photographs are served over http in the documentation's example. A card is
58
+ * an https page and a browser blocks an http image on it without a word, so
59
+ * the address is asked for over https.
60
+ */
61
+ export declare const secure: (url: string | undefined) => string | undefined;
62
+ /** The picture a tile leads with: a featured or exterior photograph, first by sort order. */
63
+ export declare const leadPhoto: (h: HotelStatic) => string | undefined;
64
+ /** A room type's own photograph, where the hotel published one. */
65
+ export declare const roomPhoto: (h: HotelStatic, roomTypeCode: string) => string | undefined;
66
+ /** A hotel as the connector shows it, before any dates. */
67
+ export declare const toHotel: (h: HotelStatic, v: CatalogView, now: number) => Hotel;
68
+ /** Everything a guest's words might be matched against, lower-cased once. */
69
+ export declare const haystackOf: (h: HotelStatic) => string;
70
+ /**
71
+ * Is this hotel in the place asked for - a city, a province, a district or a
72
+ * country, in Chinese or as the English address spells it ("Shanghai").
73
+ */
74
+ export declare const inPlace: (h: HotelStatic, place: string) => boolean;
75
+ //# sourceMappingURL=catalog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"catalog.d.ts","sourceRoot":"","sources":["../../src/huazhu/catalog.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,KAAK,YAAY,EAAiB,MAAM,aAAa,CAAC;AAC/D,OAAO,EAA4C,KAAK,WAAW,EAAY,MAAM,WAAW,CAAC;AAkBjG,MAAM,MAAM,cAAc,GAAG;IAC3B,MAAM,EAAE,YAAY,CAAC;IACrB,4DAA4D;IAC5D,QAAQ,EAAE,MAAM,CAAC;IACjB,gEAAgE;IAChE,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,kEAAkE;IAClE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9B,CAAC;AAeF,MAAM,MAAM,YAAY,GAAG;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,OAAO,CAAC;IACjB,oDAAoD;IACpD,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAIF,qBAAa,OAAO;IAMN,OAAO,CAAC,QAAQ,CAAC,IAAI;IALjC,OAAO,CAAC,IAAI,CAAsD;IAClE,OAAO,CAAC,OAAO,CAAC,CAAgB;IAChC,OAAO,CAAC,cAAc,CAAC,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAS;gBAED,IAAI,EAAE,cAAc;IAUjD,OAAO,CAAC,GAAG;IAIX,OAAO,CAAC,GAAG;IAIX,mDAAmD;IACnD,GAAG,IAAI,WAAW,EAAE;IAMpB,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS;IAIxC,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,KAAK,IAAI,YAAY;IAUrB,sFAAsF;IACtF,KAAK,IAAI,OAAO;IAUhB,8DAA8D;IAC9D,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IASrB,OAAO,CAAC,IAAI;YAOE,GAAG;CAiElB;AAID,MAAM,MAAM,WAAW,GAAG;IACxB,QAAQ,EAAE,IAAI,GAAG,IAAI,CAAC;IACtB,EAAE,EAAE,MAAM,CAAC;CACZ,CAAC;AAEF,+FAA+F;AAC/F,eAAO,MAAM,MAAM,GAAI,GAAG,WAAW,KAAG,MAGvC,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,SAAS,GAAI,GAAG,WAAW,KAAG,MACmC,CAAC;AAE/E;;;;GAIG;AACH,eAAO,MAAM,MAAM,GAAI,KAAK,MAAM,GAAG,SAAS,KAAG,MAAM,GAAG,SAMzD,CAAC;AAKF,6FAA6F;AAC7F,eAAO,MAAM,SAAS,GAAI,GAAG,WAAW,KAAG,MAAM,GAAG,SAQnD,CAAC;AAEF,mEAAmE;AACnE,eAAO,MAAM,SAAS,GAAI,GAAG,WAAW,EAAE,cAAc,MAAM,KAAG,MAAM,GAAG,SAOvE,CAAC;AAIJ,2DAA2D;AAC3D,eAAO,MAAM,OAAO,GAAI,GAAG,WAAW,EAAE,GAAG,WAAW,EAAE,KAAK,MAAM,KAAG,KA0BrE,CAAC;AAEF,6EAA6E;AAC7E,eAAO,MAAM,UAAU,GAAI,GAAG,WAAW,KAAG,MAiB1B,CAAC;AAQnB;;;GAGG;AACH,eAAO,MAAM,OAAO,GAAI,GAAG,WAAW,EAAE,OAAO,MAAM,KAAG,OASvD,CAAC"}
@@ -0,0 +1,251 @@
1
+ import { mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
2
+ import { dirname, join } from 'node:path';
3
+ import { todayAt } from '../dates.js';
4
+ import { DESCRIPTION_MAX, cleanHotelText } from '../fence.js';
5
+ import { HuazhuRefusal } from './client.js';
6
+ import { DAILY_LIMIT, HOTEL_GONE, NO_MATCH } from './wire.js';
7
+ const DAY = 86_400_000;
8
+ export class Catalog {
9
+ opts;
10
+ file = { version: 1, hotels: {}, order: [] };
11
+ syncing;
12
+ stoppedBecause;
13
+ path;
14
+ constructor(opts) {
15
+ this.opts = opts;
16
+ this.path = join(opts.stateDir, 'catalog.json');
17
+ try {
18
+ const raw = JSON.parse(readFileSync(this.path, 'utf8'));
19
+ if (raw?.version === 1 && raw.hotels && Array.isArray(raw.order))
20
+ this.file = raw;
21
+ }
22
+ catch {
23
+ // No copy yet: the first sync makes one.
24
+ }
25
+ }
26
+ now() {
27
+ return this.opts.now?.() ?? Date.now();
28
+ }
29
+ log(line) {
30
+ (this.opts.log ?? console.log)(`[huazhu catalog] ${line}`);
31
+ }
32
+ /** Every hotel, in the order Huazhu lists them. */
33
+ all() {
34
+ return this.file.order
35
+ .map((id) => this.file.hotels[id]?.data)
36
+ .filter((h) => !!h);
37
+ }
38
+ get(id) {
39
+ return this.file.hotels[id]?.data;
40
+ }
41
+ get size() {
42
+ return this.file.order.filter((id) => this.file.hotels[id]).length;
43
+ }
44
+ state() {
45
+ return {
46
+ hotels: this.size,
47
+ ...(this.file.listedAt ? { listedAt: new Date(this.file.listedAt).toISOString() } : {}),
48
+ ...(this.file.completeAt ? { completeAt: new Date(this.file.completeAt).toISOString() } : {}),
49
+ syncing: !!this.syncing,
50
+ ...(this.stoppedBecause ? { stoppedBecause: this.stoppedBecause } : {}),
51
+ };
52
+ }
53
+ /** True when the list is older than a day, or the last sync did not reach its end. */
54
+ stale() {
55
+ const age = this.opts.maxAgeMs ?? DAY;
56
+ return (!this.file.listedAt ||
57
+ this.now() - this.file.listedAt > age ||
58
+ !this.file.completeAt ||
59
+ this.file.completeAt < this.file.listedAt);
60
+ }
61
+ /** One sync at a time; a second caller waits on the first. */
62
+ sync() {
63
+ if (!this.syncing) {
64
+ this.syncing = this.run().finally(() => {
65
+ this.syncing = undefined;
66
+ });
67
+ }
68
+ return this.syncing;
69
+ }
70
+ save() {
71
+ mkdirSync(dirname(this.path), { recursive: true });
72
+ const tmp = `${this.path}.${process.pid}.tmp`;
73
+ writeFileSync(tmp, JSON.stringify(this.file));
74
+ renameSync(tmp, this.path);
75
+ }
76
+ async run() {
77
+ const { client } = this.opts;
78
+ const maxAge = this.opts.maxAgeMs ?? DAY;
79
+ this.stoppedBecause = undefined;
80
+ let listed;
81
+ try {
82
+ listed = await client.hotels();
83
+ }
84
+ catch (err) {
85
+ if (err instanceof HuazhuRefusal && NO_MATCH.has(err.code))
86
+ listed = [];
87
+ else {
88
+ this.stoppedBecause = `the hotel list could not be read: ${err.message}`;
89
+ this.log(this.stoppedBecause);
90
+ return;
91
+ }
92
+ }
93
+ const wanted = this.opts.only?.length ? new Set(this.opts.only.map(String)) : undefined;
94
+ const ids = [...new Set(listed.map((h) => String(h.hotelId)))].filter((id) => !wanted || wanted.has(id));
95
+ // ⚠️ OFF THE LIST IS OFF THE CONNECTOR, now - not after its record ages out.
96
+ const keep = new Set(ids);
97
+ const gone = Object.keys(this.file.hotels).filter((id) => !keep.has(id));
98
+ for (const id of gone)
99
+ delete this.file.hotels[id];
100
+ this.file.order = ids;
101
+ this.file.listedAt = this.now();
102
+ if (gone.length)
103
+ this.log(`${gone.length} hotel(s) left the list and were removed`);
104
+ this.save();
105
+ let fetched = 0;
106
+ let failures = 0;
107
+ for (const id of ids) {
108
+ const had = this.file.hotels[id];
109
+ if (had && this.now() - had.fetchedAt < maxAge)
110
+ continue;
111
+ try {
112
+ const data = await client.hotel(id);
113
+ this.file.hotels[id] = { fetchedAt: this.now(), data: { ...data, hotelId: id } };
114
+ fetched += 1;
115
+ failures = 0;
116
+ }
117
+ catch (err) {
118
+ if (err instanceof HuazhuRefusal && DAILY_LIMIT.has(err.code)) {
119
+ this.stoppedBecause = `the daily limit on /hotel was reached after ${fetched} hotel(s); the rest follow at the next sync`;
120
+ this.log(this.stoppedBecause);
121
+ this.save();
122
+ return;
123
+ }
124
+ if (err instanceof HuazhuRefusal && HOTEL_GONE.has(err.code)) {
125
+ // Listed but closed or unknown: not sellable, so not shown.
126
+ delete this.file.hotels[id];
127
+ continue;
128
+ }
129
+ failures += 1;
130
+ this.log(`${id}: ${err.message}${had ? ' (keeping the copy from before)' : ''}`);
131
+ if (failures >= 20) {
132
+ this.stoppedBecause = `20 hotels in a row could not be read; last: ${err.message}`;
133
+ this.log(this.stoppedBecause);
134
+ this.save();
135
+ return;
136
+ }
137
+ }
138
+ if (fetched % 50 === 0)
139
+ this.save();
140
+ }
141
+ this.file.completeAt = this.now();
142
+ this.save();
143
+ this.log(`${this.size} hotel(s); ${fetched} fetched this run`);
144
+ }
145
+ }
146
+ /** "上海市" → "上海". A city is written without its administrative suffix on every booking site. */
147
+ export const cityOf = (h) => {
148
+ const c = (h.cityName ?? '').trim();
149
+ return c.length > 2 ? c.replace(/市$/, '') : c;
150
+ };
151
+ /**
152
+ * The country. Huazhu's record has no such field: `international: false` is
153
+ * China, and for an international hotel the province is the nearest thing the
154
+ * record has - to be checked against the first real international record.
155
+ */
156
+ export const countryOf = (h) => h.international ? (h.provinceName ?? '').trim() || 'International' : 'China';
157
+ /**
158
+ * Photographs are served over http in the documentation's example. A card is
159
+ * an https page and a browser blocks an http image on it without a word, so
160
+ * the address is asked for over https.
161
+ */
162
+ export const secure = (url) => {
163
+ if (!url)
164
+ return undefined;
165
+ const u = url.trim();
166
+ if (u.startsWith('//'))
167
+ return `https:${u}`;
168
+ if (u.startsWith('http://'))
169
+ return `https://${u.slice(7)}`;
170
+ return u.startsWith('https://') ? u : undefined;
171
+ };
172
+ const usable = (p) => p.status !== 0 && !!secure(p.fileUrl);
173
+ const LEAD_TYPES = ['FeaturedImage', 'ImagePhoto'];
174
+ /** The picture a tile leads with: a featured or exterior photograph, first by sort order. */
175
+ export const leadPhoto = (h) => {
176
+ const photos = (h.hotelPhotoList ?? []).filter(usable);
177
+ const bySort = (a, b) => (a.sortId ?? 1e9) - (b.sortId ?? 1e9);
178
+ const lead = photos.filter((p) => LEAD_TYPES.includes(String(p.typeCode))).sort(bySort)[0] ??
179
+ photos.filter((p) => String(p.typeCode) !== 'RoomType').sort(bySort)[0] ??
180
+ photos.sort(bySort)[0];
181
+ return secure(lead?.fileUrl);
182
+ };
183
+ /** A room type's own photograph, where the hotel published one. */
184
+ export const roomPhoto = (h, roomTypeCode) => secure((h.hotelPhotoList ?? [])
185
+ .filter((p) => usable(p) && String(p.typeCode) === 'RoomType' && p.typeMappingCode === roomTypeCode)
186
+ .sort((a, b) => (a.sortId ?? 1e9) - (b.sortId ?? 1e9))[0]?.fileUrl);
187
+ const clean = (s, max = 500) => cleanHotelText(s, max).text;
188
+ /** A hotel as the connector shows it, before any dates. */
189
+ export const toHotel = (h, v, now) => {
190
+ const en = v.language === 'en';
191
+ const name = clean(en ? h.hotelNameEn || h.hotelName : h.hotelName, 200);
192
+ const intro = clean(h.hotelWordIntroduce, DESCRIPTION_MAX);
193
+ return {
194
+ id: h.hotelId,
195
+ name,
196
+ currency: (h.tradeCurrency || 'CNY').toUpperCase(),
197
+ brand: clean(h.brandName, 100) || 'Huazhu',
198
+ city: cityOf(h),
199
+ cityId: h.cityNo || cityOf(h),
200
+ country: countryOf(h),
201
+ ...(h.countyName ? { area: clean(h.countyName, 100) } : {}),
202
+ summary: intro || [clean(h.brandName, 100), clean(h.countyName, 100)].filter(Boolean).join(' · '),
203
+ amenities: [
204
+ ...new Set((h.hotelServiceList ?? []).map((s) => clean(s.childClassName, 60)).filter(Boolean)),
205
+ ],
206
+ ...((en ? h.hotelAddressEn || h.hotelAddress : h.hotelAddress)
207
+ ? { address: clean(en ? h.hotelAddressEn || h.hotelAddress : h.hotelAddress, 300) }
208
+ : {}),
209
+ ...(leadPhoto(h) ? { imageUrl: leadPhoto(h) } : {}),
210
+ todayThere: todayAt(now, v.tz),
211
+ };
212
+ };
213
+ /** Everything a guest's words might be matched against, lower-cased once. */
214
+ export const haystackOf = (h) => [
215
+ h.hotelName,
216
+ h.hotelNameEn,
217
+ h.brandName,
218
+ h.provinceName,
219
+ h.cityName,
220
+ h.countyName,
221
+ h.hotelAddress,
222
+ h.hotelAddressEn,
223
+ h.hotelAddressTip,
224
+ ...(h.hotelTrafficList ?? []).map((t) => t.destinationName),
225
+ ...(h.hotelServiceList ?? []).map((s) => s.childClassName),
226
+ countryOf(h),
227
+ ]
228
+ .filter(Boolean)
229
+ .join(' ')
230
+ .toLowerCase();
231
+ const bare = (s) => (s ?? '')
232
+ .trim()
233
+ .toLowerCase()
234
+ .replace(/(维吾尔自治区|壮族自治区|回族自治区|自治区|特别行政区|省|市|区|县)$/, '');
235
+ /**
236
+ * Is this hotel in the place asked for - a city, a province, a district or a
237
+ * country, in Chinese or as the English address spells it ("Shanghai").
238
+ */
239
+ export const inPlace = (h, place) => {
240
+ const p = place.trim().toLowerCase();
241
+ if (!p)
242
+ return true;
243
+ const b = bare(p);
244
+ const names = [h.cityName, h.provinceName, h.countyName, countryOf(h)].map(bare);
245
+ if (names.includes(b) || h.cityNo === place.trim())
246
+ return true;
247
+ // The English address ends in the city: "…, Huangpu District, Shanghai".
248
+ const en = (h.hotelAddressEn ?? '').toLowerCase();
249
+ return !!en && (en.endsWith(`, ${p}`) || en.includes(`, ${p},`) || en.includes(`, ${p} city`));
250
+ };
251
+ //# sourceMappingURL=catalog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"catalog.js","sourceRoot":"","sources":["../../src/huazhu/catalog.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ import type { ApiSource } from '../config.js';
2
+ type Line = (s: string) => void;
3
+ export declare const checkHuazhu: (api: ApiSource, env: Record<string, string | undefined>, out?: Line, fetchImpl?: typeof fetch) => Promise<boolean>;
4
+ export {};
5
+ //# sourceMappingURL=check.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check.d.ts","sourceRoot":"","sources":["../../src/huazhu/check.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAsB9C,KAAK,IAAI,GAAG,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;AAEhC,eAAO,MAAM,WAAW,GACtB,KAAK,SAAS,EACd,KAAK,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,EACvC,MAAK,IAA4B,EACjC,YAAY,OAAO,KAAK,KACvB,OAAO,CAAC,OAAO,CAyMjB,CAAC"}