@mattywhite/skyscanner-api 1.0.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.
package/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 @irrisolto (original Python version)
4
+ Copyright (c) 2025 Node.js port contributors
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in all
14
+ copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,407 @@
1
+ # SkyScanner API Client (Node.js)
2
+
3
+ A Node.js library for querying the reverse-engineered SkyScanner Android app API for flights, airports, locations, and car rentals, with built-in retries and error handling.
4
+
5
+ > **Note**: This is a Node.js port of the original Python library. Original credit goes to @irrisolto.
6
+
7
+ ## Table of Contents
8
+
9
+ - [Installation](#installation)
10
+ - [Quick Start](#quick-start)
11
+ - [Class Reference](#class-reference)
12
+ - [Methods](#methods)
13
+ - [Types and Enums](#types-and-enums)
14
+ - [Error Handling](#error-handling)
15
+ - [Examples](#examples)
16
+
17
+ ## Installation
18
+
19
+ ```bash
20
+ npm install skyscanner-api
21
+ ```
22
+
23
+ Or if you're installing from source:
24
+
25
+ ```bash
26
+ npm install
27
+ ```
28
+
29
+ ## Quick Start
30
+
31
+ ```javascript
32
+ const { SkyScanner, CabinClass, SpecialTypes } = require('skyscanner-api');
33
+
34
+ async function main() {
35
+ // Initialize the client
36
+ const scanner = new SkyScanner({
37
+ locale: "en-US",
38
+ currency: "USD",
39
+ market: "US"
40
+ });
41
+
42
+ // Search for airports
43
+ const airports = await scanner.searchAirports("London");
44
+ const origin = airports[0];
45
+
46
+ const destinationAirports = await scanner.searchAirports("New York");
47
+ const destination = destinationAirports[0];
48
+
49
+ // Search for flights
50
+ const departDate = new Date('2025-08-15');
51
+ const returnDate = new Date('2025-08-22');
52
+
53
+ const response = await scanner.getFlightPrices({
54
+ origin: origin,
55
+ destination: destination,
56
+ departDate: departDate,
57
+ returnDate: returnDate,
58
+ cabinClass: CabinClass.ECONOMY,
59
+ adults: 2
60
+ });
61
+
62
+ console.log(response.json);
63
+ }
64
+
65
+ main().catch(console.error);
66
+ ```
67
+
68
+ ## Class Reference
69
+
70
+ ### SkyScanner
71
+
72
+ The main client class for interacting with Skyscanner APIs.
73
+
74
+ #### Constructor
75
+
76
+ ```javascript
77
+ const scanner = new SkyScanner({
78
+ locale: "en-US", // Locale code for results
79
+ currency: "USD", // Currency code for pricing
80
+ market: "US", // Market region code
81
+ retryDelay: 2, // Seconds to wait between polling retries
82
+ maxRetries: 15, // Maximum number of polling retries
83
+ proxy: "", // Proxy URL for HTTP requests
84
+ pxAuthorization: null, // Optional pre-generated PX authorization token
85
+ verify: true // Whether to verify SSL certificates
86
+ });
87
+ ```
88
+
89
+ **Parameters:**
90
+ - `locale` (string): Locale code for results (e.g., "en-US", "fr-FR")
91
+ - `currency` (string): Currency code for pricing (e.g., "USD", "EUR", "GBP")
92
+ - `market` (string): Market region code (e.g., "US", "UK", "DE")
93
+ - `retryDelay` (number): Seconds to wait between polling retries
94
+ - `maxRetries` (number): Maximum number of polling retries before giving up
95
+ - `proxy` (string): Proxy URL configuration for HTTP requests
96
+ - `pxAuthorization` (string | null): Optional pre-generated PX authorization token
97
+ - `verify` (boolean): Whether to verify SSL certificates
98
+
99
+ ## Methods
100
+
101
+ ### getFlightPrices()
102
+
103
+ Search for flight prices between two locations.
104
+
105
+ ```javascript
106
+ await scanner.getFlightPrices({
107
+ origin, // Airport object
108
+ destination, // Airport | SpecialTypes
109
+ departDate, // Date | SpecialTypes | null
110
+ returnDate, // Date | SpecialTypes | null
111
+ cabinClass, // CabinClass (default: ECONOMY)
112
+ adults, // number (default: 1)
113
+ childAges // number[] (default: [])
114
+ });
115
+ ```
116
+
117
+ **Parameters:**
118
+ - `origin` (Airport): Origin airport object
119
+ - `destination` (Airport | SpecialTypes): Destination airport or special search type (e.g., SpecialTypes.EVERYWHERE)
120
+ - `departDate` (Date | SpecialTypes | null): Departure date or SpecialTypes.ANYTIME
121
+ - `returnDate` (Date | SpecialTypes | null): Return date (optional for one-way trips)
122
+ - `cabinClass` (string): Cabin class preference (from CabinClass enum)
123
+ - `adults` (number): Number of adult passengers (1-8)
124
+ - `childAges` (number[]): Ages of child passengers (0-17 years, max 8 children)
125
+
126
+ **Returns:** `SkyscannerResponse` object containing flight options and pricing data
127
+
128
+ **Throws:**
129
+ - `Error`: Invalid dates, passenger counts, or search parameters
130
+ - `BannedWithCaptcha`: When blocked by Skyscanner's anti-bot measures
131
+ - `AttemptsExhaustedIncompleteResponse`: When max retries exceeded
132
+
133
+ ### searchAirports()
134
+
135
+ Auto-suggest airports based on a search query.
136
+
137
+ ```javascript
138
+ await scanner.searchAirports(query, departDate, returnDate);
139
+ ```
140
+
141
+ **Parameters:**
142
+ - `query` (string): Search text (airport name, city, or IATA code)
143
+ - `departDate` (Date | null): Optional departure date for context
144
+ - `returnDate` (Date | null): Optional return date for context
145
+
146
+ **Returns:** Array of `Airport` objects matching the query
147
+
148
+ ### searchLocations()
149
+
150
+ Auto-suggest locations for car rentals and other services.
151
+
152
+ ```javascript
153
+ await scanner.searchLocations(query);
154
+ ```
155
+
156
+ **Parameters:**
157
+ - `query` (string): Location search text
158
+
159
+ **Returns:** Array of `Location` objects
160
+
161
+ ### getAirportByCode()
162
+
163
+ Retrieve a specific airport by its IATA code.
164
+
165
+ ```javascript
166
+ await scanner.getAirportByCode(airportCode);
167
+ ```
168
+
169
+ **Parameters:**
170
+ - `airportCode` (string): Three-letter IATA airport code (e.g., "JFK", "LHR")
171
+
172
+ **Returns:** `Airport` object for the specified code
173
+
174
+ **Throws:**
175
+ - `GenericError`: If airport code not found
176
+
177
+ ### getItineraryDetails()
178
+
179
+ Get detailed information for a specific flight itinerary.
180
+
181
+ ```javascript
182
+ await scanner.getItineraryDetails(itineraryId, response);
183
+ ```
184
+
185
+ **Parameters:**
186
+ - `itineraryId` (string): Unique itinerary identifier from search results
187
+ - `response` (SkyscannerResponse): Original search response containing session data
188
+
189
+ **Returns:** Object with detailed itinerary information including flight legs and preferences
190
+
191
+ ### getCarRental()
192
+
193
+ Search for car rental options between locations and times.
194
+
195
+ ```javascript
196
+ await scanner.getCarRental({
197
+ origin, // Location | Coordinates | Airport
198
+ departTime, // Date
199
+ returnTime, // Date
200
+ destination, // Location | Coordinates | Airport | null
201
+ isDriverOver25 // boolean (default: true)
202
+ });
203
+ ```
204
+
205
+ **Parameters:**
206
+ - `origin` (Location | Coordinates | Airport): Pickup location
207
+ - `departTime` (Date): Pickup date and time
208
+ - `returnTime` (Date): Drop-off date and time
209
+ - `destination` (Location | Coordinates | Airport | null): Drop-off location (defaults to origin)
210
+ - `isDriverOver25` (boolean): Driver age flag affecting pricing
211
+
212
+ **Returns:** Object containing car rental options and pricing
213
+
214
+ ### getCarRentalFromUrl()
215
+
216
+ Parse a Skyscanner car rental URL and fetch rental options.
217
+
218
+ ```javascript
219
+ await scanner.getCarRentalFromUrl(url);
220
+ ```
221
+
222
+ **Parameters:**
223
+ - `url` (string): Skyscanner car hire URL
224
+
225
+ **Returns:** Car rental search results
226
+
227
+ **Example URL format:**
228
+ ```
229
+ https://www.skyscanner.net/g/carhire-quotes/GB/en-GB/GBP/30/27544008/27544008/2025-07-01T10:00/2025-08-01T10:00/
230
+ ```
231
+
232
+ ## Types and Enums
233
+
234
+ ### Airport
235
+ ```javascript
236
+ const airport = new Airport(
237
+ "London Heathrow", // title
238
+ "27544008", // entityId
239
+ "LHR" // skyId (IATA code)
240
+ );
241
+ ```
242
+
243
+ ### Location
244
+ ```javascript
245
+ const location = new Location(
246
+ "London", // entityName
247
+ "27544008", // entityId
248
+ "51.5074,-0.1278" // coordinates (lat,lng)
249
+ );
250
+ ```
251
+
252
+ ### CabinClass
253
+ - `CabinClass.ECONOMY`
254
+ - `CabinClass.PREMIUM_ECONOMY`
255
+ - `CabinClass.BUSINESS`
256
+ - `CabinClass.FIRST`
257
+
258
+ ### SpecialTypes
259
+ - `SpecialTypes.ANYTIME` - Flexible date search
260
+ - `SpecialTypes.EVERYWHERE` - Open destination search
261
+
262
+ ## Error Handling
263
+
264
+ The library defines several custom exceptions:
265
+
266
+ ### BannedWithCaptcha
267
+ Raised when Skyscanner blocks requests with CAPTCHA challenges.
268
+
269
+ ```javascript
270
+ try {
271
+ const response = await scanner.getFlightPrices({...});
272
+ } catch (error) {
273
+ if (error instanceof BannedWithCaptcha) {
274
+ console.log(`Blocked by anti-bot measures: ${error.captchaUrl}`);
275
+ // Consider using proxies or reducing request frequency
276
+ }
277
+ }
278
+ ```
279
+
280
+ ### AttemptsExhaustedIncompleteResponse
281
+ Raised when polling retries are exhausted without getting complete results.
282
+
283
+ ```javascript
284
+ try {
285
+ const response = await scanner.getFlightPrices({...});
286
+ } catch (error) {
287
+ if (error instanceof AttemptsExhaustedIncompleteResponse) {
288
+ console.log("Search timed out - try again later");
289
+ }
290
+ }
291
+ ```
292
+
293
+ ### GenericError
294
+ General API errors with status codes and response details.
295
+
296
+ ## Examples
297
+
298
+ ### Basic Flight Search
299
+
300
+ ```javascript
301
+ const { SkyScanner } = require('skyscanner-api');
302
+
303
+ async function searchFlights() {
304
+ const scanner = new SkyScanner();
305
+
306
+ // Find airports
307
+ const londonAirports = await scanner.searchAirports("London");
308
+ const heathrow = londonAirports[0];
309
+
310
+ const nycAirports = await scanner.searchAirports("New York");
311
+ const jfk = nycAirports[0];
312
+
313
+ // Search flights
314
+ const response = await scanner.getFlightPrices({
315
+ origin: heathrow,
316
+ destination: jfk,
317
+ departDate: new Date('2025-09-01'),
318
+ returnDate: new Date('2025-09-08'),
319
+ adults: 1
320
+ });
321
+
322
+ console.log(response.json);
323
+ }
324
+
325
+ searchFlights().catch(console.error);
326
+ ```
327
+
328
+ ### Flexible Search (Anywhere, Anytime)
329
+
330
+ ```javascript
331
+ const { SkyScanner, SpecialTypes } = require('skyscanner-api');
332
+
333
+ async function flexibleSearch() {
334
+ const scanner = new SkyScanner();
335
+ const londonAirports = await scanner.searchAirports("London");
336
+ const heathrow = londonAirports[0];
337
+
338
+ // Search from London to anywhere
339
+ const response = await scanner.getFlightPrices({
340
+ origin: heathrow,
341
+ destination: SpecialTypes.EVERYWHERE,
342
+ departDate: SpecialTypes.ANYTIME
343
+ });
344
+
345
+ console.log(response.json);
346
+ }
347
+
348
+ flexibleSearch().catch(console.error);
349
+ ```
350
+
351
+ ### Car Rental Search
352
+
353
+ ```javascript
354
+ const { SkyScanner } = require('skyscanner-api');
355
+
356
+ async function searchCarRentals() {
357
+ const scanner = new SkyScanner();
358
+
359
+ // Search locations
360
+ const locations = await scanner.searchLocations("London");
361
+ const pickupLocation = locations[0];
362
+
363
+ // Set dates
364
+ const pickupDate = new Date('2025-07-01');
365
+ pickupDate.setHours(10, 0);
366
+
367
+ const returnDate = new Date('2025-07-08');
368
+ returnDate.setHours(10, 0);
369
+
370
+ // Search car rentals
371
+ const rentals = await scanner.getCarRental({
372
+ origin: pickupLocation,
373
+ departTime: pickupDate,
374
+ returnTime: returnDate,
375
+ isDriverOver25: true
376
+ });
377
+
378
+ console.log(rentals);
379
+ }
380
+
381
+ searchCarRentals().catch(console.error);
382
+ ```
383
+
384
+ ## Rate Limiting and Best Practices
385
+
386
+ 1. **Use Proxies**: Consider proxy rotation for high-volume usage
387
+ 2. **Cache Results**: Store airport/location searches to reduce API calls
388
+ 3. **Validate Inputs**: Check dates and passenger counts before API calls
389
+ 4. **Reuse PX Authorization**: X-Px-Authorization header isn't single use. Once you've made one you can use it for multiple requests, but once you get captcha you need to switch IP and authorization
390
+
391
+ ## Dependencies
392
+
393
+ - `axios`: HTTP client for making API requests
394
+ - `https-proxy-agent`: Proxy support for HTTPS requests
395
+
396
+ ## License
397
+
398
+ MIT
399
+
400
+ ## Credits
401
+
402
+ Original Python library by @irrisolto on Discord.
403
+ Node.js port created for broader accessibility.
404
+
405
+ ## Disclaimer
406
+
407
+ This library uses reverse-engineered Skyscanner Android app endpoints. Use at your own risk. The library is not affiliated with, endorsed by, or sponsored by Skyscanner.
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "@mattywhite/skyscanner-api",
3
+ "version": "1.0.0",
4
+ "description": "A Node.js library for querying the reverse-engineered SkyScanner Android app API for flights, airports, locations, and car rentals, with built-in retries and error handling",
5
+ "main": "src/index.js",
6
+ "types": "src/index.d.ts",
7
+ "scripts": {
8
+ "test": "node examples/flight_prices.js"
9
+ },
10
+ "keywords": [
11
+ "skyscanner",
12
+ "flight",
13
+ "api",
14
+ "travel",
15
+ "booking",
16
+ "car-rental",
17
+ "airport",
18
+ "flights"
19
+ ],
20
+ "author": "@irrisolto (ported to Node.js)",
21
+ "license": "MIT",
22
+ "dependencies": {
23
+ "axios": "^1.6.0",
24
+ "https-proxy-agent": "^7.0.2"
25
+ },
26
+ "repository": {
27
+ "type": "git",
28
+ "url": "https://github.com/irrisolto/skyscanner"
29
+ },
30
+ "engines": {
31
+ "node": ">=14.0.0"
32
+ }
33
+ }
package/src/config.js ADDED
@@ -0,0 +1,41 @@
1
+ const path = require('path');
2
+
3
+ // TLS fingerprint
4
+ const JA3 = '771,4865-4866-4867-49195-49196-52393-49199-49200-52392-49171-49172-156-157-47-53,0-23-65281-10-11-35-16-5-13-51-45-43-21,29-23-24,0';
5
+
6
+ const AKAMAI = "4:16777216|16711681|0|m,p,a,s";
7
+
8
+ const EXTRA_FP = {
9
+ tls_signature_algorithms: [
10
+ "ecdsa_secp256r1_sha256",
11
+ "rsa_pss_rsae_sha256",
12
+ "rsa_pkcs1_sha256",
13
+ "ecdsa_secp384r1_sha384",
14
+ "rsa_pss_rsae_sha384",
15
+ "rsa_pkcs1_sha384",
16
+ "rsa_pss_rsae_sha512",
17
+ "rsa_pkcs1_sha512",
18
+ "rsa_pkcs1_sha1"
19
+ ]
20
+ };
21
+
22
+ // Endpoints
23
+ const UNIFIED_SEARCH_ENDPOINT = "https://www.skyscanner.net/g/radar/api/v2/unified-search/";
24
+ const SEARCH_ORIGIN_ENDPOINT = "https://www.skyscanner.net/g/fenryr/v1/inputorigin";
25
+ const ITINERARY_DETAILS_ENDPOINT = 'https://www.skyscanner.net/g/sonar/v3/itinerary/details';
26
+ const LOCATION_SEARCH_ENDPOINT = 'https://www.skyscanner.net/g/autosuggest-search/api/v1/search-car/{market}/{locale}/';
27
+ const CAR_RENTAL_ENDPOINT = 'https://www.skyscanner.net/g/carhire-quotes/{market}/{locale}/{currency}/{driver_age}/{first_location}/{second_location}/{first_date}/{second_date}';
28
+
29
+ const PX_DEVICE_DATA_PATH = path.join(__dirname, 'devicedata.json');
30
+
31
+ module.exports = {
32
+ JA3,
33
+ AKAMAI,
34
+ EXTRA_FP,
35
+ UNIFIED_SEARCH_ENDPOINT,
36
+ SEARCH_ORIGIN_ENDPOINT,
37
+ ITINERARY_DETAILS_ENDPOINT,
38
+ LOCATION_SEARCH_ENDPOINT,
39
+ CAR_RENTAL_ENDPOINT,
40
+ PX_DEVICE_DATA_PATH
41
+ };
@@ -0,0 +1,123 @@
1
+ [
2
+ {
3
+ "height": 2400,
4
+ "width": 1080,
5
+ "pixel_density": 428,
6
+ "model": "Pixel 8",
7
+ "brand": "google",
8
+ "build_device": "shiba",
9
+ "os_version": "15-AP1A.240505.005",
10
+ "cpu_cores": 8,
11
+ "sdk_int": 35
12
+ },
13
+ {
14
+ "height": 3120,
15
+ "width": 1440,
16
+ "pixel_density": 501,
17
+ "model": "SM-S928B",
18
+ "brand": "samsung",
19
+ "build_device": "gts8uq",
20
+ "os_version": "15-UP1A.231005.007.S928BXXU1AXCB",
21
+ "cpu_cores": 8,
22
+ "sdk_int": 35
23
+ },
24
+ {
25
+ "height": 3168,
26
+ "width": 1440,
27
+ "pixel_density": 510,
28
+ "model": "CPH2573",
29
+ "brand": "oneplus",
30
+ "build_device": "pandan",
31
+ "os_version": "15-UKQ1.230804.001",
32
+ "cpu_cores": 8,
33
+ "sdk_int": 35
34
+ },
35
+ {
36
+ "height": 2412,
37
+ "width": 1080,
38
+ "pixel_density": 394,
39
+ "model": "A065",
40
+ "brand": "nothing",
41
+ "build_device": "spacewar",
42
+ "os_version": "15-TKQ1.221013.002",
43
+ "cpu_cores": 8,
44
+ "sdk_int": 35
45
+ },
46
+ {
47
+ "height": 2488,
48
+ "width": 1116,
49
+ "pixel_density": 395,
50
+ "model": "AI2401_A",
51
+ "brand": "asus",
52
+ "build_device": "AI2401",
53
+ "os_version": "15-UP1A.231005.007.33.0820.0820.172",
54
+ "cpu_cores": 8,
55
+ "sdk_int": 35
56
+ },
57
+ {
58
+ "height": 2780,
59
+ "width": 1264,
60
+ "pixel_density": 450,
61
+ "model": "RMX3851",
62
+ "brand": "realme",
63
+ "build_device": "mandrill",
64
+ "os_version": "15-UP1A.231005.007",
65
+ "cpu_cores": 8,
66
+ "sdk_int": 35
67
+ },
68
+ {
69
+ "height": 2400,
70
+ "width": 1080,
71
+ "pixel_density": 402,
72
+ "model": "XT2307-1",
73
+ "brand": "motorola",
74
+ "build_device": "manaus",
75
+ "os_version": "15-UP1A.231005.007",
76
+ "cpu_cores": 8,
77
+ "sdk_int": 35
78
+ },
79
+ {
80
+ "height": 2712,
81
+ "width": 1220,
82
+ "pixel_density": 446,
83
+ "model": "2306EPN60G",
84
+ "brand": "xiaomi",
85
+ "build_device": "aristotle",
86
+ "os_version": "15-UP1A.231005.007",
87
+ "cpu_cores": 8,
88
+ "sdk_int": 35
89
+ },
90
+ {
91
+ "height": 2664,
92
+ "width": 1200,
93
+ "pixel_density": 435,
94
+ "model": "REA-NX9",
95
+ "brand": "honor",
96
+ "build_device": "reehan",
97
+ "os_version": "15-UP1A.231005.007",
98
+ "cpu_cores": 8,
99
+ "sdk_int": 35
100
+ },
101
+ {
102
+ "height": 2412,
103
+ "width": 1080,
104
+ "pixel_density": 394,
105
+ "model": "CPH2521",
106
+ "brand": "oppo",
107
+ "build_device": "PHU110",
108
+ "os_version": "15-UP1A.231005.007",
109
+ "cpu_cores": 8,
110
+ "sdk_int": 35
111
+ },
112
+ {
113
+ "height": 3200,
114
+ "width": 1440,
115
+ "pixel_density": 515,
116
+ "model": "Pixel 8 Pro",
117
+ "brand": "google",
118
+ "build_device": "husky",
119
+ "os_version": "15-UP1A.231005.007.100345",
120
+ "cpu_cores": 8,
121
+ "sdk_int": 35
122
+ }
123
+ ]
package/src/errors.js ADDED
@@ -0,0 +1,36 @@
1
+ class AttemptsExhaustedIncompleteResponse extends Error {
2
+ constructor(message = "All attempts exhausted and response is still incomplete.") {
3
+ super(message);
4
+ this.name = "AttemptsExhaustedIncompleteResponse";
5
+ }
6
+ }
7
+
8
+ class BannedWithCaptcha extends Error {
9
+ constructor(captchaUrl, message = null) {
10
+ const msg = message || `Access banned. CAPTCHA challenge encountered: ${captchaUrl}`;
11
+ super(msg);
12
+ this.name = "BannedWithCaptcha";
13
+ this.captchaUrl = captchaUrl;
14
+ }
15
+ }
16
+
17
+ class PerimeterXError extends Error {
18
+ constructor(message) {
19
+ super(message);
20
+ this.name = "PerimeterXError";
21
+ }
22
+ }
23
+
24
+ class GenericError extends Error {
25
+ constructor(message) {
26
+ super(message);
27
+ this.name = "GenericError";
28
+ }
29
+ }
30
+
31
+ module.exports = {
32
+ AttemptsExhaustedIncompleteResponse,
33
+ BannedWithCaptcha,
34
+ PerimeterXError,
35
+ GenericError
36
+ };
package/src/index.js ADDED
@@ -0,0 +1,20 @@
1
+ const SkyScanner = require('./skyscanner');
2
+ const { Location, Airport, SkyscannerResponse, Coordinates, CabinClass, SpecialTypes } = require('./types');
3
+ const { AttemptsExhaustedIncompleteResponse, BannedWithCaptcha, PerimeterXError, GenericError } = require('./errors');
4
+
5
+ module.exports = {
6
+ SkyScanner,
7
+ Location,
8
+ Airport,
9
+ SkyscannerResponse,
10
+ Coordinates,
11
+ CabinClass,
12
+ SpecialTypes,
13
+ AttemptsExhaustedIncompleteResponse,
14
+ BannedWithCaptcha,
15
+ PerimeterXError,
16
+ GenericError
17
+ };
18
+
19
+ // Default export
20
+ module.exports.default = SkyScanner;