rocket-launch-live-client 1.0.4 → 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.
- package/README.md +105 -61
- package/lib/{esm/types/Client.d.ts → Client.d.ts} +6 -8
- package/lib/Client.js +61 -0
- package/lib/fetcher.d.ts +5 -0
- package/lib/fetcher.js +146 -0
- package/lib/{cjs/types/index.d.ts → index.d.ts} +0 -1
- package/lib/index.js +4 -0
- package/lib/{cjs/types/types → types}/application.d.ts +41 -27
- package/lib/types/application.js +41 -0
- package/lib/types/standards.d.ts +8 -0
- package/lib/types/standards.js +314 -0
- package/lib/{cjs/types/utils.d.ts → utils.d.ts} +1 -4
- package/lib/{esm/utils.js → utils.js} +18 -4
- package/lib/{cjs/types → watcher}/Watcher.d.ts +16 -12
- package/lib/watcher/Watcher.js +145 -0
- package/lib/watcher/index.d.ts +25 -0
- package/lib/watcher/index.js +3 -0
- package/package.json +30 -27
- package/lib/cjs/Client.js +0 -233
- package/lib/cjs/Watcher.js +0 -172
- package/lib/cjs/fetcher.js +0 -61
- package/lib/cjs/index.js +0 -30
- package/lib/cjs/package.json +0 -3
- package/lib/cjs/types/Client.d.ts +0 -189
- package/lib/cjs/types/application.js +0 -46
- package/lib/cjs/types/fetcher.d.ts +0 -2
- package/lib/cjs/types/standards.js +0 -321
- package/lib/cjs/types/types/standards.d.ts +0 -316
- package/lib/cjs/utils.js +0 -266
- package/lib/esm/Client.js +0 -229
- package/lib/esm/Watcher.js +0 -168
- package/lib/esm/fetcher.js +0 -54
- package/lib/esm/index.mjs +0 -22
- package/lib/esm/package.json +0 -3
- package/lib/esm/types/Watcher.d.ts +0 -81
- package/lib/esm/types/application.js +0 -43
- package/lib/esm/types/fetcher.d.ts +0 -2
- package/lib/esm/types/index.d.ts +0 -24
- package/lib/esm/types/standards.js +0 -316
- package/lib/esm/types/types/application.d.ts +0 -206
- package/lib/esm/types/types/standards.d.ts +0 -316
- 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.
|
|
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
|
|
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
|
-
|
|
39
|
-
const RLL_API_KEY = process.env.RLL_API_KEY;
|
|
55
|
+
const API_KEY = process.env.RLL_API_KEY;
|
|
40
56
|
|
|
41
|
-
|
|
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
|
|
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(
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
193
|
+
// Also accepts number-parseable strings like "1"
|
|
166
194
|
location_id: 1,
|
|
167
195
|
|
|
168
196
|
// Launch pad id
|
|
169
|
-
// Also accepts number
|
|
197
|
+
// Also accepts number-parseable strings like "1"
|
|
170
198
|
pad_id: 1,
|
|
171
199
|
|
|
172
200
|
// Launch provider id
|
|
173
|
-
// Also accepts number
|
|
201
|
+
// Also accepts number-parseable strings like "1"
|
|
174
202
|
provider_id: 1,
|
|
175
203
|
|
|
176
204
|
// Launch tag id
|
|
177
|
-
// Also accepts number
|
|
205
|
+
// Also accepts number-parseable strings like "1"
|
|
178
206
|
tag_id: 1,
|
|
179
207
|
|
|
180
208
|
// Launch vehicle id
|
|
181
|
-
// Also accepts number
|
|
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
|
|
202
|
-
// Note: this param is
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
410
|
+
`watch()` takes up to three arguments:
|
|
363
411
|
|
|
364
|
-
1.
|
|
365
|
-
2.
|
|
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
|
-
|
|
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
|
-
####
|
|
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
|
|
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
|
|
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
|
|
511
|
+
The `err` object has the following shape, and is accessible via TypeScript as `RLLError`:
|
|
459
512
|
|
|
460
|
-
```
|
|
461
|
-
|
|
462
|
-
error:
|
|
463
|
-
statusCode:
|
|
464
|
-
message:
|
|
465
|
-
server_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
|
|
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
|
|
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
|
-
*
|
|
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
|
-
|
|
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
|
+
}
|
package/lib/fetcher.d.ts
ADDED
|
@@ -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