letsfg 2026.5.67 → 2026.5.69

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/README.md CHANGED
@@ -86,6 +86,19 @@ and `idempotencyKey`).
86
86
  - `offerSummary(offer)` — One-line string summary
87
87
  - `cheapestOffer(result)` — Get cheapest offer from search
88
88
 
89
+ ## Starlink Wi-Fi
90
+
91
+ Offers may carry `starlink`: `confirmed_all` / `confirmed_some` mean the carrier
92
+ has **fully** fitted that aircraft type; `likely_all` / `likely_some` mean the
93
+ rollout on that type is underway but incomplete. Segments carry `confirmed` or
94
+ `likely`.
95
+
96
+ Only `confirmed_*` is safe to state as fact — `likely_*` is a signal, not a
97
+ promise. Anything ending `_some` has at least one leg without it. An **absent**
98
+ field means no information, **not** an absence of Wi-Fi.
99
+
100
+ Full semantics: [docs/api-search.md](https://github.com/LetsFG/LetsFG/blob/main/docs/api-search.md#starlink-wi-fi).
101
+
89
102
  ## Zero Dependencies
90
103
 
91
104
  Uses native `fetch` (Node 18+). No `axios`, no `node-fetch`, nothing. Safe for sandboxed environments.
@@ -138,14 +151,14 @@ print(booking["confirmation"], booking["pay_link"])
138
151
 
139
152
  ### How you pay
140
153
 
141
- **10% now, the rest to the hotel later.** At booking we charge 10% of the price
154
+ **5% now, the rest to the hotel later.** At booking we charge 5% of the price
142
155
  to your card as a reservation fee. The remaining balance is paid **directly to
143
156
  the supplier** through a `pay_link` we return — we never hold it.
144
157
 
145
158
  `balance_due_by` is the supplier's own auto-cancellation date, not a date we
146
159
  invent. Miss it and the room is released.
147
160
 
148
- The 10% is **non-refundable**. Cancelling before `balance_due_by` costs nothing
161
+ The 5% is **non-refundable**. Cancelling before `balance_due_by` costs nothing
149
162
  else; after it, the hotel's own cancellation ladder applies and can reach 100%.
150
163
  That ladder ships in the booking's `terms`, so you can always see the cost before
151
164
  you cancel.
@@ -171,7 +184,7 @@ is not metered, only the search call itself.
171
184
  hotel signup.
172
185
  - **Only free-cancellation, pay-later rates are sold.** Those are the rates where
173
186
  the balance can safely be settled with the supplier after booking, which is
174
- what makes 10%-now/rest-later work at all. You will see fewer results than a
187
+ what makes 5%-now/rest-later work at all. You will see fewer results than a
175
188
  metasearch shows you. Every one of them can actually be booked.
176
189
  - **Booking is asynchronous.** `book_hotel` returns a `booking_job_id`, not a
177
190
  booking — the real thing takes minutes. Poll `hotel_booking(job_id)` until
@@ -87,6 +87,23 @@ function collectFlightNumbers(offer) {
87
87
  offer.flight_number
88
88
  ]);
89
89
  }
