rocket-launch-live-client 1.0.3 → 2.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.
Files changed (42) hide show
  1. package/README.md +105 -61
  2. package/lib/{esm/types/Client.d.ts → Client.d.ts} +6 -8
  3. package/lib/Client.js +61 -0
  4. package/lib/fetcher.d.ts +5 -0
  5. package/lib/fetcher.js +146 -0
  6. package/lib/{cjs/types/index.d.ts → index.d.ts} +0 -1
  7. package/lib/index.js +4 -0
  8. package/lib/{cjs/types/types → types}/application.d.ts +41 -27
  9. package/lib/types/application.js +41 -0
  10. package/lib/types/standards.d.ts +8 -0
  11. package/lib/types/standards.js +314 -0
  12. package/lib/{cjs/types/utils.d.ts → utils.d.ts} +1 -4
  13. package/lib/{esm/utils.js → utils.js} +18 -4
  14. package/lib/{cjs/types → watcher}/Watcher.d.ts +16 -12
  15. package/lib/watcher/Watcher.js +145 -0
  16. package/lib/watcher/index.d.ts +25 -0
  17. package/lib/watcher/index.js +3 -0
  18. package/package.json +30 -26
  19. package/lib/cjs/Client.js +0 -233
  20. package/lib/cjs/Watcher.js +0 -172
  21. package/lib/cjs/fetcher.js +0 -61
  22. package/lib/cjs/index.js +0 -30
  23. package/lib/cjs/package.json +0 -3
  24. package/lib/cjs/types/Client.d.ts +0 -189
  25. package/lib/cjs/types/application.js +0 -46
  26. package/lib/cjs/types/fetcher.d.ts +0 -2
  27. package/lib/cjs/types/standards.js +0 -321
  28. package/lib/cjs/types/types/standards.d.ts +0 -316
  29. package/lib/cjs/utils.js +0 -266
  30. package/lib/esm/Client.js +0 -229
  31. package/lib/esm/Watcher.js +0 -168
  32. package/lib/esm/fetcher.js +0 -54
  33. package/lib/esm/index.mjs +0 -22
  34. package/lib/esm/package.json +0 -3
  35. package/lib/esm/types/Watcher.d.ts +0 -81
  36. package/lib/esm/types/application.js +0 -43
  37. package/lib/esm/types/fetcher.d.ts +0 -2
  38. package/lib/esm/types/index.d.ts +0 -24
  39. package/lib/esm/types/standards.js +0 -316
  40. package/lib/esm/types/types/application.d.ts +0 -206
  41. package/lib/esm/types/types/standards.d.ts +0 -316
  42. package/lib/esm/types/utils.d.ts +0 -12
package/README.md CHANGED
@@ -3,6 +3,7 @@
3
3
  ## Table of Contents
4
4
 
