google-fli 0.1.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 (107) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +158 -0
  3. package/dist/core/airports.d.ts +31 -0
  4. package/dist/core/airports.d.ts.map +1 -0
  5. package/dist/core/airports.js +168 -0
  6. package/dist/core/airports.js.map +1 -0
  7. package/dist/core/builders.d.ts +30 -0
  8. package/dist/core/builders.d.ts.map +1 -0
  9. package/dist/core/builders.js +113 -0
  10. package/dist/core/builders.js.map +1 -0
  11. package/dist/core/currency.d.ts +21 -0
  12. package/dist/core/currency.d.ts.map +1 -0
  13. package/dist/core/currency.js +172 -0
  14. package/dist/core/currency.js.map +1 -0
  15. package/dist/core/dates.d.ts +22 -0
  16. package/dist/core/dates.d.ts.map +1 -0
  17. package/dist/core/dates.js +41 -0
  18. package/dist/core/dates.js.map +1 -0
  19. package/dist/core/index.d.ts +7 -0
  20. package/dist/core/index.d.ts.map +1 -0
  21. package/dist/core/index.js +7 -0
  22. package/dist/core/index.js.map +1 -0
  23. package/dist/core/links.d.ts +39 -0
  24. package/dist/core/links.d.ts.map +1 -0
  25. package/dist/core/links.js +53 -0
  26. package/dist/core/links.js.map +1 -0
  27. package/dist/core/parsers.d.ts +34 -0
  28. package/dist/core/parsers.d.ts.map +1 -0
  29. package/dist/core/parsers.js +161 -0
  30. package/dist/core/parsers.js.map +1 -0
  31. package/dist/index.d.ts +30 -0
  32. package/dist/index.d.ts.map +1 -0
  33. package/dist/index.js +30 -0
  34. package/dist/index.js.map +1 -0
  35. package/dist/models/airline.d.ts +1120 -0
  36. package/dist/models/airline.d.ts.map +1 -0
  37. package/dist/models/airline.js +2224 -0
  38. package/dist/models/airline.js.map +1 -0
  39. package/dist/models/airport.d.ts +7899 -0
  40. package/dist/models/airport.d.ts.map +1 -0
  41. package/dist/models/airport.js +15782 -0
  42. package/dist/models/airport.js.map +1 -0
  43. package/dist/models/google-flights/base.d.ts +232 -0
  44. package/dist/models/google-flights/base.d.ts.map +1 -0
  45. package/dist/models/google-flights/base.js +188 -0
  46. package/dist/models/google-flights/base.js.map +1 -0
  47. package/dist/models/google-flights/dates.d.ts +51 -0
  48. package/dist/models/google-flights/dates.d.ts.map +1 -0
  49. package/dist/models/google-flights/dates.js +222 -0
  50. package/dist/models/google-flights/dates.js.map +1 -0
  51. package/dist/models/google-flights/flights.d.ts +57 -0
  52. package/dist/models/google-flights/flights.d.ts.map +1 -0
  53. package/dist/models/google-flights/flights.js +224 -0
  54. package/dist/models/google-flights/flights.js.map +1 -0
  55. package/dist/models/google-flights/index.d.ts +4 -0
  56. package/dist/models/google-flights/index.d.ts.map +1 -0
  57. package/dist/models/google-flights/index.js +4 -0
  58. package/dist/models/google-flights/index.js.map +1 -0
  59. package/dist/models/index.d.ts +4 -0
  60. package/dist/models/index.d.ts.map +1 -0
  61. package/dist/models/index.js +4 -0
  62. package/dist/models/index.js.map +1 -0
  63. package/dist/search/client.d.ts +66 -0
  64. package/dist/search/client.d.ts.map +1 -0
  65. package/dist/search/client.js +205 -0
  66. package/dist/search/client.js.map +1 -0
  67. package/dist/search/concurrency.d.ts +45 -0
  68. package/dist/search/concurrency.d.ts.map +1 -0
  69. package/dist/search/concurrency.js +148 -0
  70. package/dist/search/concurrency.js.map +1 -0
  71. package/dist/search/dates.d.ts +33 -0
  72. package/dist/search/dates.d.ts.map +1 -0
  73. package/dist/search/dates.js +159 -0
  74. package/dist/search/dates.js.map +1 -0
  75. package/dist/search/decoders.d.ts +22 -0
  76. package/dist/search/decoders.d.ts.map +1 -0
  77. package/dist/search/decoders.js +333 -0
  78. package/dist/search/decoders.js.map +1 -0
  79. package/dist/search/exceptions.d.ts +21 -0
  80. package/dist/search/exceptions.d.ts.map +1 -0
  81. package/dist/search/exceptions.js +37 -0
  82. package/dist/search/exceptions.js.map +1 -0
  83. package/dist/search/flights.d.ts +57 -0
  84. package/dist/search/flights.d.ts.map +1 -0
  85. package/dist/search/flights.js +298 -0
  86. package/dist/search/flights.js.map +1 -0
  87. package/dist/search/helpers.d.ts +10 -0
  88. package/dist/search/helpers.d.ts.map +1 -0
  89. package/dist/search/helpers.js +30 -0
  90. package/dist/search/helpers.js.map +1 -0
  91. package/dist/search/index.d.ts +10 -0
  92. package/dist/search/index.d.ts.map +1 -0
  93. package/dist/search/index.js +10 -0
  94. package/dist/search/index.js.map +1 -0
  95. package/dist/search/proto.d.ts +69 -0
  96. package/dist/search/proto.d.ts.map +1 -0
  97. package/dist/search/proto.js +355 -0
  98. package/dist/search/proto.js.map +1 -0
  99. package/dist/search/urls.d.ts +9 -0
  100. package/dist/search/urls.d.ts.map +1 -0
  101. package/dist/search/urls.js +9 -0
  102. package/dist/search/urls.js.map +1 -0
  103. package/dist/search/wire.d.ts +14 -0
  104. package/dist/search/wire.d.ts.map +1 -0
  105. package/dist/search/wire.js +120 -0
  106. package/dist/search/wire.js.map +1 -0
  107. package/package.json +91 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Punit Arani
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,158 @@
1
+ # fli (TypeScript)
2
+
3
+ A 1:1 TypeScript / JavaScript port of the [Python `fli` library](https://github.com/punitarani/fli).
4
+
5
+ Programmatic access to Google Flights data via direct API interaction (no
6
+ scraping). The API surface mirrors the Python package — same models, same
7
+ filter encoding, same wire-format decoders.
8
+
9
+ ## Install
10
+
11
+ ```bash
12
+ npm i fli-js # or: pnpm add fli-js / yarn add fli-js / bun add fli-js
13
+ ```
14
+
15
+ ## Quick start
16
+
17
+ ```ts
18
+ import {
19
+ Airport,
20
+ FlightSearchFilters,
21
+ FlightSegment,
22
+ MaxStops,
23
+ PassengerInfo,
24
+ SearchFlights,
25
+ SeatType,
26
+ SortBy,
27
+ } from "fli-js";
28
+
29
+ const filters = new FlightSearchFilters({
30
+ passenger_info: { adults: 1, children: 0, infants_in_seat: 0, infants_on_lap: 0 },
31
+ flight_segments: [
32
+ new FlightSegment({
33
+ departure_airport: [[[Airport.JFK, 0]]],
34
+ arrival_airport: [[[Airport.LAX, 0]]],
35
+ travel_date: "2026-12-25",
36
+ }),
37
+ ],
38
+ seat_type: SeatType.ECONOMY,
39
+ stops: MaxStops.NON_STOP,
40
+ sort_by: SortBy.CHEAPEST,
41
+ });
42
+
43
+ const results = await new SearchFlights().search(filters, { currency: "USD" });
44
+ console.log(results);
45
+ ```
46
+
47
+ ### Date-range search
48
+
49
+ ```ts
50
+ import { Airport, DateSearchFilters, FlightSegment, SearchDates } from "fli-js";
51
+
52
+ const filters = new DateSearchFilters({
53
+ passenger_info: { adults: 1, children: 0, infants_in_seat: 0, infants_on_lap: 0 },
54
+ flight_segments: [
55
+ new FlightSegment({
56
+ departure_airport: [[[Airport.JFK, 0]]],
57
+ arrival_airport: [[[Airport.LAX, 0]]],
58
+ travel_date: "2026-12-01",
59
+ }),
60
+ ],
61
+ from_date: "2026-12-01",
62
+ to_date: "2026-12-31",
63
+ });
64
+
65
+ const dates = await new SearchDates().search(filters);
66
+ ```
67
+
68
+ ### Booking links
69
+
70
+ Turn search results into clickable Google Flights links — no extra network
71
+ call, fully deterministic.
72
+
73
+ ```ts
74
+ import { googleFlightsUrl, SearchFlights } from "fli-js";
75
+
76
+ const search = new SearchFlights();
77
+ const results = await search.search(filters, { currency: "USD" });
78
+
79
+ // Per-flight deep link: opens the specific itinerary's booking page
80
+ // (vendor fares + "Continue" CTA) via a `tfs` protobuf token.
81
+ const first = results?.[0];
82
+ if (first) {
83
+ console.log(search.buildFlightBookingUrl(first, { currency: "USD" }));
84
+ // https://www.google.com/travel/flights/booking?tfs=…&curr=USD
85
+ }
86
+
87
+ // Search-level deep link: route + dates pre-filled (handy for a date result
88
+ // or a quick "view on Google Flights" link).
89
+ console.log(googleFlightsUrl("JFK", "LHR", "2026-12-25", null, { currency: "USD" }));
90
+ // https://www.google.com/travel/flights?q=Flights%20from%20JFK%20to%20LHR%20on%202026-12-25&curr=USD
91
+ ```
92
+
93
+ `buildFlightBookingUrl` accepts a single `FlightResult` (one-way) or an array
94
+ of them (round-trip / multi-city). It never throws — on malformed input it
95
+ falls back to the generic Google Flights URL.
96
+
97
+ ## HTTP / proxy configuration
98
+
99
+ The TypeScript port uses native `fetch` (Bun's built-in) and replaces
100
+ `curl_cffi`'s TLS impersonation with:
101
+
102
+ - realistic Chrome `User-Agent` + `Sec-CH-*` headers,
103
+ - automatic rate-limiting at 10 req/s,
104
+ - 3-attempt exponential backoff on transient errors,
105
+ - proxy support via the `HTTPS_PROXY` / `HTTP_PROXY` env vars (or via the
106
+ explicit `proxy` option on `new Client({...})`).
107
+
108
+ ```ts
109
+ import { Client, SearchFlights } from "fli-js";
110
+
111
+ const search = new SearchFlights(
112
+ new Client({ proxy: "http://user:pass@proxy.example.com:8080" }),
113
+ );
114
+ ```
115
+
116
+ Set the per-request timeout with `FLI_TIMEOUT=30` (seconds) or via the
117
+ `timeoutMs` option on `new Client({...})`.
118
+
119
+ ## Modules
120
+
121
+ - `fli-js/models` — `Airport`, `Airline`, `FlightSearchFilters`, `DateSearchFilters`,
122
+ `FlightSegment`, `FlightResult`, `BookingOption`, all enums.
123
+ - `fli-js/core` — string-to-enum parsers, segment builders, airport search,
124
+ currency token decoders, deep-link builder (`googleFlightsUrl`).
125
+ - `fli-js/search` — `SearchFlights` (incl. `buildFlightBookingUrl`),
126
+ `SearchDates`, `Client`, error classes, protobuf token helpers
127
+ (`buildBookingToken`, `buildTfsToken`, `extractBookingTokenFromTfu`).
128
+
129
+ ## Development
130
+
131
+ ```bash
132
+ bun install
133
+ bun run generate:enums # regenerate airport.ts / airline.ts from data/*.csv
134
+ bun run typecheck
135
+ bun run lint # biome + oxlint
136
+ bun run format # biome format
137
+ bun test # unit + integration tests (no network)
138
+ bun run test:e2e # live tests (FLI_E2E=1; talks to Google Flights)
139
+ bun run ci # format-check + lint + typecheck + tests
140
+ ```
141
+
142
+ ## Parity with the Python library
143
+
144
+ The TypeScript port preserves byte-perfect wire compatibility with the
145
+ Python upstream:
146
+
147
+ - `FlightSearchFilters.format()` produces structurally identical
148
+ nested-list payloads (see `tests/integration/filter_format_snapshots.test.ts`).
149
+ - `buildBookingToken(...)` and `buildTfsToken(...)` reproduce captured live
150
+ booking-page tokens byte-for-byte (see `tests/search/proto.test.ts`); the
151
+ resulting `buildFlightBookingUrl(...)` output matches the Python library
152
+ character-for-character.
153
+ - The wire-format parser handles both the legacy single-chunk JSONP
154
+ shape and the multi-chunk format used by `GetBookingResults`.
155
+
156
+ ## License
157
+
158
+ MIT — same as the upstream Python project.
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Airport search by IATA code, city name, or airport name.
3
+ * 1:1 port of fli/core/airports.py — same 5-priority cascade.
4
+ */
5
+ import { type Airport } from "../models/airport.ts";
6
+ export type MatchType = "iata_exact" | "iata_prefix" | "city" | "name";
7
+ export interface AirportMatch {
8
+ code: Airport;
9
+ name: string;
10
+ match_type: MatchType;
11
+ score: number;
12
+ }
13
+ /**
14
+ * Curated multi-airport city aliases (e.g., "new york" → JFK / LGA / EWR).
15
+ * Mirrors the Python CITY_AIRPORTS dict.
16
+ */
17
+ export declare const CITY_AIRPORTS: Record<string, string[]>;
18
+ /**
19
+ * Search airports by city name, airport name, or IATA code.
20
+ *
21
+ * Results are ranked 0-100 (higher = better) via the same 5-priority
22
+ * cascade used in the Python implementation:
23
+ *
24
+ * 1. iata_exact (100) — exact IATA code
25
+ * 2. city (90) — exact city/alias
26
+ * 3. city (80) — prefix of a city/alias
27
+ * 4. name (≤70) — substring of an airport's name (position-weighted)
28
+ * 5. iata_prefix (60) — prefix of an IATA code (only for ≤3-char queries)
29
+ */
30
+ export declare function searchAirports(query: string, limit?: number): AirportMatch[];
31
+ //# sourceMappingURL=airports.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"airports.d.ts","sourceRoot":"","sources":["../../src/core/airports.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAiB,KAAK,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAEnE,MAAM,MAAM,SAAS,GAAG,YAAY,GAAG,aAAa,GAAG,MAAM,GAAG,MAAM,CAAC;AAEvE,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,OAAO,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,SAAS,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;GAGG;AACH,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAoDlD,CAAC;AAYF;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,SAAK,GAAG,YAAY,EAAE,CAsFxE"}
@@ -0,0 +1,168 @@
1
+ /**
2
+ * Airport search by IATA code, city name, or airport name.
3
+ * 1:1 port of fli/core/airports.py — same 5-priority cascade.
4
+ */
5
+ import { AIRPORT_NAMES } from "../models/airport.js";
6
+ /**
7
+ * Curated multi-airport city aliases (e.g., "new york" → JFK / LGA / EWR).
8
+ * Mirrors the Python CITY_AIRPORTS dict.
9
+ */
10
+ export const CITY_AIRPORTS = {
11
+ "new york": ["JFK", "LGA", "EWR"],
12
+ nyc: ["JFK", "LGA", "EWR"],
13
+ chicago: ["ORD", "MDW"],
14
+ washington: ["IAD", "DCA", "BWI"],
15
+ "washington dc": ["IAD", "DCA", "BWI"],
16
+ london: ["LHR", "LGW", "STN", "LTN", "LCY"],
17
+ paris: ["CDG", "ORY"],
18
+ tokyo: ["NRT", "HND"],
19
+ osaka: ["KIX", "ITM"],
20
+ seoul: ["ICN", "GMP"],
21
+ beijing: ["PEK", "PKX"],
22
+ shanghai: ["PVG", "SHA"],
23
+ bangkok: ["BKK", "DMK"],
24
+ istanbul: ["IST", "SAW"],
25
+ moscow: ["SVO", "DME", "VKO"],
26
+ milan: ["MXP", "LIN"],
27
+ rome: ["FCO", "CIA"],
28
+ berlin: ["BER"],
29
+ mumbai: ["BOM"],
30
+ delhi: ["DEL"],
31
+ "sao paulo": ["GRU", "CGH"],
32
+ rio: ["GIG", "SDU"],
33
+ "rio de janeiro": ["GIG", "SDU"],
34
+ toronto: ["YYZ", "YTZ"],
35
+ montreal: ["YUL"],
36
+ "mexico city": ["MEX"],
37
+ "buenos aires": ["EZE", "AEP"],
38
+ dubai: ["DXB", "DWC"],
39
+ singapore: ["SIN"],
40
+ "hong kong": ["HKG"],
41
+ taipei: ["TPE", "TSA"],
42
+ sydney: ["SYD"],
43
+ melbourne: ["MEL"],
44
+ "san francisco": ["SFO", "OAK", "SJC"],
45
+ sf: ["SFO", "OAK", "SJC"],
46
+ "bay area": ["SFO", "OAK", "SJC"],
47
+ "los angeles": ["LAX", "BUR", "SNA", "ONT", "LGB"],
48
+ la: ["LAX", "BUR", "SNA", "ONT", "LGB"],
49
+ dallas: ["DFW", "DAL"],
50
+ houston: ["IAH", "HOU"],
51
+ atlanta: ["ATL"],
52
+ denver: ["DEN"],
53
+ seattle: ["SEA"],
54
+ boston: ["BOS"],
55
+ miami: ["MIA", "FLL"],
56
+ detroit: ["DTW"],
57
+ minneapolis: ["MSP"],
58
+ phoenix: ["PHX"],
59
+ orlando: ["MCO"],
60
+ "las vegas": ["LAS"],
61
+ honolulu: ["HNL"],
62
+ };
63
+ // Validate at module load — surfaces typos in CITY_AIRPORTS immediately
64
+ // instead of returning silently-empty results at search time.
65
+ for (const [city, codes] of Object.entries(CITY_AIRPORTS)) {
66
+ for (const code of codes) {
67
+ if (!(code in AIRPORT_NAMES)) {
68
+ throw new Error(`CITY_AIRPORTS[${city}] references unknown IATA code '${code}'`);
69
+ }
70
+ }
71
+ }
72
+ /**
73
+ * Search airports by city name, airport name, or IATA code.
74
+ *
75
+ * Results are ranked 0-100 (higher = better) via the same 5-priority
76
+ * cascade used in the Python implementation:
77
+ *
78
+ * 1. iata_exact (100) — exact IATA code
79
+ * 2. city (90) — exact city/alias
80
+ * 3. city (80) — prefix of a city/alias
81
+ * 4. name (≤70) — substring of an airport's name (position-weighted)
82
+ * 5. iata_prefix (60) — prefix of an IATA code (only for ≤3-char queries)
83
+ */
84
+ export function searchAirports(query, limit = 10) {
85
+ const queryLower = query.trim().toLowerCase();
86
+ if (!queryLower || limit < 1)
87
+ return [];
88
+ const results = [];
89
+ const seen = new Set();
90
+ // 1. Exact IATA code match
91
+ const queryUpper = query.trim().toUpperCase();
92
+ if (queryUpper in AIRPORT_NAMES) {
93
+ results.push({
94
+ code: queryUpper,
95
+ name: queryUpper,
96
+ match_type: "iata_exact",
97
+ score: 100.0,
98
+ });
99
+ seen.add(queryUpper);
100
+ }
101
+ // 2. Exact city alias match
102
+ if (queryLower in CITY_AIRPORTS) {
103
+ for (const code of CITY_AIRPORTS[queryLower] ?? []) {
104
+ if (!seen.has(code)) {
105
+ results.push({
106
+ code: code,
107
+ name: code,
108
+ match_type: "city",
109
+ score: 90.0,
110
+ });
111
+ seen.add(code);
112
+ }
113
+ }
114
+ }
115
+ // 3. City alias prefix match
116
+ if (!(queryLower in CITY_AIRPORTS)) {
117
+ for (const [city, codes] of Object.entries(CITY_AIRPORTS)) {
118
+ if (city.startsWith(queryLower)) {
119
+ for (const code of codes) {
120
+ if (!seen.has(code)) {
121
+ results.push({
122
+ code: code,
123
+ name: code,
124
+ match_type: "city",
125
+ score: 80.0,
126
+ });
127
+ seen.add(code);
128
+ }
129
+ }
130
+ }
131
+ }
132
+ }
133
+ // 4. Airport name substring match (position-weighted score)
134
+ for (const [code, name] of Object.entries(AIRPORT_NAMES)) {
135
+ if (seen.has(code))
136
+ continue;
137
+ const nameLower = name.toLowerCase();
138
+ const pos = nameLower.indexOf(queryLower);
139
+ if (pos !== -1) {
140
+ const score = 70.0 - pos * 0.1;
141
+ results.push({ code: code, name, match_type: "name", score });
142
+ seen.add(code);
143
+ }
144
+ }
145
+ // 5. IATA prefix match (≤3-char query)
146
+ if (queryUpper.length <= 3) {
147
+ for (const [code, name] of Object.entries(AIRPORT_NAMES)) {
148
+ if (seen.has(code))
149
+ continue;
150
+ if (code.startsWith(queryUpper)) {
151
+ results.push({
152
+ code: code,
153
+ name,
154
+ match_type: "iata_prefix",
155
+ score: 60.0,
156
+ });
157
+ seen.add(code);
158
+ }
159
+ }
160
+ }
161
+ results.sort((a, b) => {
162
+ if (a.score !== b.score)
163
+ return b.score - a.score;
164
+ return a.code < b.code ? -1 : a.code > b.code ? 1 : 0;
165
+ });
166
+ return results.slice(0, limit);
167
+ }
168
+ //# sourceMappingURL=airports.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"airports.js","sourceRoot":"","sources":["../../src/core/airports.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Builder utilities for constructing search filters. 1:1 port of
3
+ * fli/core/builders.py.
4
+ */
5
+ import type { Airport } from "../models/airport.ts";
6
+ import { FlightSegment, type TimeRestrictions, TripType } from "../models/google-flights/base.ts";
7
+ /** Zero-pad a YYYY-MM-DD date string. */
8
+ export declare function normalizeDate(dateStr: string): string;
9
+ /** Build a `TimeRestrictions` from "HH-HH"-style window strings, or `null`. */
10
+ export declare function buildTimeRestrictions(departureWindow?: string | null, arrivalWindow?: string | null): TimeRestrictions | null;
11
+ /** Build flight segments for a one-way or round-trip search. */
12
+ export declare function buildFlightSegments(origin: Airport | Airport[], destination: Airport | Airport[], departureDate: string, returnDate?: string | null, timeRestrictions?: TimeRestrictions | null): {
13
+ segments: FlightSegment[];
14
+ tripType: TripType;
15
+ };
16
+ /** Build flight segments for a multi-city search from a list of legs. */
17
+ export declare function buildMultiCitySegments(legs: Array<[Airport, Airport, string]>, timeRestrictions?: TimeRestrictions | null): {
18
+ segments: FlightSegment[];
19
+ tripType: TripType;
20
+ };
21
+ /** Build flight segments for a date-range search. */
22
+ export declare function buildDateSearchSegments(origin: Airport | Airport[], destination: Airport | Airport[], startDate: string, options?: {
23
+ tripDuration?: number | null;
24
+ isRoundTrip?: boolean;
25
+ timeRestrictions?: TimeRestrictions | null;
26
+ }): {
27
+ segments: FlightSegment[];
28
+ tripType: TripType;
29
+ };
30
+ //# sourceMappingURL=builders.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"builders.d.ts","sourceRoot":"","sources":["../../src/core/builders.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,KAAK,gBAAgB,EAAE,QAAQ,EAAE,MAAM,kCAAkC,CAAC;AAKlG,yCAAyC;AACzC,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAgBrD;AAED,+EAA+E;AAC/E,wBAAgB,qBAAqB,CACnC,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,EAC/B,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,GAC5B,gBAAgB,GAAG,IAAI,CAqBzB;AAED,gEAAgE;AAChE,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,OAAO,GAAG,OAAO,EAAE,EAC3B,WAAW,EAAE,OAAO,GAAG,OAAO,EAAE,EAChC,aAAa,EAAE,MAAM,EACrB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,EAC1B,gBAAgB,CAAC,EAAE,gBAAgB,GAAG,IAAI,GACzC;IAAE,QAAQ,EAAE,aAAa,EAAE,CAAC;IAAC,QAAQ,EAAE,QAAQ,CAAA;CAAE,CA6BnD;AAED,yEAAyE;AACzE,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,KAAK,CAAC,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC,EACvC,gBAAgB,CAAC,EAAE,gBAAgB,GAAG,IAAI,GACzC;IAAE,QAAQ,EAAE,aAAa,EAAE,CAAC;IAAC,QAAQ,EAAE,QAAQ,CAAA;CAAE,CAWnD;AAED,qDAAqD;AACrD,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,OAAO,GAAG,OAAO,EAAE,EAC3B,WAAW,EAAE,OAAO,GAAG,OAAO,EAAE,EAChC,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE;IACP,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,gBAAgB,CAAC,EAAE,gBAAgB,GAAG,IAAI,CAAC;CACvC,GACL;IAAE,QAAQ,EAAE,aAAa,EAAE,CAAC;IAAC,QAAQ,EAAE,QAAQ,CAAA;CAAE,CAoCnD"}
@@ -0,0 +1,113 @@
1
+ /**
2
+ * Builder utilities for constructing search filters. 1:1 port of
3
+ * fli/core/builders.py.
4
+ */
5
+ import { FlightSegment, TripType } from "../models/google-flights/base.js";
6
+ import { parseTimeRange } from "./parsers.js";
7
+ const ISO_DATE_RE = /^\d{4}-\d{1,2}-\d{1,2}$/;
8
+ /** Zero-pad a YYYY-MM-DD date string. */
9
+ export function normalizeDate(dateStr) {
10
+ if (!ISO_DATE_RE.test(dateStr)) {
11
+ throw new Error(`Invalid date string: ${dateStr}`);
12
+ }
13
+ const parts = dateStr.split("-").map((p) => Number.parseInt(p, 10));
14
+ const [y, m, d] = parts;
15
+ if (y == null || m == null || d == null) {
16
+ throw new Error(`Invalid date string: ${dateStr}`);
17
+ }
18
+ const date = new Date(Date.UTC(y, m - 1, d));
19
+ if (date.getUTCFullYear() !== y || date.getUTCMonth() !== m - 1 || date.getUTCDate() !== d) {
20
+ throw new Error(`Invalid date: ${dateStr}`);
21
+ }
22
+ const mm = String(m).padStart(2, "0");
23
+ const dd = String(d).padStart(2, "0");
24
+ return `${y}-${mm}-${dd}`;
25
+ }
26
+ /** Build a `TimeRestrictions` from "HH-HH"-style window strings, or `null`. */
27
+ export function buildTimeRestrictions(departureWindow, arrivalWindow) {
28
+ if (!departureWindow && !arrivalWindow)
29
+ return null;
30
+ let earliestDeparture = null;
31
+ let latestDeparture = null;
32
+ let earliestArrival = null;
33
+ let latestArrival = null;
34
+ if (departureWindow) {
35
+ [earliestDeparture, latestDeparture] = parseTimeRange(departureWindow);
36
+ }
37
+ if (arrivalWindow) {
38
+ [earliestArrival, latestArrival] = parseTimeRange(arrivalWindow);
39
+ }
40
+ return {
41
+ earliest_departure: earliestDeparture ?? undefined,
42
+ latest_departure: latestDeparture ?? undefined,
43
+ earliest_arrival: earliestArrival ?? undefined,
44
+ latest_arrival: latestArrival ?? undefined,
45
+ };
46
+ }
47
+ /** Build flight segments for a one-way or round-trip search. */
48
+ export function buildFlightSegments(origin, destination, departureDate, returnDate, timeRestrictions) {
49
+ const depDate = normalizeDate(departureDate);
50
+ const origins = Array.isArray(origin) ? origin : [origin];
51
+ const destinations = Array.isArray(destination) ? destination : [destination];
52
+ const segments = [
53
+ new FlightSegment({
54
+ departure_airport: [origins.map((apt) => [apt, 0])],
55
+ arrival_airport: [destinations.map((apt) => [apt, 0])],
56
+ travel_date: depDate,
57
+ time_restrictions: timeRestrictions ?? null,
58
+ }),
59
+ ];
60
+ let tripType = TripType.ONE_WAY;
61
+ if (returnDate) {
62
+ const retDate = normalizeDate(returnDate);
63
+ tripType = TripType.ROUND_TRIP;
64
+ segments.push(new FlightSegment({
65
+ departure_airport: [destinations.map((apt) => [apt, 0])],
66
+ arrival_airport: [origins.map((apt) => [apt, 0])],
67
+ travel_date: retDate,
68
+ time_restrictions: timeRestrictions ?? null,
69
+ }));
70
+ }
71
+ return { segments, tripType };
72
+ }
73
+ /** Build flight segments for a multi-city search from a list of legs. */
74
+ export function buildMultiCitySegments(legs, timeRestrictions) {
75
+ const segments = legs.map(([origin, destination, date]) => new FlightSegment({
76
+ departure_airport: [[[origin, 0]]],
77
+ arrival_airport: [[[destination, 0]]],
78
+ travel_date: normalizeDate(date),
79
+ time_restrictions: timeRestrictions ?? null,
80
+ }));
81
+ return { segments, tripType: TripType.MULTI_CITY };
82
+ }
83
+ /** Build flight segments for a date-range search. */
84
+ export function buildDateSearchSegments(origin, destination, startDate, options = {}) {
85
+ const startNormalized = normalizeDate(startDate);
86
+ const origins = Array.isArray(origin) ? origin : [origin];
87
+ const destinations = Array.isArray(destination) ? destination : [destination];
88
+ const segments = [
89
+ new FlightSegment({
90
+ departure_airport: [origins.map((apt) => [apt, 0])],
91
+ arrival_airport: [destinations.map((apt) => [apt, 0])],
92
+ travel_date: startNormalized,
93
+ time_restrictions: options.timeRestrictions ?? null,
94
+ }),
95
+ ];
96
+ let tripType = TripType.ONE_WAY;
97
+ if (options.isRoundTrip) {
98
+ tripType = TripType.ROUND_TRIP;
99
+ const startMs = Date.parse(`${startNormalized}T00:00:00Z`);
100
+ const days = options.tripDuration ?? 3;
101
+ const returnMs = startMs + days * 24 * 60 * 60 * 1000;
102
+ const returnDate = new Date(returnMs);
103
+ const ret = `${returnDate.getUTCFullYear()}-${String(returnDate.getUTCMonth() + 1).padStart(2, "0")}-${String(returnDate.getUTCDate()).padStart(2, "0")}`;
104
+ segments.push(new FlightSegment({
105
+ departure_airport: [destinations.map((apt) => [apt, 0])],
106
+ arrival_airport: [origins.map((apt) => [apt, 0])],
107
+ travel_date: ret,
108
+ time_restrictions: options.timeRestrictions ?? null,
109
+ }));
110
+ }
111
+ return { segments, tripType };
112
+ }
113
+ //# sourceMappingURL=builders.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"builders.js","sourceRoot":"","sources":["../../src/core/builders.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Currency extraction from Google Flights price tokens.
3
+ *
4
+ * 1:1 port of fli/core/currency.py — varint walk over a base64-urlsafe
5
+ * protobuf payload to extract the nested ISO currency code, cached to
6
+ * avoid repeated decoding for the common one-currency-per-response case.
7
+ */
8
+ /**
9
+ * Extract the ISO currency code from a Google Flights price token.
10
+ *
11
+ * Cached so the same token returned for every row in a response decodes
12
+ * the protobuf payload exactly once.
13
+ */
14
+ export declare function extractCurrencyFromPriceToken(token: string | null | undefined): string | null;
15
+ /** Currency-code-aware formatter built on `Intl.NumberFormat`. */
16
+ export declare function formatPrice(amount: number | null | undefined, currencyCode: string | null | undefined): string;
17
+ /** Build a chart-axis label for one or more result currencies. */
18
+ export declare function formatPriceAxisLabel(currencies: Iterable<string | null | undefined>): string;
19
+ /** Clear the in-memory token-decode cache (test helper). */
20
+ export declare function _clearCurrencyCache(): void;
21
+ //# sourceMappingURL=currency.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"currency.d.ts","sourceRoot":"","sources":["../../src/core/currency.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAgGH;;;;;GAKG;AACH,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAiB7F;AAED,kEAAkE;AAClE,wBAAgB,WAAW,CACzB,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EACjC,YAAY,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GACtC,MAAM,CAsBR;AAED,kEAAkE;AAClE,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,QAAQ,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,GAAG,MAAM,CAU5F;AAED,4DAA4D;AAC5D,wBAAgB,mBAAmB,IAAI,IAAI,CAE1C"}