90
+ function buildStarlinkNote(offer) {
91
+ const segments = collectOfferSegments(offer);
92
+ const label = (s) => s.origin && s.destination ? `${s.origin}\u2192${s.destination}` : (s.flight_number || "").trim();
93
+ const confirmed = segments.filter((s) => s.starlink === "confirmed");
94
+ const likely = segments.filter((s) => s.starlink === "likely");
95
+ if (confirmed.length === 0 && likely.length === 0) return void 0;
96
+ const parts = [];
97
+ if (confirmed.length > 0) {
98
+ const where = confirmed.length === segments.length ? "every leg" : confirmed.map(label).filter(Boolean).join(", ");
99
+ parts.push(`Starlink Wi-Fi on ${where}`);
100
+ }
101
+ if (likely.length > 0) {
102
+ const where = likely.map(label).filter(Boolean).join(", ");
103
+ parts.push(`Starlink being installed on this aircraft type${where ? ` (${where})` : ""}, not guaranteed`);
104
+ }
105
+ return parts.join("; ");
106
+ }
90
107
  function collectAircraftTypes(offer) {
91
108
  return collectUniqueDetailValues(
92
109
  collectOfferSegments(offer).map((segment) => segment.aircraft?.replace(/\s*\([^)]*\)/g, "").trim())
@@ -428,6 +445,10 @@ function getOfferDetailPromptNotes(offer) {
428
445
  if (aircraftTypes.length > 0) {
429
446
  pushUniqueNote(notes, `Aircraft shown: ${aircraftTypes.join(", ")}`);
430
447
  }
448
+ const starlinkNote = buildStarlinkNote(offer);
449
+ if (starlinkNote) {
450
+ pushUniqueNote(notes, starlinkNote);
451
+ }
431
452
  const operatingCarriers = collectOperatingCarriers(offer);
432
453
  const normalizedOfferAirline = typeof offer.airline === "string" ? offer.airline.trim().toLowerCase() : "";
433
454
  if (operatingCarriers.length === 1 && operatingCarriers[0].toLowerCase() !== normalizedOfferAirline) {
@@ -1688,7 +1709,7 @@ var LetsFG = class {
1688
1709
  //
1689
1710
  // Only free-cancellation, pay-later rates are sold. Those are the rates where
1690
1711
  // the guest's balance can safely be settled with the supplier after booking,
1691
- // which is what makes 10%-now/rest-later work. The result set is smaller than
1712
+ // which is what makes 5%-now/rest-later work. The result set is smaller than
1692
1713
  // a metasearch's, and every row in it can actually be booked.
1693
1714
  /**
1694
1715
  * Resolve a place name to the city id that searchHotels() needs.
@@ -1711,7 +1732,7 @@ var LetsFG = class {
1711
1732
  * — so this gets its own generous timeout rather than the client default.
1712
1733
  *
1713
1734
  * Each offer carries `price` (what the guest pays), `reservation_fee_now`
1714
- * (the 10% taken at booking), `balance_to_supplier`, `balance_due_by` and
1735
+ * (the 5% taken at booking), `balance_to_supplier`, `balance_due_by` and
1715
1736
  * `free_cancellation_until`. There is no wholesale figure to quote by mistake.
1716
1737
  *
1717
1738
  * Keep `session_id` and the chosen offer's `combination_id_v2`: together they
@@ -1737,7 +1758,7 @@ var LetsFG = class {
1737
1758
  * Start a booking. Returns a job immediately — it does NOT book inline.
1738
1759
  *
1739
1760
  * A booking takes minutes: the rate is re-blocked at the supplier, every
1740
- * price and date rail is checked, the 10% reservation fee is charged to your
1761
+ * price and date rail is checked, the 5% reservation fee is charged to your
1741
1762
  * card, and only then is the room committed. No proxy holds a connection that
1742
1763
  * long, so this returns at once and you poll hotelBooking() for the outcome.
1743
1764
  * Use bookHotelAndWait() if you would rather block.
@@ -1819,7 +1840,7 @@ var LetsFG = class {
1819
1840
  *
1820
1841
  * Free until `balance_due_by`; after that the hotel's own cancellation ladder
1821
1842
  * applies and can reach 100%. The ladder ships in the booking's `terms`, so
1822
- * you can see the cost before calling this. The 10% reservation fee is NOT
1843
+ * you can see the cost before calling this. The 5% reservation fee is NOT
1823
1844
  * refunded.
1824
1845
  *
1825
1846
  * This drives a browser at the supplier and takes over a minute. If it times
package/dist/cli.js CHANGED
@@ -605,7 +605,7 @@ var LetsFG = class {
605
605
  //
606
606
  // Only free-cancellation, pay-later rates are sold. Those are the rates where
607
607
  // the guest's balance can safely be settled with the supplier after booking,
608
- // which is what makes 10%-now/rest-later work. The result set is smaller than
608
+ // which is what makes 5%-now/rest-later work. The result set is smaller than
609
609
  // a metasearch's, and every row in it can actually be booked.
610
610
  /**
611
611
  * Resolve a place name to the city id that searchHotels() needs.
@@ -628,7 +628,7 @@ var LetsFG = class {
628
628
  * — so this gets its own generous timeout rather than the client default.
629
629
  *
630
630
  * Each offer carries `price` (what the guest pays), `reservation_fee_now`
631
- * (the 10% taken at booking), `balance_to_supplier`, `balance_due_by` and
631
+ * (the 5% taken at booking), `balance_to_supplier`, `balance_due_by` and
632
632
  * `free_cancellation_until`. There is no wholesale figure to quote by mistake.
633
633
  *
634
634
  * Keep `session_id` and the chosen offer's `combination_id_v2`: together they
@@ -654,7 +654,7 @@ var LetsFG = class {
654
654
  * Start a booking. Returns a job immediately — it does NOT book inline.
655
655
  *
656
656
  * A booking takes minutes: the rate is re-blocked at the supplier, every
657
- * price and date rail is checked, the 10% reservation fee is charged to your
657
+ * price and date rail is checked, the 5% reservation fee is charged to your
658
658
  * card, and only then is the room committed. No proxy holds a connection that
659
659
  * long, so this returns at once and you poll hotelBooking() for the outcome.
660
660
  * Use bookHotelAndWait() if you would rather block.
@@ -736,7 +736,7 @@ var LetsFG = class {
736
736
  *
737
737
  * Free until `balance_due_by`; after that the hotel's own cancellation ladder
738
738
  * applies and can reach 100%. The ladder ships in the booking's `terms`, so
739
- * you can see the cost before calling this. The 10% reservation fee is NOT
739
+ * you can see the cost before calling this. The 5% reservation fee is NOT
740
740
  * refunded.
741
741
  *
742
742
  * This drives a browser at the supplier and takes over a minute. If it times
package/dist/cli.mjs CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  LetsFG,
4
4
  LetsFGError,
5
5
  offerSummary
6
- } from "./chunk-2SNO3AGS.mjs";
6
+ } from "./chunk-PW775XDB.mjs";
7
7
  import {
8
8
  BearerTokenError,
9
9
  getBearerToken,
package/dist/index.d.mts CHANGED
@@ -212,6 +212,10 @@ interface OfferDetailSegmentLike {
212
212
  airline?: string;
213
213
  flight_number?: string;
214
214
  aircraft?: string;
215
+ origin?: string;
216
+ destination?: string;
217
+ /** Per-leg Starlink verdict; see StarlinkSegmentVerdict in index.ts. */
218
+ starlink?: 'confirmed' | 'likely';
215
219
  }
216
220
  interface OfferDetailLegLike {
217
221
  segments?: OfferDetailSegmentLike[];
@@ -296,7 +300,17 @@ interface FlightSegment {
296
300
  duration_seconds: number;
297
301
  cabin_class: string;
298
302
  aircraft: string;
303
+ /** Starlink Wi-Fi on this leg. 'confirmed' = the carrier has fitted every
304
+ * aircraft of this type. 'likely' = installation on this type is underway
305
+ * but incomplete, so this airframe may not have it. Undefined means no
306
+ * information — NOT an absence of Wi-Fi. */
307
+ starlink?: StarlinkSegmentVerdict;
299
308
  }
309
+ type StarlinkSegmentVerdict = 'confirmed' | 'likely';
310
+ /** Itinerary-level Starlink verdict. '*_some' means at least one leg has none;
311
+ * 'likely_*' means the rollout on that subfleet is underway but incomplete.
312
+ * Only 'confirmed_*' should be shown to an end user as a fact. */
313
+ type StarlinkOfferVerdict = 'confirmed_all' | 'confirmed_some' | 'likely_all' | 'likely_some';
300
314
  interface FlightRoute {
301
315
  segments: FlightSegment[];
302
316
  total_duration_seconds: number;
@@ -312,6 +326,9 @@ interface FlightOffer {
312
326
  airlines: string[];
313
327
  owner_airline: string;
314
328
  bags_price: Record<string, number>;
329
+ /** Starlink Wi-Fi across the whole itinerary; per-leg detail is on each
330
+ * segment's `starlink`. Undefined when no leg has any information. */
331
+ starlink?: StarlinkOfferVerdict;
315
332
  availability_seats: number | null;
316
333
  conditions: Record<string, string>;
317
334
  is_locked: boolean;
@@ -503,7 +520,7 @@ declare class LetsFG {
503
520
  * — so this gets its own generous timeout rather than the client default.
504
521
  *
505
522
  * Each offer carries `price` (what the guest pays), `reservation_fee_now`
506
- * (the 10% taken at booking), `balance_to_supplier`, `balance_due_by` and
523
+ * (the 5% taken at booking), `balance_to_supplier`, `balance_due_by` and
507
524
  * `free_cancellation_until`. There is no wholesale figure to quote by mistake.
508
525
  *
509
526
  * Keep `session_id` and the chosen offer's `combination_id_v2`: together they
@@ -526,7 +543,7 @@ declare class LetsFG {
526
543
  * Start a booking. Returns a job immediately — it does NOT book inline.
527
544
  *
528
545
  * A booking takes minutes: the rate is re-blocked at the supplier, every
529
- * price and date rail is checked, the 10% reservation fee is charged to your
546
+ * price and date rail is checked, the 5% reservation fee is charged to your
530
547
  * card, and only then is the room committed. No proxy holds a connection that
531
548
  * long, so this returns at once and you poll hotelBooking() for the outcome.
532
549
  * Use bookHotelAndWait() if you would rather block.
@@ -589,7 +606,7 @@ declare class LetsFG {
589
606
  *
590
607
  * Free until `balance_due_by`; after that the hotel's own cancellation ladder
591
608
  * applies and can reach 100%. The ladder ships in the booking's `terms`, so
592
- * you can see the cost before calling this. The 10% reservation fee is NOT
609
+ * you can see the cost before calling this. The 5% reservation fee is NOT
593
610
  * refunded.
594
611
  *
595
612
  * This drives a browser at the supplier and takes over a minute. If it times
@@ -628,4 +645,4 @@ declare const BoostedTravel: typeof LetsFG;
628
645
  declare const BoostedTravelError: typeof LetsFGError;
629
646
  type BoostedTravelConfig = LetsFGConfig;
630
647
 
631
- export { AuthenticationError, type BookingResult, BoostedTravel, type BoostedTravelConfig, BoostedTravelError, ErrorCategory, type ErrorCategoryType, ErrorCode, type ErrorCodeType, type FlightOffer, type FlightRoute, type FlightSearchResult, type FlightSegment, LetsFG, type LetsFGConfig, LetsFGError, type OfferDetailSignals, OfferExpiredError, type Passenger, PaymentRequiredError, type RankOffer, type RankedOffer, type RankingContext, type ScoreBreakdown, type SearchOptions, TRIP_PURPOSES, type TripPurpose, type TripPurposeOptions, type UnlockResult, ValidationError, cheapestOffer, deduplicateOffers, LetsFG as default, extractOfferDetailSignals, getOfferDetailBadges, getOfferDetailPromptNotes, getPrimaryTripPurpose, getProfileLabel, normalizeTripPurposes, offerSummary, rankOffers, selectDiverseTop };
648
+ export { AuthenticationError, type BookingResult, BoostedTravel, type BoostedTravelConfig, BoostedTravelError, ErrorCategory, type ErrorCategoryType, ErrorCode, type ErrorCodeType, type FlightOffer, type FlightRoute, type FlightSearchResult, type FlightSegment, LetsFG, type LetsFGConfig, LetsFGError, type OfferDetailSignals, OfferExpiredError, type Passenger, PaymentRequiredError, type RankOffer, type RankedOffer, type RankingContext, type ScoreBreakdown, type SearchOptions, type StarlinkOfferVerdict, type StarlinkSegmentVerdict, TRIP_PURPOSES, type TripPurpose, type TripPurposeOptions, type UnlockResult, ValidationError, cheapestOffer, deduplicateOffers, LetsFG as default, extractOfferDetailSignals, getOfferDetailBadges, getOfferDetailPromptNotes, getPrimaryTripPurpose, getProfileLabel, normalizeTripPurposes, offerSummary, rankOffers, selectDiverseTop };
package/dist/index.d.ts CHANGED
@@ -212,6 +212,10 @@ interface OfferDetailSegmentLike {
212
212
  airline?: string;
213
213
  flight_number?: string;
214
214
  aircraft?: string;
215
+ origin?: string;
216
+ destination?: string;
217
+ /** Per-leg Starlink verdict; see StarlinkSegmentVerdict in index.ts. */
218
+ starlink?: 'confirmed' | 'likely';
215
219
  }
216
220
  interface OfferDetailLegLike {
217
221
  segments?: OfferDetailSegmentLike[];
@@ -296,7 +300,17 @@ interface FlightSegment {
296
300
  duration_seconds: number;
297
301
  cabin_class: string;
298
302
  aircraft: string;
303
+ /** Starlink Wi-Fi on this leg. 'confirmed' = the carrier has fitted every
304
+ * aircraft of this type. 'likely' = installation on this type is underway
305
+ * but incomplete, so this airframe may not have it. Undefined means no
306
+ * information — NOT an absence of Wi-Fi. */
307
+ starlink?: StarlinkSegmentVerdict;
299
308
  }
309
+ type StarlinkSegmentVerdict = 'confirmed' | 'likely';
310
+ /** Itinerary-level Starlink verdict. '*_some' means at least one leg has none;
311
+ * 'likely_*' means the rollout on that subfleet is underway but incomplete.
312
+ * Only 'confirmed_*' should be shown to an end user as a fact. */
313
+ type StarlinkOfferVerdict = 'confirmed_all' | 'confirmed_some' | 'likely_all' | 'likely_some';
300
314
  interface FlightRoute {
301
315
  segments: FlightSegment[];
302
316
  total_duration_seconds: number;
@@ -312,6 +326,9 @@ interface FlightOffer {
312
326
  airlines: string[];
313
327
  owner_airline: string;
314
328
  bags_price: Record<string, number>;
329
+ /** Starlink Wi-Fi across the whole itinerary; per-leg detail is on each
330
+ * segment's `starlink`. Undefined when no leg has any information. */
331
+ starlink?: StarlinkOfferVerdict;
315
332
  availability_seats: number | null;
316
333
  conditions: Record<string, string>;
317
334
  is_locked: boolean;
@@ -503,7 +520,7 @@ declare class LetsFG {
503
520
  * — so this gets its own generous timeout rather than the client default.
504
521
  *
505
522
  * Each offer carries `price` (what the guest pays), `reservation_fee_now`
506
- * (the 10% taken at booking), `balance_to_supplier`, `balance_due_by` and
523
+ * (the 5% taken at booking), `balance_to_supplier`, `balance_due_by` and
507
524
  * `free_cancellation_until`. There is no wholesale figure to quote by mistake.
508
525
  *
509
526
  * Keep `session_id` and the chosen offer's `combination_id_v2`: together they
@@ -526,7 +543,7 @@ declare class LetsFG {
526
543
  * Start a booking. Returns a job immediately — it does NOT book inline.
527
544
  *
528
545
  * A booking takes minutes: the rate is re-blocked at the supplier, every
529
- * price and date rail is checked, the 10% reservation fee is charged to your
546
+ * price and date rail is checked, the 5% reservation fee is charged to your
530
547
  * card, and only then is the room committed. No proxy holds a connection that
531
548
  * long, so this returns at once and you poll hotelBooking() for the outcome.
532
549
  * Use bookHotelAndWait() if you would rather block.
@@ -589,7 +606,7 @@ declare class LetsFG {
589
606
  *
590
607
  * Free until `balance_due_by`; after that the hotel's own cancellation ladder
591
608
  * applies and can reach 100%. The ladder ships in the booking's `terms`, so
592
- * you can see the cost before calling this. The 10% reservation fee is NOT
609
+ * you can see the cost before calling this. The 5% reservation fee is NOT
593
610
  * refunded.
594
611
  *
595
612
  * This drives a browser at the supplier and takes over a minute. If it times
@@ -628,4 +645,4 @@ declare const BoostedTravel: typeof LetsFG;
628
645
  declare const BoostedTravelError: typeof LetsFGError;
629
646
  type BoostedTravelConfig = LetsFGConfig;
630
647
 
631
- export { AuthenticationError, type BookingResult, BoostedTravel, type BoostedTravelConfig, BoostedTravelError, ErrorCategory, type ErrorCategoryType, ErrorCode, type ErrorCodeType, type FlightOffer, type FlightRoute, type FlightSearchResult, type FlightSegment, LetsFG, type LetsFGConfig, LetsFGError, type OfferDetailSignals, OfferExpiredError, type Passenger, PaymentRequiredError, type RankOffer, type RankedOffer, type RankingContext, type ScoreBreakdown, type SearchOptions, TRIP_PURPOSES, type TripPurpose, type TripPurposeOptions, type UnlockResult, ValidationError, cheapestOffer, deduplicateOffers, LetsFG as default, extractOfferDetailSignals, getOfferDetailBadges, getOfferDetailPromptNotes, getPrimaryTripPurpose, getProfileLabel, normalizeTripPurposes, offerSummary, rankOffers, selectDiverseTop };
648
+ export { AuthenticationError, type BookingResult, BoostedTravel, type BoostedTravelConfig, BoostedTravelError, ErrorCategory, type ErrorCategoryType, ErrorCode, type ErrorCodeType, type FlightOffer, type FlightRoute, type FlightSearchResult, type FlightSegment, LetsFG, type LetsFGConfig, LetsFGError, type OfferDetailSignals, OfferExpiredError, type Passenger, PaymentRequiredError, type RankOffer, type RankedOffer, type RankingContext, type ScoreBreakdown, type SearchOptions, type StarlinkOfferVerdict, type StarlinkSegmentVerdict, TRIP_PURPOSES, type TripPurpose, type TripPurposeOptions, type UnlockResult, ValidationError, cheapestOffer, deduplicateOffers, LetsFG as default, extractOfferDetailSignals, getOfferDetailBadges, getOfferDetailPromptNotes, getPrimaryTripPurpose, getProfileLabel, normalizeTripPurposes, offerSummary, rankOffers, selectDiverseTop };
package/dist/index.js CHANGED
@@ -135,6 +135,23 @@ function collectFlightNumbers(offer) {
135
135
  offer.flight_number
136
136
  ]);
137
137
  }
138
+ function buildStarlinkNote(offer) {
139
+ const segments = collectOfferSegments(offer);
140
+ const label = (s) => s.origin && s.destination ? `${s.origin}\u2192${s.destination}` : (s.flight_number || "").trim();
141
+ const confirmed = segments.filter((s) => s.starlink === "confirmed");
142
+ const likely = segments.filter((s) => s.starlink === "likely");
143
+ if (confirmed.length === 0 && likely.length === 0) return void 0;
144
+ const parts = [];
145
+ if (confirmed.length > 0) {
146
+ const where = confirmed.length === segments.length ? "every leg" : confirmed.map(label).filter(Boolean).join(", ");
147
+ parts.push(`Starlink Wi-Fi on ${where}`);
148
+ }
149
+ if (likely.length > 0) {
150
+ const where = likely.map(label).filter(Boolean).join(", ");
151
+ parts.push(`Starlink being installed on this aircraft type${where ? ` (${where})` : ""}, not guaranteed`);
152
+ }
153
+ return parts.join("; ");
154
+ }
138
155
  function collectAircraftTypes(offer) {
139
156
  return collectUniqueDetailValues(
140
157
  collectOfferSegments(offer).map((segment) => segment.aircraft?.replace(/\s*\([^)]*\)/g, "").trim())
@@ -476,6 +493,10 @@ function getOfferDetailPromptNotes(offer) {
476
493
  if (aircraftTypes.length > 0) {
477
494
  pushUniqueNote(notes, `Aircraft shown: ${aircraftTypes.join(", ")}`);
478
495
  }
496
+ const starlinkNote = buildStarlinkNote(offer);
497
+ if (starlinkNote) {
498
+ pushUniqueNote(notes, starlinkNote);
499
+ }
479
500
  const operatingCarriers = collectOperatingCarriers(offer);
480
501
  const normalizedOfferAirline = typeof offer.airline === "string" ? offer.airline.trim().toLowerCase() : "";
481
502
  if (operatingCarriers.length === 1 && operatingCarriers[0].toLowerCase() !== normalizedOfferAirline) {
@@ -1736,7 +1757,7 @@ var LetsFG = class {
1736
1757
  //
1737
1758
  // Only free-cancellation, pay-later rates are sold. Those are the rates where
1738
1759
  // the guest's balance can safely be settled with the supplier after booking,
1739
- // which is what makes 10%-now/rest-later work. The result set is smaller than
1760
+ // which is what makes 5%-now/rest-later work. The result set is smaller than
1740
1761
  // a metasearch's, and every row in it can actually be booked.
1741
1762
  /**
1742
1763
  * Resolve a place name to the city id that searchHotels() needs.
@@ -1759,7 +1780,7 @@ var LetsFG = class {
1759
1780
  * — so this gets its own generous timeout rather than the client default.
1760
1781
  *
1761
1782
  * Each offer carries `price` (what the guest pays), `reservation_fee_now`
1762
- * (the 10% taken at booking), `balance_to_supplier`, `balance_due_by` and
1783
+ * (the 5% taken at booking), `balance_to_supplier`, `balance_due_by` and
1763
1784
  * `free_cancellation_until`. There is no wholesale figure to quote by mistake.
1764
1785
  *
1765
1786
  * Keep `session_id` and the chosen offer's `combination_id_v2`: together they
@@ -1785,7 +1806,7 @@ var LetsFG = class {
1785
1806
  * Start a booking. Returns a job immediately — it does NOT book inline.
1786
1807
  *
1787
1808
  * A booking takes minutes: the rate is re-blocked at the supplier, every
1788
- * price and date rail is checked, the 10% reservation fee is charged to your
1809
+ * price and date rail is checked, the 5% reservation fee is charged to your
1789
1810
  * card, and only then is the room committed. No proxy holds a connection that
1790
1811
  * long, so this returns at once and you poll hotelBooking() for the outcome.
1791
1812
  * Use bookHotelAndWait() if you would rather block.
@@ -1867,7 +1888,7 @@ var LetsFG = class {
1867
1888
  *
1868
1889
  * Free until `balance_due_by`; after that the hotel's own cancellation ladder
1869
1890
  * applies and can reach 100%. The ladder ships in the booking's `terms`, so
1870
- * you can see the cost before calling this. The 10% reservation fee is NOT
1891
+ * you can see the cost before calling this. The 5% reservation fee is NOT
1871
1892
  * refunded.
1872
1893
  *
1873
1894
  * This drives a browser at the supplier and takes over a minute. If it times
package/dist/index.mjs CHANGED
@@ -22,7 +22,7 @@ import {
22
22
  offerSummary,
23
23
  rankOffers,
24
24
  selectDiverseTop
25
- } from "./chunk-2SNO3AGS.mjs";
25
+ } from "./chunk-PW775XDB.mjs";
26
26
  export {
27
27
  AuthenticationError,
28
28
  BoostedTravel,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "letsfg",
3
- "version": "2026.5.67",
3
+ "version": "2026.5.69",
4
4
  "description": "Flights and hotels for AI agents. Server-side engine covers hundreds of airlines; hotels are real bookable inventory with free cancellation and pay-later terms. Includes open-source ranking engine.",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",