5
5
  - [Requirements](#reqs)
6
+ - [Install](#install)
6
7
  - [Simple Usage](#simple)
7
8
  - [Client Configuration](#config)
8
9
  - [Endpoints](#endpoints)
@@ -15,33 +16,46 @@
15
16
  - [Tags](#tags)
16
17
  - [Vehicles](#vehicles)
17
18
  - [Watcher](#watcher)
18
- - [Methods and Properties](#watcher_props)
19
19
  - [Options](#watcher_options)
20
+ - [Methods and Properties](#watcher_props)
20
21
  - [Events](#watcher_events)
21
22
 
22
- This package is a fully-typed, promise-based, zero-dependency Node.JS JavaScript/TypeScript library for interacting with the [RocketLaunch.Live](https://www.rocketlaunch.live) API.
23
+ This package is a fully-typed, promise-based, zero-dependency Node.js JavaScript/TypeScript library for interacting with the [RocketLaunch.Live](https://www.rocketlaunch.live) API.
23
24
 
24
25
  <a name="reqs"></a>
25
26
 
26
27
  ## Requirements
27
28
 
28
- This package is tested on and supports Node 14.18 or higher. It is fully CommonJS/ESM compatible and has Typescript support built in.
29
+ This package requires Node **20.19.0** or higher. It ships as an ESM-only package with built-in TypeScript types.
30
+
31
+ Use `import` as the primary entry path. On supported Node versions, `require()` also works via Node's [`require(esm)`](https://nodejs.org/api/modules.md#loading-ecmascript-modules-using-require) support.
32
+
33
+ Package entry points:
34
+
35
+ | Import | Purpose |
36
+ | --- | --- |
37
+ | `rocket-launch-live-client` | REST client (`rllc`, types) |
38
+ | `rocket-launch-live-client/watcher` | Launch Watcher (`watch`, `RLLWatcher`) |
39
+
40
+ <a name="install"></a>
41
+
42
+ ## Install
43
+
44
+ ```bash
45
+ npm install rocket-launch-live-client
46
+ ```
29
47
 
30
48
  <a name="simple"></a>
31
49
 
32
50
  ## Simple usage
33
51
 
34
52
  ```js
35
- // Import package
36
53
  import { rllc } from "rocket-launch-live-client";
37
54
 
38
- // Get API Your Key
39
- const RLL_API_KEY = process.env.RLL_API_KEY;
55
+ const API_KEY = process.env.RLL_API_KEY;
40
56
 
41
- // Create client
42
- const client = rllc(RLL_API_KEY);
57
+ const client = rllc(API_KEY);
43
58
 
44
- // Call endpoints
45
59
  const launches = await client.launches();
46
60
  ```
47
61
 
@@ -51,19 +65,27 @@ const launches = await client.launches();
51
65
 
52
66
  RLL Clients require an API key as the first parameter, and will throw if one is not present.
53
67
 
54
- The client can be configured with an optional second parameter using the following keys:
68
+ The client can be configured with an optional second parameter:
55
69
 
56
70
  ```js
57
71
  const options = {
58
72
  // Defaults to false.
59
- // API Key is normally passed in the Authorization Bearer header
60
- // Set to true to pass your API key as a query parameter instead (not recommended)
73
+ // API Key is normally passed in the Authorization Bearer header.
74
+ // Set to true to pass your API key as a query parameter instead (not recommended).
61
75
  keyInQueryParams: true,
76
+ // Defaults to 30000 (30 seconds).
77
+ // Abort in-flight HTTP requests that exceed this duration.
78
+ timeoutMs: 15000,
79
+ // Defaults to 10 MiB (10485760 bytes).
80
+ // Reject responses whose decompressed body exceeds this size (also stops gzip bombs).
81
+ maxResponseBytes: 5 * 1024 * 1024,
62
82
  };
63
83
 
64
- const client = rllc(RLL_API_KEY, options);
84
+ const client = rllc(API_KEY, options);
65
85
  ```
66
86
 
87
+ Requests send `Accept-Encoding: gzip` and transparently decode gzip responses from the API.
88
+
67
89
  <a name="endpoints"></a>
68
90
 
69
91
  ## Endpoints
@@ -89,13 +111,13 @@ const response = await client.launches({ page: 2 });
89
111
  // Also accepts page number as a number-parseable string like "2"
90
112
  ```
91
113
 
92
- For complete API Documentation on parameters, be sure to visit [RocketLaunch.Live](https://www.rocketlaunch.live/api)
114
+ For complete API documentation on parameters, visit [RocketLaunch.Live](https://www.rocketlaunch.live/api).
93
115
 
94
116
  <a name="types"></a>
95
117
 
96
118
  ### Response Types
97
119
 
98
- All entity response types are browseable in the [GitHub Repository](https://github.com/mendahu/rocket-launch-live-client/tree/main/src/types).
120
+ Entity response types are exported from the package (`RLLEntity`, `RLLResponse`, `RLLError`, `RLLQueryConfig`, and others) and are browseable in the [GitHub repository](https://github.com/mendahu/rocket-launch-live-client/tree/main/src/types).
99
121
 
100
122
  <a name="companies"></a>
101
123
 
@@ -110,9 +132,12 @@ Optional search parameters:
110
132
  ```js
111
133
  const options = {
112
134
  // Company numeric id
113
- // Also accepts number parseable strings like "1"
135
+ // Also accepts number-parseable strings like "1"
114
136
  id: 1,
115
137
 
138
+ // Page number of results
139
+ page: 1,
140
+
116
141
  // Company name
117
142
  name: "SpaceX",
118
143
 
@@ -138,9 +163,12 @@ Optional search parameters:
138
163
  ```js
139
164
  const options = {
140
165
  // Launch numeric id
141
- // Also accepts number parseable strings like "1"
166
+ // Also accepts number-parseable strings like "1"
142
167
  id: 1,
143
168
 
169
+ // Page number of results
170
+ page: 1,
171
+
144
172
  // COSPAR
145
173
  // Format YYYY-NNN
146
174
  cospar_id: "2022-123",
@@ -162,23 +190,23 @@ const options = {
162
190
  modified_since: new Date("2023-01-31T06:00:00Z"),
163
191
 
164
192
  // Launch location id
165
- // Also accepts number parseable strings like "1"
193
+ // Also accepts number-parseable strings like "1"
166
194
  location_id: 1,
167
195
 
168
196
  // Launch pad id
169
- // Also accepts number parseable strings like "1"
197
+ // Also accepts number-parseable strings like "1"
170
198
  pad_id: 1,
171
199
 
172
200
  // Launch provider id
173
- // Also accepts number parseable strings like "1"
201
+ // Also accepts number-parseable strings like "1"
174
202
  provider_id: 1,
175
203
 
176
204
  // Launch tag id
177
- // Also accepts number parseable strings like "1"
205
+ // Also accepts number-parseable strings like "1"
178
206
  tag_id: 1,
179
207
 
180
208
  // Launch vehicle id
181
- // Also accepts number parseable strings like "1"
209
+ // Also accepts number-parseable strings like "1"
182
210
  vehicle_id: 1,
183
211
 
184
212
  // US State
@@ -198,8 +226,8 @@ const options = {
198
226
 
199
227
  // Limit amount of launches returned
200
228
  // Must be between 1 and 25
201
- // Also accepts number parseable strings like "10"
202
- // Note: this param is safely ignored when using in conjuction with a Watcher (see below)
229
+ // Also accepts number-parseable strings like "10"
230
+ // Note: this param is ignored when using the Watcher (see below)
203
231
  limit: 10,
204
232
 
205
233
  // Sort order (by date) of results
@@ -222,9 +250,12 @@ Optional search parameters:
222
250
  ```js
223
251
  const options = {
224
252
  // Location numeric id
225
- // Also accepts number parseable strings like "1"
253
+ // Also accepts number-parseable strings like "1"
226
254
  id: 1,
227
255
 
256
+ // Page number of results
257
+ page: 1,
258
+
228
259
  // Location name
229
260
  name: "Cape Canaveral",
230
261
 
@@ -251,9 +282,12 @@ Optional search parameters:
251
282
  ```js
252
283
  const options = {
253
284
  // Mission numeric id
254
- // Also accepts number parseable strings like "1"
285
+ // Also accepts number-parseable strings like "1"
255
286
  id: 1,
256
287
 
288
+ // Page number of results
289
+ page: 1,
290
+
257
291
  // Mission name
258
292
  name: "Juno",
259
293
  };
@@ -272,9 +306,12 @@ Optional search parameters:
272
306
  ```js
273
307
  const options = {
274
308
  // Pad numeric id
275
- // Also accepts number parseable strings like "1"
309
+ // Also accepts number-parseable strings like "1"
276
310
  id: 1,
277
311
 
312
+ // Page number of results
313
+ page: 1,
314
+
278
315
  // Pad name
279
316
  name: "SpaceX",
280
317
 
@@ -301,9 +338,12 @@ Optional search parameters:
301
338
  ```js
302
339
  const options = {
303
340
  // Tag numeric id
304
- // Also accepts number parseable strings like "1"
341
+ // Also accepts number-parseable strings like "1"
305
342
  id: 1,
306
343
 
344
+ // Page number of results
345
+ page: 1,
346
+
307
347
  // Tag text
308
348
  text: "Crewed",
309
349
  };
@@ -322,9 +362,12 @@ Optional search parameters:
322
362
  ```js
323
363
  const options = {
324
364
  // Vehicle numeric id
325
- // Also accepts number parseable strings like "1"
365
+ // Also accepts number-parseable strings like "1"
326
366
  id: 1,
327
367
 
368
+ // Page number of results
369
+ page: 1,
370
+
328
371
  // Vehicle name
329
372
  name: "Atlas V",
330
373
  };
@@ -334,12 +377,17 @@ const options = {
334
377
 
335
378
  ## Watcher
336
379
 
337
- The `rocket-launch-live-client` has the ability to monitor the `launches` endpoint on a regular basis and return changes as they happen live.
380
+ The Watcher monitors the `launches` endpoint on a regular basis and emits changes as they happen. Import it from the `./watcher` subpath so applications that only need the REST client do not load Watcher code.
338
381
 
339
382
  ```js
383
+ import { rllc } from "rocket-launch-live-client";
384
+ import { watch } from "rocket-launch-live-client/watcher";
385
+
386
+ const client = rllc(process.env.RLL_API_KEY);
387
+
340
388
  // Instantiate a new watcher
341
389
  // See below for options
342
- const watcher = client.watch(5, options);
390
+ const watcher = watch(client, 5, options);
343
391
 
344
392
  // Define event handlers
345
393
  watcher.on("new", (newLaunch) => {
@@ -353,32 +401,35 @@ watcher.start();
353
401
  watcher.stop();
354
402
  ```
355
403
 
356
- <a name="watcher_props"></a>
404
+ You can also import the `RLLWatcher` class from the same subpath if you need the type or class directly.
357
405
 
358
406
  <a name="watcher_options"></a>
359
407
 
360
408
  ### Watcher Options
361
409
 
362
- A new watcher takes up to two arguments:
410
+ `watch()` takes up to three arguments:
363
411
 
364
- 1. Interval - (optional) (default: 5) - a duration, in minutes, between calls to the API. Adjust this based on the frequency you wish to stay up to date. To avoid needlessly querying the API, this client will now allow any option less than 1 minute.
365
- 2. Query Options - (optional) - The exact same query options that can be submitted to the [`launches`](#launches) endpoint. _NOTE:_ the "limit" param is ignored on the `watcher`.
412
+ 1. Client - (required) - an `RLLClient` instance from `rllc()`
413
+ 2. Interval - (optional) (default: 5) - a duration, in minutes, between calls to the API. Adjust this based on the frequency you wish to stay up to date. To avoid needlessly querying the API, this client will not allow any option less than 1 minute.
414
+ 3. Query Options - (optional) - The exact same query options that can be submitted to the [`launches`](#launches) endpoint. _NOTE:_ the `limit` and `page` params are ignored on the Watcher (it always builds the cache from page 1 across all matching pages).
366
415
 
367
- Query options cannot be altered on a running watcher. In order to change your search conditions, you'll need to stop the watcher and start a new one.
416
+ On start, the Watcher pages through every matching result to build an in-memory cache, then polls with `modified_since` on the configured interval. Query options cannot be altered on a running watcher stop it and create a new one to change search conditions.
417
+
418
+ <a name="watcher_props"></a>
368
419
 
369
420
  ### Watcher Methods and Properties
370
421
 
371
422
  #### Start
372
423
 
373
- Begin watching the `launches` endpoint using the interval and query options provided during watcher instantiation.
424
+ Begin watching the `launches` endpoint using the interval and query options provided during watcher instantiation. Calling `start()` again while already running is a no-op.
374
425
 
375
426
  ```js
376
427
  watcher.start();
377
428
  ```
378
429
 
379
- #### End
430
+ #### Stop
380
431
 
381
- Stop watching the `launches` endpoint.
432
+ Stop watching the `launches` endpoint. Clears the interval timer so polling ends. You can call `start()` again afterward to rebuild the cache and resume.
382
433
 
383
434
  ```js
384
435
  watcher.stop();
@@ -401,7 +452,7 @@ Access the launches data cache. The data is stored in a [JavaScript Map](https:/
401
452
  ```js
402
453
  watcher.launches; // Map of all launches in cache
403
454
  watcher.launches.size // Count of launches in cache
404
- watcher.launches.get(1) // Get launch with launch_id of 1
455
+ watcher.launches.get(1) // Get launch with launch id of 1
405
456
  watcher.launches.forEach((launch, launchId) => /* Do something to each launch */ )
406
457
  ```
407
458
 
@@ -411,10 +462,12 @@ Note: We recommend not altering the `launches` cache directly (such as by using
411
462
 
412
463
  ### Watcher Events
413
464
 
414
- Watcher events are triggered when the client recieves a response to a query to `launches` using the `modified_since` parameter. The client will compare the changes to a cached version of the launch and trigger the appropriate event.
465
+ Watcher events are triggered when the client receives a response to a query to `launches` using the `modified_since` parameter. The client will compare the changes to a cached version of the launch and trigger the appropriate event.
415
466
 
416
467
  If there are multiple changes on a single API call, the appropriate events will be triggered more than once, so have your callbacks handle a single event.
417
468
 
469
+ Interval ticks that fire while a previous poll is still in flight are skipped, so slow or multi-page polls do not stack concurrent requests.
470
+
418
471
  <a name="watcher_events_new"></a>
419
472
 
420
473
  #### New
@@ -455,15 +508,15 @@ watcher.on("error", (err) => {
455
508
  });
456
509
  ```
457
510
 
458
- The `err` object will have the following shape, and is accessible via TypeScript as `RLLError`:
511
+ The `err` object has the following shape, and is accessible via TypeScript as `RLLError`:
459
512
 
460
- ```js
461
- const err = {
462
- error: "Error title";
463
- statusCode: 404; // HTTP status code or null if no response
464
- message: "Could not find this resource"; // Custom error string from RLLC
465
- server_response: { } // error passed through from server, can be null if no response
466
- }
513
+ ```ts
514
+ type RLLError = {
515
+ error: string;
516
+ statusCode: number | null; // HTTP status code, or null if no response
517
+ message: string; // Custom error string from RLLC
518
+ server_response: string | null; // Server body, or null if no response
519
+ };
467
520
  ```
468
521
 
469
522
  #### Ready
@@ -478,7 +531,7 @@ watcher.on("ready", (launches) => {
478
531
 
479
532
  #### Initialization Errors
480
533
 
481
- The watcher has experienced a problem setting up its initial cache and is has not started monitoring.
534
+ The watcher experienced a problem setting up its initial cache and has not started monitoring.
482
535
 
483
536
  ```js
484
537
  watcher.on("init_error", (err) => {
@@ -486,20 +539,11 @@ watcher.on("init_error", (err) => {
486
539
  });
487
540
  ```
488
541
 
489
- The `err` object will have the following shape, and is accessible via TypeScript as `RLLError`:
490
-
491
- ```js
492
- const err = {
493
- error: "Error title";
494
- statusCode: 404; // HTTP status code or null if no response
495
- message: "Could not find this resource"; // Custom error string from RLLC
496
- server_response: { } // error passed through from server, can be null if no response
497
- }
498
- ```
542
+ The `err` object is the same `RLLError` shape as the `error` event above.
499
543
 
500
544
  #### Call
501
545
 
502
- The watcher also emits a `call` event every time it makes a request, passing in the query parameters it used in the Node URLSearchParams format. Use this to monitor or diagnose how often the API is being queried.
546
+ The watcher also emits a `call` event every time it makes a request, passing in the query parameters it used in the Node `URLSearchParams` format. Use this to monitor or diagnose how often the API is being queried.
503
547
 
504
548
  ```js
505
549
  watcher.on("call", (params) => {
@@ -1,5 +1,4 @@
1
1
  import { RLLClientOptions, RLLEntity, RLLQueryConfig, RLLResponse } from "./types/application.js";
2
- import { RLLWatcher } from "./Watcher.js";
3
2
  /**
4
3
  * Class representing a RocketLaunch.Live client
5
4
  * @class
@@ -13,6 +12,8 @@ export declare class RLLClient {
13
12
  * @param {string} apiKey - Your RocketLaunch.Live API Key
14
13
  * @param {Object} [options] - Optional Client Configuration options
15
14
  * @param {boolean} options.keyInQueryParams - Set to true to send your API Key via Query parameters instead of Authorization Header (not recommended)
15
+ * @param {number} options.timeoutMs - HTTP request timeout in milliseconds (default 30000)
16
+ * @param {number} options.maxResponseBytes - Max decompressed response body size in bytes (default 10 MiB)
16
17
  *
17
18
  */
18
19
  constructor(apiKey: string, options?: RLLClientOptions);
@@ -30,16 +31,13 @@ export declare class RLLClient {
30
31
  */
31
32
  private query;
32
33
  /**
33
- * Instantiate a new RLL Watcher which will continually query the API for changes to the launches endpoint
34
+ * Execute a launches query with pre-built search params.
35
+ * Prefer {@link RLLClient.launches} for normal use; this exists for the
36
+ * `rocket-launch-live-client/watcher` subpath.
34
37
  *
35
38
  * @public
36
- *
37
- * @param {number} interval - Interval in minutes to query the API for changes. Defaults to 5 minutes, cannot be less than 1 minute
38
- * @param {RLLQueryConfig.Launches} options - Query options, same as calling the launches method
39
- *
40
- * @returns {RLLWatcher}
41
39
  */
42
- watch(interval?: number | string, options?: RLLQueryConfig.Launches): RLLWatcher;
40
+ queryLaunches(params: URLSearchParams): Promise<RLLResponse<RLLEntity.Launch[]>>;
43
41
  /**
44
42
  * Fetch launch companies
45
43
  *
package/lib/Client.js ADDED
@@ -0,0 +1,61 @@
1
+ import { RLLEndPoint, } from "./types/application.js";
2
+ import { fetcher, DEFAULT_REQUEST_TIMEOUT_MS, DEFAULT_MAX_RESPONSE_BYTES, } from "./fetcher.js";
3
+ import { apiKeyValidator, optionsValidator, queryOptionsValidator, } from "./utils.js";
4
+ export class RLLClient {
5
+ apiKey;
6
+ config = {
7
+ keyInQueryParams: false,
8
+ timeoutMs: DEFAULT_REQUEST_TIMEOUT_MS,
9
+ maxResponseBytes: DEFAULT_MAX_RESPONSE_BYTES,
10
+ };
11
+ constructor(apiKey, options) {
12
+ this.apiKey = apiKeyValidator(apiKey);
13
+ if (!options) {
14
+ return;
15
+ }
16
+ optionsValidator(options);
17
+ if (options.keyInQueryParams) {
18
+ this.config.keyInQueryParams = options.keyInQueryParams;
19
+ }
20
+ if (options.timeoutMs !== undefined) {
21
+ this.config.timeoutMs = options.timeoutMs;
22
+ }
23
+ if (options.maxResponseBytes !== undefined) {
24
+ this.config.maxResponseBytes = options.maxResponseBytes;
25
+ }
26
+ }
27
+ query(endpoint, params) {
28
+ return fetcher(this.apiKey, endpoint, params, this.config.keyInQueryParams, this.config.timeoutMs, this.config.maxResponseBytes);
29
+ }
30
+ queryLaunches(params) {
31
+ return this.query(RLLEndPoint.LAUNCHES, params);
32
+ }
33
+ companies(options) {
34
+ const params = queryOptionsValidator(RLLEndPoint.COMPANIES, options);
35
+ return this.query(RLLEndPoint.COMPANIES, params);
36
+ }
37
+ launches(options) {
38
+ const params = queryOptionsValidator(RLLEndPoint.LAUNCHES, options);
39
+ return this.query(RLLEndPoint.LAUNCHES, params);
40
+ }
41
+ locations(options) {
42
+ const params = queryOptionsValidator(RLLEndPoint.LOCATIONS, options);
43
+ return this.query(RLLEndPoint.LOCATIONS, params);
44
+ }
45
+ missions(options) {
46
+ const params = queryOptionsValidator(RLLEndPoint.MISSIONS, options);
47
+ return this.query(RLLEndPoint.MISSIONS, params);
48
+ }
49
+ pads(options) {
50
+ const params = queryOptionsValidator(RLLEndPoint.PADS, options);
51
+ return this.query(RLLEndPoint.PADS, params);
52
+ }
53
+ tags(options) {
54
+ const params = queryOptionsValidator(RLLEndPoint.TAGS, options);
55
+ return this.query(RLLEndPoint.TAGS, params);
56
+ }
57
+ vehicles(options) {
58
+ const params = queryOptionsValidator(RLLEndPoint.VEHICLES, options);
59
+ return this.query(RLLEndPoint.VEHICLES, params);
60
+ }
61
+ }
@@ -0,0 +1,5 @@
1
+ /** Default HTTP request timeout (30 seconds). */
2
+ export declare const DEFAULT_REQUEST_TIMEOUT_MS = 30000;
3
+ /** Default max decompressed response body size (10 MiB). */
4
+ export declare const DEFAULT_MAX_RESPONSE_BYTES: number;
5
+ export declare const fetcher: <T>(apiKey: string, endpoint: string, params: URLSearchParams, keyInQueryParams: boolean, timeoutMs?: number, maxResponseBytes?: number) => Promise<T>;
package/lib/fetcher.js ADDED
@@ -0,0 +1,146 @@
1
+ import https from "node:https";
2
+ import { createGunzip } from "node:zlib";
3
+ const BASE_URL = "https://fdo.rocketlaunch.live";
4
+ export const DEFAULT_REQUEST_TIMEOUT_MS = 30_000;
5
+ export const DEFAULT_MAX_RESPONSE_BYTES = 10 * 1024 * 1024;
6
+ const RESPONSE_TOO_LARGE = "ERR_RESPONSE_TOO_LARGE";
7
+ export const fetcher = (apiKey, endpoint, params, keyInQueryParams, timeoutMs = DEFAULT_REQUEST_TIMEOUT_MS, maxResponseBytes = DEFAULT_MAX_RESPONSE_BYTES) => {
8
+ const url = new URL("json/" + endpoint, BASE_URL);
9
+ const headers = {
10
+ "accept-encoding": "gzip",
11
+ };
12
+ if (keyInQueryParams) {
13
+ params.set("key", apiKey);
14
+ }
15
+ else {
16
+ headers.authorization = `Bearer ${apiKey}`;
17
+ }
18
+ params.forEach((v, k) => url.searchParams.set(k, v));
19
+ return query(url, headers, timeoutMs, maxResponseBytes);
20
+ };
21
+ const readResponseBody = (res, maxBytes) => {
22
+ return new Promise((resolve, reject) => {
23
+ const encoding = res.headers["content-encoding"];
24
+ const isGzip = encoding === "gzip" || encoding === "x-gzip";
25
+ const contentLength = Number(res.headers["content-length"]);
26
+ if (Number.isFinite(contentLength) && contentLength > maxBytes) {
27
+ res.destroy();
28
+ const err = new Error(`RLLC response exceeded the ${maxBytes}-byte size limit.`);
29
+ err.code = RESPONSE_TOO_LARGE;
30
+ reject(err);
31
+ return;
32
+ }
33
+ const stream = isGzip ? res.pipe(createGunzip()) : res;
34
+ const chunks = [];
35
+ let total = 0;
36
+ let settled = false;
37
+ const fail = (err) => {
38
+ if (settled) {
39
+ return;
40
+ }
41
+ settled = true;
42
+ stream.destroy();
43
+ res.destroy();
44
+ reject(err);
45
+ };
46
+ stream.on("data", (chunk) => {
47
+ total += chunk.length;
48
+ if (total > maxBytes) {
49
+ const err = new Error(`RLLC response exceeded the ${maxBytes}-byte size limit.`);
50
+ err.code = RESPONSE_TOO_LARGE;
51
+ fail(err);
52
+ return;
53
+ }
54
+ chunks.push(chunk);
55
+ });
56
+ stream.on("error", (err) => {
57
+ fail(err);
58
+ });
59
+ stream.on("end", () => {
60
+ if (settled) {
61
+ return;
62
+ }
63
+ settled = true;
64
+ resolve(Buffer.concat(chunks).toString());
65
+ });
66
+ });
67
+ };
68
+ const query = (url, headers, timeoutMs, maxResponseBytes) => {
69
+ return new Promise((resolve, reject) => {
70
+ const controller = new AbortController();
71
+ let settled = false;
72
+ const finish = (action) => {
73
+ if (settled) {
74
+ return;
75
+ }
76
+ settled = true;
77
+ clearTimeout(timeout);
78
+ action();
79
+ };
80
+ const timeout = setTimeout(() => {
81
+ controller.abort();
82
+ }, timeoutMs);
83
+ const req = https.get(url, { headers, signal: controller.signal }, (res) => {
84
+ readResponseBody(res, maxResponseBytes)
85
+ .then((response) => {
86
+ let server_response;
87
+ try {
88
+ server_response = JSON.parse(response);
89
+ }
90
+ catch {
91
+ server_response = response;
92
+ }
93
+ if (res.statusCode === 200) {
94
+ finish(() => resolve(server_response));
95
+ }
96
+ else {
97
+ const error = {
98
+ error: "API Call Failed",
99
+ statusCode: res.statusCode ?? null,
100
+ message: "RLLC recieved a response from the server but it did not complete as expected.",
101
+ server_response,
102
+ };
103
+ finish(() => reject(error));
104
+ }
105
+ })
106
+ .catch((err) => {
107
+ if (err?.code === RESPONSE_TOO_LARGE) {
108
+ const error = {
109
+ error: "Response too large",
110
+ statusCode: res.statusCode ?? null,
111
+ message: err.message,
112
+ server_response: null,
113
+ };
114
+ finish(() => reject(error));
115
+ return;
116
+ }
117
+ const error = {
118
+ error: "Unknown error",
119
+ statusCode: null,
120
+ message: "RLLC recieved an unknown error. This usually means that the HTTP request did not complete",
121
+ server_response: null,
122
+ };
123
+ finish(() => reject(error));
124
+ });
125
+ });
126
+ req.on("error", () => {
127
+ if (controller.signal.aborted) {
128
+ const error = {
129
+ error: "Timeout",
130
+ statusCode: null,
131
+ message: `RLLC request timed out after ${timeoutMs}ms.`,
132
+ server_response: null,
133
+ };
134
+ finish(() => reject(error));
135
+ return;
136
+ }
137
+ const error = {
138
+ error: "Unknown error",
139
+ statusCode: null,
140
+ message: "RLLC recieved an unknown error. This usually means that the HTTP request did not complete",
141
+ server_response: null,
142
+ };
143
+ finish(() => reject(error));
144
+ });
145
+ });
146
+ };
@@ -3,7 +3,6 @@ import { RLLClientOptions } from "./types/application.js";
3
3
  export { RLLEndPoint, RLLResponse, RLLClientOptions, RLLQueryConfig, RLLEntity, RLLError, } from "./types/application.js";
4
4
  export { ISO3166Alpha2 } from "./types/standards.js";
5
5
  export { RLLClient } from "./Client.js";
6
- export { RLLWatcher } from "./Watcher.js";
7
6
  /**
8
7
  * Generate a RocketLaunch.Live client
9
8
  *
package/lib/index.js ADDED
@@ -0,0 +1,4 @@
1
+ import { RLLClient } from "./Client.js";
2
+ export { RLLEndPoint, RLLEntity, } from "./types/application.js";
3
+ export { RLLClient } from "./Client.js";
4
+ export const rllc = (apiKey, options) => new RLLClient(apiKey, options);