@squawk/adsbtop 0.2.0 → 0.3.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 +14 -2
- package/package.json +6 -6
- package/dist/aircraft-state.d.ts +0 -77
- package/dist/aircraft-state.d.ts.map +0 -1
- package/dist/aircraft-state.js +0 -87
- package/dist/app.d.ts +0 -41
- package/dist/app.d.ts.map +0 -1
- package/dist/app.js +0 -166
- package/dist/cli-args.d.ts +0 -43
- package/dist/cli-args.d.ts.map +0 -1
- package/dist/cli-args.js +0 -150
- package/dist/cli.d.ts +0 -3
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js +0 -17
- package/dist/columns.d.ts +0 -84
- package/dist/columns.d.ts.map +0 -1
- package/dist/columns.js +0 -203
- package/dist/components/aircraft-table.d.ts +0 -27
- package/dist/components/aircraft-table.d.ts.map +0 -1
- package/dist/components/aircraft-table.js +0 -83
- package/dist/components/detail-view.d.ts +0 -20
- package/dist/components/detail-view.d.ts.map +0 -1
- package/dist/components/detail-view.js +0 -14
- package/dist/components/help-overlay.d.ts +0 -4
- package/dist/components/help-overlay.d.ts.map +0 -1
- package/dist/components/help-overlay.js +0 -22
- package/dist/components/hotkey-bar.d.ts +0 -21
- package/dist/components/hotkey-bar.d.ts.map +0 -1
- package/dist/components/hotkey-bar.js +0 -28
- package/dist/components/messages-panel.d.ts +0 -27
- package/dist/components/messages-panel.d.ts.map +0 -1
- package/dist/components/messages-panel.js +0 -17
- package/dist/components/search-bar.d.ts +0 -19
- package/dist/components/search-bar.d.ts.map +0 -1
- package/dist/components/search-bar.js +0 -13
- package/dist/components/status-header.d.ts +0 -17
- package/dist/components/status-header.d.ts.map +0 -1
- package/dist/components/status-header.js +0 -14
- package/dist/create-feed.d.ts +0 -33
- package/dist/create-feed.d.ts.map +0 -1
- package/dist/create-feed.js +0 -40
- package/dist/detail-fields.d.ts +0 -23
- package/dist/detail-fields.d.ts.map +0 -1
- package/dist/detail-fields.js +0 -68
- package/dist/format.d.ts +0 -91
- package/dist/format.d.ts.map +0 -1
- package/dist/format.js +0 -138
- package/dist/location.d.ts +0 -20
- package/dist/location.d.ts.map +0 -1
- package/dist/location.js +0 -29
- package/dist/registration-cache.d.ts +0 -23
- package/dist/registration-cache.d.ts.map +0 -1
- package/dist/registration-cache.js +0 -24
- package/dist/search.d.ts +0 -27
- package/dist/search.d.ts.map +0 -1
- package/dist/search.js +0 -54
- package/dist/selection.d.ts +0 -25
- package/dist/selection.d.ts.map +0 -1
- package/dist/selection.js +0 -38
- package/dist/sparkline.d.ts +0 -26
- package/dist/sparkline.d.ts.map +0 -1
- package/dist/sparkline.js +0 -62
- package/dist/status-line.d.ts +0 -28
- package/dist/status-line.d.ts.map +0 -1
- package/dist/status-line.js +0 -18
- package/dist/test-utils.d.ts +0 -23
- package/dist/test-utils.d.ts.map +0 -1
- package/dist/test-utils.js +0 -35
- package/dist/use-aircraft-feed.d.ts +0 -28
- package/dist/use-aircraft-feed.d.ts.map +0 -1
- package/dist/use-aircraft-feed.js +0 -68
- package/dist/use-icao-registry.d.ts +0 -27
- package/dist/use-icao-registry.d.ts.map +0 -1
- package/dist/use-icao-registry.js +0 -37
package/README.md
CHANGED
|
@@ -44,11 +44,23 @@ adsbtop --source sbs --host 192.168.1.50
|
|
|
44
44
|
| `H` | Toggle the help overlay |
|
|
45
45
|
| `Q` | Quit |
|
|
46
46
|
|
|
47
|
-
Aircraft
|
|
47
|
+
Aircraft render in bold red when they carry any of: an emergency squawk code (7500/7600/7700), a declared emergency state, or an active ACAS/TCAS Resolution Advisory.
|
|
48
48
|
|
|
49
49
|
### Detail view
|
|
50
50
|
|
|
51
|
-
Selecting a row and pressing `Enter` or `D` opens a full field dump for that aircraft, including barometric and geometric altitude, true track and magnetic heading,
|
|
51
|
+
Selecting a row and pressing `Enter` or `D` opens a full field dump for that aircraft, including barometric and geometric altitude, true track and magnetic heading, indicated/true airspeed, squawk alert/ident status, declared emergency state, active Resolution Advisory, and pilot-selected target state (altitude/heading/autopilot). Fields the active source doesn't populate show as `-`.
|
|
52
|
+
|
|
53
|
+
#### Field population by source
|
|
54
|
+
|
|
55
|
+
Five detail-view fields have meaningfully different coverage depending on `--source` - see [`@squawk/adsb-feed`'s README](../../packages/libs/adsb-feed/README.md#field-population-by-source) for the decode details behind each:
|
|
56
|
+
|
|
57
|
+
| Field | JSON | SBS | Beast |
|
|
58
|
+
| ------------------- | ---- | --- | ----- |
|
|
59
|
+
| Squawk alert | - | Yes | Yes |
|
|
60
|
+
| Ident active | - | Yes | Yes |
|
|
61
|
+
| Emergency state | Yes | - | Yes |
|
|
62
|
+
| Resolution advisory | - | - | Yes |
|
|
63
|
+
| Target state | - | - | Yes |
|
|
52
64
|
|
|
53
65
|
### Messages panel
|
|
54
66
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@squawk/adsbtop",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Terminal dashboard for live ADS-B aircraft tracking, built on @squawk/adsb-feed",
|
|
6
6
|
"license": "MIT",
|
|
@@ -28,11 +28,11 @@
|
|
|
28
28
|
"lint": "tsc --noEmit && eslint src"
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@squawk/adsb-feed": "^0.
|
|
32
|
-
"@squawk/geo": "^0.4.
|
|
33
|
-
"@squawk/icao-registry": "^0.5.
|
|
34
|
-
"@squawk/icao-registry-data": "^0.8.
|
|
35
|
-
"@squawk/types": "^0.
|
|
31
|
+
"@squawk/adsb-feed": "^0.3.0",
|
|
32
|
+
"@squawk/geo": "^0.4.10",
|
|
33
|
+
"@squawk/icao-registry": "^0.5.8",
|
|
34
|
+
"@squawk/icao-registry-data": "^0.8.12",
|
|
35
|
+
"@squawk/types": "^0.9.0",
|
|
36
36
|
"ink": "^7.1.1",
|
|
37
37
|
"ink-text-input": "^6.0.0",
|
|
38
38
|
"react": "^19.2.8"
|
package/dist/aircraft-state.d.ts
DELETED
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
import type { Aircraft } from '@squawk/types';
|
|
2
|
-
/** Which underlying feed event a {@link MessageLogEntry} records. */
|
|
3
|
-
export type MessageLogEntryType = 'new' | 'update' | 'lost';
|
|
4
|
-
/** One entry in the `[M]essages` panel's log, oldest first. */
|
|
5
|
-
export interface MessageLogEntry {
|
|
6
|
-
/** Monotonically increasing id, stable across log trimming - used as the React list key. */
|
|
7
|
-
id: number;
|
|
8
|
-
/** Which feed event this entry records. */
|
|
9
|
-
type: MessageLogEntryType;
|
|
10
|
-
/** 24-bit ICAO hex address the event concerns. */
|
|
11
|
-
icaoHex: string;
|
|
12
|
-
/** Callsign at the time of the event, if known. */
|
|
13
|
-
callsign: string | undefined;
|
|
14
|
-
/** Unix epoch ms the event was observed. */
|
|
15
|
-
at: number;
|
|
16
|
-
}
|
|
17
|
-
/**
|
|
18
|
-
* Accumulated view of a live `@squawk/adsb-feed` `AircraftFeed`'s event
|
|
19
|
-
* stream: currently tracked aircraft, two bounded logs of recent events for
|
|
20
|
-
* the `[M]essages` panel, plus lightweight message-activity bookkeeping for
|
|
21
|
-
* the status header.
|
|
22
|
-
*/
|
|
23
|
-
export interface AircraftTableState {
|
|
24
|
-
/** Currently tracked aircraft, keyed by 24-bit ICAO hex address. */
|
|
25
|
-
aircraftByHex: Map<string, Aircraft>;
|
|
26
|
-
/** Total `aircraft:new`/`aircraft:update` events observed since the feed started. */
|
|
27
|
-
messageCount: number;
|
|
28
|
-
/** Unix epoch ms of the most recent `aircraft:new`/`aircraft:update` event, or undefined if none has arrived yet. */
|
|
29
|
-
lastMessageAt: number | undefined;
|
|
30
|
-
/** Every event type, oldest first, capped at {@link MAX_MESSAGE_LOG_ENTRIES} - backs the `[M]essages` panel's `all` verbosity. */
|
|
31
|
-
messageLog: MessageLogEntry[];
|
|
32
|
-
/**
|
|
33
|
-
* `new`/`lost` events only, oldest first, capped independently at
|
|
34
|
-
* {@link MAX_MESSAGE_LOG_ENTRIES} - backs the panel's default `newAndLost`
|
|
35
|
-
* verbosity. Kept as its own log rather than derived by filtering
|
|
36
|
-
* `messageLog`: `update` events fire far more often than `new`/`lost`, so
|
|
37
|
-
* a shared cap gets dominated by update volume and evicts a still-relevant
|
|
38
|
-
* new/lost entry within seconds of real traffic, even though nothing
|
|
39
|
-
* about it changed - toggling verbosity back to `newAndLost` would then
|
|
40
|
-
* show fewer entries than a moment before, for no reason visible to the
|
|
41
|
-
* user. A separate cap means `update` volume can never evict a
|
|
42
|
-
* `new`/`lost` entry.
|
|
43
|
-
*/
|
|
44
|
-
newAndLostLog: MessageLogEntry[];
|
|
45
|
-
/** Next {@link MessageLogEntry.id} to assign - kept separate from either log's length so ids stay stable once old entries are trimmed off the front. */
|
|
46
|
-
nextLogId: number;
|
|
47
|
-
}
|
|
48
|
-
/** An update or removal observed on the underlying `AircraftFeed`. */
|
|
49
|
-
export type AircraftStateAction = {
|
|
50
|
-
type: 'message';
|
|
51
|
-
kind: 'new' | 'update';
|
|
52
|
-
aircraft: Aircraft;
|
|
53
|
-
at: number;
|
|
54
|
-
} | {
|
|
55
|
-
type: 'lost';
|
|
56
|
-
icaoHex: string;
|
|
57
|
-
callsign: string | undefined;
|
|
58
|
-
at: number;
|
|
59
|
-
};
|
|
60
|
-
/** Empty {@link AircraftTableState}, before any feed event has arrived. */
|
|
61
|
-
export declare const initialAircraftState: AircraftTableState;
|
|
62
|
-
/**
|
|
63
|
-
* Reducer accumulating {@link AircraftStateAction}s into an {@link AircraftTableState}.
|
|
64
|
-
* Pure and side-effect-free - the caller supplies `at` from `Date.now()` at
|
|
65
|
-
* dispatch time, rather than this function reading the clock itself, so it
|
|
66
|
-
* stays trivially testable with fixed timestamps. Every action appends a
|
|
67
|
-
* {@link MessageLogEntry}, including a `lost` action for an ICAO hex this
|
|
68
|
-
* state never tracked - that shouldn't happen against a real `AircraftFeed`,
|
|
69
|
-
* but the log's job is showing what events actually arrived, not judging
|
|
70
|
-
* whether they were expected.
|
|
71
|
-
*
|
|
72
|
-
* @param state - The current accumulated state.
|
|
73
|
-
* @param action - The event to apply.
|
|
74
|
-
* @returns The next state. Always a new `aircraftByHex` map when it changes, so React's `useReducer` sees a fresh reference.
|
|
75
|
-
*/
|
|
76
|
-
export declare function aircraftStateReducer(state: AircraftTableState, action: AircraftStateAction): AircraftTableState;
|
|
77
|
-
//# sourceMappingURL=aircraft-state.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"aircraft-state.d.ts","sourceRoot":"","sources":["../src/aircraft-state.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAE9C,qEAAqE;AACrE,MAAM,MAAM,mBAAmB,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;AAE5D,+DAA+D;AAC/D,MAAM,WAAW,eAAe;IAC9B,4FAA4F;IAC5F,EAAE,EAAE,MAAM,CAAC;IACX,2CAA2C;IAC3C,IAAI,EAAE,mBAAmB,CAAC;IAC1B,kDAAkD;IAClD,OAAO,EAAE,MAAM,CAAC;IAChB,mDAAmD;IACnD,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,4CAA4C;IAC5C,EAAE,EAAE,MAAM,CAAC;CACZ;AASD;;;;;GAKG;AACH,MAAM,WAAW,kBAAkB;IACjC,oEAAoE;IACpE,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IACrC,qFAAqF;IACrF,YAAY,EAAE,MAAM,CAAC;IACrB,qHAAqH;IACrH,aAAa,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,kIAAkI;IAClI,UAAU,EAAE,eAAe,EAAE,CAAC;IAC9B;;;;;;;;;;;OAWG;IACH,aAAa,EAAE,eAAe,EAAE,CAAC;IACjC,wJAAwJ;IACxJ,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,sEAAsE;AACtE,MAAM,MAAM,mBAAmB,GAC3B;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,IAAI,EAAE,KAAK,GAAG,QAAQ,CAAC;IAAC,QAAQ,EAAE,QAAQ,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GAC3E;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC;AAEhF,2EAA2E;AAC3E,eAAO,MAAM,oBAAoB,EAAE,kBAOlC,CAAC;AAiBF;;;;;;;;;;;;;GAaG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,kBAAkB,EACzB,MAAM,EAAE,mBAAmB,GAC1B,kBAAkB,CA4CpB"}
|
package/dist/aircraft-state.js
DELETED
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Maximum {@link MessageLogEntry} rows retained. Oldest entries are dropped
|
|
3
|
-
* once exceeded, so the log stays a bounded "recent activity" view rather
|
|
4
|
-
* than growing unbounded for a long-running session.
|
|
5
|
-
*/
|
|
6
|
-
const MAX_MESSAGE_LOG_ENTRIES = 200;
|
|
7
|
-
/** Empty {@link AircraftTableState}, before any feed event has arrived. */
|
|
8
|
-
export const initialAircraftState = {
|
|
9
|
-
aircraftByHex: new Map(),
|
|
10
|
-
messageCount: 0,
|
|
11
|
-
lastMessageAt: undefined,
|
|
12
|
-
messageLog: [],
|
|
13
|
-
newAndLostLog: [],
|
|
14
|
-
nextLogId: 0,
|
|
15
|
-
};
|
|
16
|
-
/**
|
|
17
|
-
* Appends `entry` to `log`, dropping the oldest entry once
|
|
18
|
-
* {@link MAX_MESSAGE_LOG_ENTRIES} is exceeded.
|
|
19
|
-
*
|
|
20
|
-
* @param log - The current log, oldest first.
|
|
21
|
-
* @param entry - The entry to append.
|
|
22
|
-
* @returns A new array with `entry` appended, trimmed to the max length.
|
|
23
|
-
*/
|
|
24
|
-
function appendLogEntry(log, entry) {
|
|
25
|
-
const next = [...log, entry];
|
|
26
|
-
return next.length > MAX_MESSAGE_LOG_ENTRIES
|
|
27
|
-
? next.slice(next.length - MAX_MESSAGE_LOG_ENTRIES)
|
|
28
|
-
: next;
|
|
29
|
-
}
|
|
30
|
-
/**
|
|
31
|
-
* Reducer accumulating {@link AircraftStateAction}s into an {@link AircraftTableState}.
|
|
32
|
-
* Pure and side-effect-free - the caller supplies `at` from `Date.now()` at
|
|
33
|
-
* dispatch time, rather than this function reading the clock itself, so it
|
|
34
|
-
* stays trivially testable with fixed timestamps. Every action appends a
|
|
35
|
-
* {@link MessageLogEntry}, including a `lost` action for an ICAO hex this
|
|
36
|
-
* state never tracked - that shouldn't happen against a real `AircraftFeed`,
|
|
37
|
-
* but the log's job is showing what events actually arrived, not judging
|
|
38
|
-
* whether they were expected.
|
|
39
|
-
*
|
|
40
|
-
* @param state - The current accumulated state.
|
|
41
|
-
* @param action - The event to apply.
|
|
42
|
-
* @returns The next state. Always a new `aircraftByHex` map when it changes, so React's `useReducer` sees a fresh reference.
|
|
43
|
-
*/
|
|
44
|
-
export function aircraftStateReducer(state, action) {
|
|
45
|
-
switch (action.type) {
|
|
46
|
-
case 'message': {
|
|
47
|
-
const aircraftByHex = new Map(state.aircraftByHex);
|
|
48
|
-
aircraftByHex.set(action.aircraft.icaoHex, action.aircraft);
|
|
49
|
-
const logEntry = {
|
|
50
|
-
id: state.nextLogId,
|
|
51
|
-
type: action.kind,
|
|
52
|
-
icaoHex: action.aircraft.icaoHex,
|
|
53
|
-
callsign: action.aircraft.callsign,
|
|
54
|
-
at: action.at,
|
|
55
|
-
};
|
|
56
|
-
const newAndLostLog = action.kind === 'new' ? appendLogEntry(state.newAndLostLog, logEntry) : state.newAndLostLog;
|
|
57
|
-
return {
|
|
58
|
-
aircraftByHex,
|
|
59
|
-
messageCount: state.messageCount + 1,
|
|
60
|
-
lastMessageAt: action.at,
|
|
61
|
-
messageLog: appendLogEntry(state.messageLog, logEntry),
|
|
62
|
-
newAndLostLog,
|
|
63
|
-
nextLogId: state.nextLogId + 1,
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
case 'lost': {
|
|
67
|
-
const logEntry = {
|
|
68
|
-
id: state.nextLogId,
|
|
69
|
-
type: 'lost',
|
|
70
|
-
icaoHex: action.icaoHex,
|
|
71
|
-
callsign: action.callsign,
|
|
72
|
-
at: action.at,
|
|
73
|
-
};
|
|
74
|
-
const messageLog = appendLogEntry(state.messageLog, logEntry);
|
|
75
|
-
const newAndLostLog = appendLogEntry(state.newAndLostLog, logEntry);
|
|
76
|
-
const nextLogId = state.nextLogId + 1;
|
|
77
|
-
if (!state.aircraftByHex.has(action.icaoHex)) {
|
|
78
|
-
return { ...state, messageLog, newAndLostLog, nextLogId };
|
|
79
|
-
}
|
|
80
|
-
const aircraftByHex = new Map(state.aircraftByHex);
|
|
81
|
-
aircraftByHex.delete(action.icaoHex);
|
|
82
|
-
return { ...state, aircraftByHex, messageLog, newAndLostLog, nextLogId };
|
|
83
|
-
}
|
|
84
|
-
default:
|
|
85
|
-
return state;
|
|
86
|
-
}
|
|
87
|
-
}
|
package/dist/app.d.ts
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import type { ReactElement } from 'react';
|
|
2
|
-
import type { AircraftFeed } from '@squawk/adsb-feed';
|
|
3
|
-
import type { Coordinates } from '@squawk/types';
|
|
4
|
-
import type { FeedSource } from './cli-args.js';
|
|
5
|
-
import type { RegistryDataLoader } from './use-icao-registry.js';
|
|
6
|
-
/** Props for {@link App}. */
|
|
7
|
-
export interface AppProps {
|
|
8
|
-
/** The live feed to subscribe to - already constructed for the CLI's selected source. */
|
|
9
|
-
feed: AircraftFeed;
|
|
10
|
-
/** Feed source in use, for the status header. */
|
|
11
|
-
source: FeedSource;
|
|
12
|
-
/** Station host, for the status header. */
|
|
13
|
-
host: string;
|
|
14
|
-
/** Station port, for the status header. */
|
|
15
|
-
port: number;
|
|
16
|
-
/** Loader for the bundled registry dataset used for registration enrichment. Defaults to a real dynamic import of `@squawk/icao-registry-data`; overridable in tests. */
|
|
17
|
-
registryDataLoader?: RegistryDataLoader;
|
|
18
|
-
/** Configured receiver location (`--lat`/`--lon`), if any. Enables the table's Dist/Brg columns when set. */
|
|
19
|
-
location: Coordinates | undefined;
|
|
20
|
-
}
|
|
21
|
-
/**
|
|
22
|
-
* adsbtop's root component: subscribes to the feed, owns display state
|
|
23
|
-
* (pause, compact columns, sort, cursor, search, messages, and which main
|
|
24
|
-
* panel is showing), wires the hotkey bar, and renders the status header,
|
|
25
|
-
* main panel, optional messages panel, optional search prompt, and hotkey
|
|
26
|
-
* bar.
|
|
27
|
-
*
|
|
28
|
-
* `[P]ause` freezes the table's displayed rows (the feed keeps running
|
|
29
|
-
* underneath - resuming immediately jumps to current state, doesn't replay
|
|
30
|
-
* what was missed). Implemented as a React "adjust state during render"
|
|
31
|
-
* pattern (comparing the live feed's aircraft-array reference against what's
|
|
32
|
-
* displayed, copying over only while not paused), not a `useEffect`, since
|
|
33
|
-
* an effect-based version of this exact pattern trips
|
|
34
|
-
* `react-hooks/set-state-in-effect` and cascades an extra render. The cursor
|
|
35
|
-
* row's auto-selection uses the same render-time-adjustment pattern for the
|
|
36
|
-
* same reason.
|
|
37
|
-
*
|
|
38
|
-
* @param props - The feed to display and its connection details.
|
|
39
|
-
*/
|
|
40
|
-
export declare function App(props: AppProps): ReactElement;
|
|
41
|
-
//# sourceMappingURL=app.d.ts.map
|
package/dist/app.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../src/app.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC;AAG1C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,KAAK,EAAY,WAAW,EAAE,MAAM,eAAe,CAAC;AAE3D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAiBhD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAUjE,6BAA6B;AAC7B,MAAM,WAAW,QAAQ;IACvB,yFAAyF;IACzF,IAAI,EAAE,YAAY,CAAC;IACnB,iDAAiD;IACjD,MAAM,EAAE,UAAU,CAAC;IACnB,2CAA2C;IAC3C,IAAI,EAAE,MAAM,CAAC;IACb,2CAA2C;IAC3C,IAAI,EAAE,MAAM,CAAC;IACb,yKAAyK;IACzK,kBAAkB,CAAC,EAAE,kBAAkB,CAAC;IACxC,6GAA6G;IAC7G,QAAQ,EAAE,WAAW,GAAG,SAAS,CAAC;CACnC;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,GAAG,CAAC,KAAK,EAAE,QAAQ,GAAG,YAAY,CAgMjD"}
|
package/dist/app.js
DELETED
|
@@ -1,166 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { Box, useApp, useInput } from 'ink';
|
|
3
|
-
import { useEffect, useMemo, useState } from 'react';
|
|
4
|
-
import { nextSortKey, sortAircraft, visibleColumns } from './columns.js';
|
|
5
|
-
import { AircraftTable } from './components/aircraft-table.js';
|
|
6
|
-
import { DetailView } from './components/detail-view.js';
|
|
7
|
-
import { HelpOverlay } from './components/help-overlay.js';
|
|
8
|
-
import { HotkeyBar } from './components/hotkey-bar.js';
|
|
9
|
-
import { MessagesPanel } from './components/messages-panel.js';
|
|
10
|
-
import { SearchBar } from './components/search-bar.js';
|
|
11
|
-
import { StatusHeader } from './components/status-header.js';
|
|
12
|
-
import { enrichAircraftList } from './registration-cache.js';
|
|
13
|
-
import { findMatchIcaoHex } from './search.js';
|
|
14
|
-
import { moveSelection } from './selection.js';
|
|
15
|
-
import { useAircraftFeed } from './use-aircraft-feed.js';
|
|
16
|
-
import { useIcaoRegistry } from './use-icao-registry.js';
|
|
17
|
-
/** How often the age column and status-header "last update" text refresh. */
|
|
18
|
-
const CLOCK_TICK_MS = 1000;
|
|
19
|
-
/** Sort key adsbtop starts with. */
|
|
20
|
-
const INITIAL_SORT_KEY = 'icaoHex';
|
|
21
|
-
/**
|
|
22
|
-
* adsbtop's root component: subscribes to the feed, owns display state
|
|
23
|
-
* (pause, compact columns, sort, cursor, search, messages, and which main
|
|
24
|
-
* panel is showing), wires the hotkey bar, and renders the status header,
|
|
25
|
-
* main panel, optional messages panel, optional search prompt, and hotkey
|
|
26
|
-
* bar.
|
|
27
|
-
*
|
|
28
|
-
* `[P]ause` freezes the table's displayed rows (the feed keeps running
|
|
29
|
-
* underneath - resuming immediately jumps to current state, doesn't replay
|
|
30
|
-
* what was missed). Implemented as a React "adjust state during render"
|
|
31
|
-
* pattern (comparing the live feed's aircraft-array reference against what's
|
|
32
|
-
* displayed, copying over only while not paused), not a `useEffect`, since
|
|
33
|
-
* an effect-based version of this exact pattern trips
|
|
34
|
-
* `react-hooks/set-state-in-effect` and cascades an extra render. The cursor
|
|
35
|
-
* row's auto-selection uses the same render-time-adjustment pattern for the
|
|
36
|
-
* same reason.
|
|
37
|
-
*
|
|
38
|
-
* @param props - The feed to display and its connection details.
|
|
39
|
-
*/
|
|
40
|
-
export function App(props) {
|
|
41
|
-
const { exit } = useApp();
|
|
42
|
-
const view = useAircraftFeed(props.feed);
|
|
43
|
-
const registry = useIcaoRegistry(props.registryDataLoader);
|
|
44
|
-
const [registrationCache] = useState(() => new Map());
|
|
45
|
-
const enrichedAircraft = useMemo(() => enrichAircraftList(view.aircraft, registry, registrationCache), [view.aircraft, registry, registrationCache]);
|
|
46
|
-
const [paused, setPaused] = useState(false);
|
|
47
|
-
const [compact, setCompact] = useState(false);
|
|
48
|
-
const [panel, setPanel] = useState('table');
|
|
49
|
-
const [sortKey, setSortKey] = useState(INITIAL_SORT_KEY);
|
|
50
|
-
const [now, setNow] = useState(() => Date.now());
|
|
51
|
-
const [displayedAircraft, setDisplayedAircraft] = useState(enrichedAircraft);
|
|
52
|
-
const [selectedIcaoHex, setSelectedIcaoHex] = useState(undefined);
|
|
53
|
-
const [showMessages, setShowMessages] = useState(false);
|
|
54
|
-
const [messageVerbosity, setMessageVerbosity] = useState('newAndLost');
|
|
55
|
-
const [searching, setSearching] = useState(false);
|
|
56
|
-
const [searchQuery, setSearchQuery] = useState('');
|
|
57
|
-
const [submittedSearchQuery, setSubmittedSearchQuery] = useState(undefined);
|
|
58
|
-
useEffect(() => {
|
|
59
|
-
const handle = setInterval(() => setNow(Date.now()), CLOCK_TICK_MS);
|
|
60
|
-
return () => clearInterval(handle);
|
|
61
|
-
}, []);
|
|
62
|
-
if (!paused && enrichedAircraft !== displayedAircraft) {
|
|
63
|
-
setDisplayedAircraft(enrichedAircraft);
|
|
64
|
-
}
|
|
65
|
-
const sortedAircraft = useMemo(() => sortAircraft(displayedAircraft, sortKey), [displayedAircraft, sortKey]);
|
|
66
|
-
const columns = useMemo(() => visibleColumns(compact, props.location), [compact, props.location]);
|
|
67
|
-
const firstAircraft = sortedAircraft[0];
|
|
68
|
-
if (selectedIcaoHex === undefined && firstAircraft !== undefined) {
|
|
69
|
-
setSelectedIcaoHex(firstAircraft.icaoHex);
|
|
70
|
-
}
|
|
71
|
-
const selectedAircraft = sortedAircraft.find((aircraft) => aircraft.icaoHex === selectedIcaoHex);
|
|
72
|
-
function handleSearchSubmit(query) {
|
|
73
|
-
setSearching(false);
|
|
74
|
-
const trimmed = query.trim();
|
|
75
|
-
if (trimmed === '') {
|
|
76
|
-
setSubmittedSearchQuery(undefined);
|
|
77
|
-
return;
|
|
78
|
-
}
|
|
79
|
-
setSubmittedSearchQuery(trimmed);
|
|
80
|
-
const match = findMatchIcaoHex(sortedAircraft, trimmed, selectedIcaoHex, 1);
|
|
81
|
-
if (match !== undefined) {
|
|
82
|
-
setSelectedIcaoHex(match);
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
useInput((input, key) => {
|
|
86
|
-
if (input === 'q' || input === 'Q') {
|
|
87
|
-
exit();
|
|
88
|
-
return;
|
|
89
|
-
}
|
|
90
|
-
if (key.escape) {
|
|
91
|
-
if (panel !== 'table') {
|
|
92
|
-
setPanel('table');
|
|
93
|
-
}
|
|
94
|
-
return;
|
|
95
|
-
}
|
|
96
|
-
if (key.upArrow) {
|
|
97
|
-
setSelectedIcaoHex((prev) => moveSelection(sortedAircraft, prev, -1));
|
|
98
|
-
return;
|
|
99
|
-
}
|
|
100
|
-
if (key.downArrow) {
|
|
101
|
-
setSelectedIcaoHex((prev) => moveSelection(sortedAircraft, prev, 1));
|
|
102
|
-
return;
|
|
103
|
-
}
|
|
104
|
-
if (key.return) {
|
|
105
|
-
if (selectedAircraft !== undefined) {
|
|
106
|
-
setPanel((prev) => (prev === 'detail' ? 'table' : 'detail'));
|
|
107
|
-
}
|
|
108
|
-
return;
|
|
109
|
-
}
|
|
110
|
-
switch (input) {
|
|
111
|
-
case 'p':
|
|
112
|
-
case 'P':
|
|
113
|
-
setPaused((prev) => !prev);
|
|
114
|
-
break;
|
|
115
|
-
case 'c':
|
|
116
|
-
case 'C':
|
|
117
|
-
setCompact((prev) => !prev);
|
|
118
|
-
break;
|
|
119
|
-
case 'h':
|
|
120
|
-
case 'H':
|
|
121
|
-
setPanel((prev) => (prev === 'help' ? 'table' : 'help'));
|
|
122
|
-
break;
|
|
123
|
-
case 'o':
|
|
124
|
-
case 'O':
|
|
125
|
-
setSortKey((prev) => nextSortKey(prev));
|
|
126
|
-
break;
|
|
127
|
-
case 'd':
|
|
128
|
-
case 'D':
|
|
129
|
-
if (selectedAircraft !== undefined) {
|
|
130
|
-
setPanel((prev) => (prev === 'detail' ? 'table' : 'detail'));
|
|
131
|
-
}
|
|
132
|
-
break;
|
|
133
|
-
case 's':
|
|
134
|
-
case 'S':
|
|
135
|
-
setSearching(true);
|
|
136
|
-
setSearchQuery('');
|
|
137
|
-
break;
|
|
138
|
-
case 'm':
|
|
139
|
-
case 'M':
|
|
140
|
-
setShowMessages((prev) => !prev);
|
|
141
|
-
break;
|
|
142
|
-
case 'v':
|
|
143
|
-
case 'V':
|
|
144
|
-
setMessageVerbosity((prev) => (prev === 'all' ? 'newAndLost' : 'all'));
|
|
145
|
-
break;
|
|
146
|
-
case 'n':
|
|
147
|
-
if (submittedSearchQuery !== undefined) {
|
|
148
|
-
setSelectedIcaoHex((prev) => findMatchIcaoHex(sortedAircraft, submittedSearchQuery, prev, 1) ?? prev);
|
|
149
|
-
}
|
|
150
|
-
break;
|
|
151
|
-
case 'N':
|
|
152
|
-
if (submittedSearchQuery !== undefined) {
|
|
153
|
-
setSelectedIcaoHex((prev) => findMatchIcaoHex(sortedAircraft, submittedSearchQuery, prev, -1) ?? prev);
|
|
154
|
-
}
|
|
155
|
-
break;
|
|
156
|
-
default:
|
|
157
|
-
break;
|
|
158
|
-
}
|
|
159
|
-
}, { isActive: !searching });
|
|
160
|
-
useInput((_input, key) => {
|
|
161
|
-
if (key.escape) {
|
|
162
|
-
setSearching(false);
|
|
163
|
-
}
|
|
164
|
-
}, { isActive: searching });
|
|
165
|
-
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(StatusHeader, { source: props.source, host: props.host, port: props.port, aircraftCount: view.aircraft.length, messageRatePerSec: view.messageRatePerSec, lastMessageAt: view.lastMessageAt, nowMs: now, paused: paused }), panel === 'help' ? (_jsx(HelpOverlay, {})) : panel === 'detail' && selectedAircraft !== undefined ? (_jsx(DetailView, { aircraft: selectedAircraft, nowMs: now, location: props.location })) : (_jsx(AircraftTable, { aircraft: sortedAircraft, columns: columns, nowMs: now, sortKey: sortKey, selectedIcaoHex: selectedIcaoHex })), showMessages ? (_jsx(MessagesPanel, { entries: messageVerbosity === 'newAndLost' ? view.newAndLostLog : view.messageLog, verbosity: messageVerbosity })) : undefined, searching ? (_jsx(SearchBar, { query: searchQuery, onChange: setSearchQuery, onSubmit: handleSearchSubmit })) : undefined, _jsx(HotkeyBar, { paused: paused, showMessages: showMessages, hasActiveSearch: submittedSearchQuery !== undefined })] }));
|
|
166
|
-
}
|
package/dist/cli-args.d.ts
DELETED
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import type { Coordinates } from '@squawk/types';
|
|
2
|
-
/** Which dump1090-fa output adsbtop connects to. */
|
|
3
|
-
export type FeedSource = 'json' | 'sbs' | 'beast';
|
|
4
|
-
/** Default TCP/HTTP port per {@link FeedSource}, matching dump1090-fa's own defaults. */
|
|
5
|
-
export declare const DEFAULT_PORT_BY_SOURCE: Record<FeedSource, number>;
|
|
6
|
-
/** Parsed and validated CLI options for the `adsbtop` command. */
|
|
7
|
-
export interface CliOptions {
|
|
8
|
-
/** Whether `--help` was passed. When true, every other field is a placeholder and the caller should print usage and exit without starting a feed. */
|
|
9
|
-
help: boolean;
|
|
10
|
-
/** Which dump1090-fa output to connect to. */
|
|
11
|
-
source: FeedSource;
|
|
12
|
-
/** Hostname or IP address of the dump1090-fa station. */
|
|
13
|
-
host: string;
|
|
14
|
-
/** TCP/HTTP port to connect to. Defaults to {@link DEFAULT_PORT_BY_SOURCE} for the selected source. */
|
|
15
|
-
port: number;
|
|
16
|
-
/** Full override URL for the `json` source's `aircraft.json` endpoint. Undefined for `sbs`/`beast`, and undefined for `json` unless `--url` was passed explicitly. */
|
|
17
|
-
url: string | undefined;
|
|
18
|
-
/**
|
|
19
|
-
* The receiving station's own position, from `--lat`/`--lon`. When set,
|
|
20
|
-
* the table's Dist/Brg columns compute distance and bearing from this
|
|
21
|
-
* point to each aircraft, and for `--source beast` the same value also
|
|
22
|
-
* feeds `BeastFeedOptions.receiverPosition` for surface CPR decoding.
|
|
23
|
-
* Undefined unless both `--lat` and `--lon` were passed.
|
|
24
|
-
*/
|
|
25
|
-
location: Coordinates | undefined;
|
|
26
|
-
}
|
|
27
|
-
/** A `parseCliArgs` failure: the reason `argv` could not be turned into {@link CliOptions}. */
|
|
28
|
-
export interface CliArgsError {
|
|
29
|
-
/** Human-readable message describing what was wrong with `argv`, suitable for printing directly to stderr. */
|
|
30
|
-
message: string;
|
|
31
|
-
}
|
|
32
|
-
/** Usage text printed for `--help`/`-h`, and on an argument error. */
|
|
33
|
-
export declare const USAGE: string;
|
|
34
|
-
/**
|
|
35
|
-
* Parses and validates `adsbtop`'s command-line arguments. Returns a result
|
|
36
|
-
* type rather than throwing, so callers (and tests) can handle a bad
|
|
37
|
-
* argument the same way as any other expected outcome.
|
|
38
|
-
*
|
|
39
|
-
* @param argv - Argument list, excluding the `node`/script entries (i.e. `process.argv.slice(2)`).
|
|
40
|
-
* @returns The parsed options, or a {@link CliArgsError} describing the first problem found.
|
|
41
|
-
*/
|
|
42
|
-
export declare function parseCliArgs(argv: string[]): CliOptions | CliArgsError;
|
|
43
|
-
//# sourceMappingURL=cli-args.d.ts.map
|
package/dist/cli-args.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"cli-args.d.ts","sourceRoot":"","sources":["../src/cli-args.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAEjD,oDAAoD;AACpD,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,KAAK,GAAG,OAAO,CAAC;AAElD,yFAAyF;AACzF,eAAO,MAAM,sBAAsB,EAAE,MAAM,CAAC,UAAU,EAAE,MAAM,CAI7D,CAAC;AAEF,kEAAkE;AAClE,MAAM,WAAW,UAAU;IACzB,qJAAqJ;IACrJ,IAAI,EAAE,OAAO,CAAC;IACd,8CAA8C;IAC9C,MAAM,EAAE,UAAU,CAAC;IACnB,yDAAyD;IACzD,IAAI,EAAE,MAAM,CAAC;IACb,uGAAuG;IACvG,IAAI,EAAE,MAAM,CAAC;IACb,sKAAsK;IACtK,GAAG,EAAE,MAAM,GAAG,SAAS,CAAC;IACxB;;;;;;OAMG;IACH,QAAQ,EAAE,WAAW,GAAG,SAAS,CAAC;CACnC;AAED,+FAA+F;AAC/F,MAAM,WAAW,YAAY;IAC3B,8GAA8G;IAC9G,OAAO,EAAE,MAAM,CAAC;CACjB;AAuCD,sEAAsE;AACtE,eAAO,MAAM,KAAK,QAUjB,CAAC;AAiCF;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,UAAU,GAAG,YAAY,CAiEtE"}
|
package/dist/cli-args.js
DELETED
|
@@ -1,150 +0,0 @@
|
|
|
1
|
-
import { parseArgs } from 'node:util';
|
|
2
|
-
/** Default TCP/HTTP port per {@link FeedSource}, matching dump1090-fa's own defaults. */
|
|
3
|
-
export const DEFAULT_PORT_BY_SOURCE = {
|
|
4
|
-
json: 8080,
|
|
5
|
-
sbs: 30003,
|
|
6
|
-
beast: 30005,
|
|
7
|
-
};
|
|
8
|
-
const DEFAULT_HOST = 'localhost';
|
|
9
|
-
const DEFAULT_SOURCE = 'sbs';
|
|
10
|
-
const NEGATIVE_NUMBER = /^-\d+(\.\d+)?$/;
|
|
11
|
-
function isFeedSource(value) {
|
|
12
|
-
return value === 'json' || value === 'sbs' || value === 'beast';
|
|
13
|
-
}
|
|
14
|
-
/**
|
|
15
|
-
* Node's `parseArgs` rejects `--lon -73.7781` as an ambiguous option value
|
|
16
|
-
* (a bare token starting with `-` looks like another flag), requiring the
|
|
17
|
-
* awkward `--lon=-73.7781` form instead - a real problem here since most
|
|
18
|
-
* real-world longitudes (and some latitudes) are negative. Rewrites a
|
|
19
|
-
* `--lat`/`--lon` immediately followed by a bare negative number into the
|
|
20
|
-
* `=` form before parsing, so the natural `--lon -73.7781` spelling works.
|
|
21
|
-
*
|
|
22
|
-
* @param argv - Raw argument list, as passed to {@link parseCliArgs}.
|
|
23
|
-
* @returns `argv` with negative `--lat`/`--lon` values rewritten to `--flag=value` form.
|
|
24
|
-
*/
|
|
25
|
-
function normalizeNegativeLocationArgs(argv) {
|
|
26
|
-
const normalized = [];
|
|
27
|
-
for (let i = 0; i < argv.length; i++) {
|
|
28
|
-
const arg = argv[i];
|
|
29
|
-
if (arg === undefined) {
|
|
30
|
-
continue;
|
|
31
|
-
}
|
|
32
|
-
const next = argv[i + 1];
|
|
33
|
-
if ((arg === '--lat' || arg === '--lon') && next !== undefined && NEGATIVE_NUMBER.test(next)) {
|
|
34
|
-
normalized.push(`${arg}=${next}`);
|
|
35
|
-
i++;
|
|
36
|
-
}
|
|
37
|
-
else {
|
|
38
|
-
normalized.push(arg);
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
return normalized;
|
|
42
|
-
}
|
|
43
|
-
/** Usage text printed for `--help`/`-h`, and on an argument error. */
|
|
44
|
-
export const USAGE = `Usage: adsbtop [options]
|
|
45
|
-
|
|
46
|
-
Options:
|
|
47
|
-
--source <json|sbs|beast> Feed to connect to (default: ${DEFAULT_SOURCE})
|
|
48
|
-
--host <host> dump1090-fa station hostname/IP (default: ${DEFAULT_HOST})
|
|
49
|
-
--port <port> Port to connect to (default: 8080 json, 30003 sbs, 30005 beast)
|
|
50
|
-
--url <url> Full aircraft.json URL, overriding --host/--port (source=json only)
|
|
51
|
-
--lat <lat> Receiver latitude in decimal degrees - enables Dist/Brg columns (requires --lon)
|
|
52
|
-
--lon <lon> Receiver longitude in decimal degrees - enables Dist/Brg columns (requires --lat)
|
|
53
|
-
-h, --help Show this help message
|
|
54
|
-
`;
|
|
55
|
-
/**
|
|
56
|
-
* Parses and validates `--lat`/`--lon` into a {@link Coordinates}. Both must
|
|
57
|
-
* be given together; either alone is an error. Returns `{ location:
|
|
58
|
-
* undefined }` when neither was passed, since omitting location entirely is
|
|
59
|
-
* valid and distinct from an invalid one.
|
|
60
|
-
*
|
|
61
|
-
* @param rawLat - Raw `--lat` value, if passed.
|
|
62
|
-
* @param rawLon - Raw `--lon` value, if passed.
|
|
63
|
-
* @returns The parsed location (possibly undefined), or a {@link CliArgsError}.
|
|
64
|
-
*/
|
|
65
|
-
function parseLocation(rawLat, rawLon) {
|
|
66
|
-
if (rawLat === undefined && rawLon === undefined) {
|
|
67
|
-
return { location: undefined };
|
|
68
|
-
}
|
|
69
|
-
if (rawLat === undefined || rawLon === undefined) {
|
|
70
|
-
return { message: '--lat and --lon must be provided together.' };
|
|
71
|
-
}
|
|
72
|
-
const lat = Number(rawLat);
|
|
73
|
-
if (!Number.isFinite(lat) || lat < -90 || lat > 90) {
|
|
74
|
-
return { message: `Invalid --lat "${rawLat}" - expected a number between -90 and 90.` };
|
|
75
|
-
}
|
|
76
|
-
const lon = Number(rawLon);
|
|
77
|
-
if (!Number.isFinite(lon) || lon < -180 || lon > 180) {
|
|
78
|
-
return { message: `Invalid --lon "${rawLon}" - expected a number between -180 and 180.` };
|
|
79
|
-
}
|
|
80
|
-
return { location: { lat, lon } };
|
|
81
|
-
}
|
|
82
|
-
/**
|
|
83
|
-
* Parses and validates `adsbtop`'s command-line arguments. Returns a result
|
|
84
|
-
* type rather than throwing, so callers (and tests) can handle a bad
|
|
85
|
-
* argument the same way as any other expected outcome.
|
|
86
|
-
*
|
|
87
|
-
* @param argv - Argument list, excluding the `node`/script entries (i.e. `process.argv.slice(2)`).
|
|
88
|
-
* @returns The parsed options, or a {@link CliArgsError} describing the first problem found.
|
|
89
|
-
*/
|
|
90
|
-
export function parseCliArgs(argv) {
|
|
91
|
-
let parsed;
|
|
92
|
-
try {
|
|
93
|
-
parsed = parseArgs({
|
|
94
|
-
args: normalizeNegativeLocationArgs(argv),
|
|
95
|
-
options: {
|
|
96
|
-
source: { type: 'string', default: DEFAULT_SOURCE },
|
|
97
|
-
host: { type: 'string', default: DEFAULT_HOST },
|
|
98
|
-
port: { type: 'string' },
|
|
99
|
-
url: { type: 'string' },
|
|
100
|
-
lat: { type: 'string' },
|
|
101
|
-
lon: { type: 'string' },
|
|
102
|
-
help: { type: 'boolean', short: 'h', default: false },
|
|
103
|
-
},
|
|
104
|
-
strict: true,
|
|
105
|
-
});
|
|
106
|
-
}
|
|
107
|
-
catch (error) {
|
|
108
|
-
return { message: error instanceof Error ? error.message : String(error) };
|
|
109
|
-
}
|
|
110
|
-
const { values } = parsed;
|
|
111
|
-
if (values.help === true) {
|
|
112
|
-
return {
|
|
113
|
-
help: true,
|
|
114
|
-
source: DEFAULT_SOURCE,
|
|
115
|
-
host: DEFAULT_HOST,
|
|
116
|
-
port: DEFAULT_PORT_BY_SOURCE[DEFAULT_SOURCE],
|
|
117
|
-
url: undefined,
|
|
118
|
-
location: undefined,
|
|
119
|
-
};
|
|
120
|
-
}
|
|
121
|
-
const parsedLocation = parseLocation(values.lat, values.lon);
|
|
122
|
-
if ('message' in parsedLocation) {
|
|
123
|
-
return parsedLocation;
|
|
124
|
-
}
|
|
125
|
-
const rawSource = values.source ?? DEFAULT_SOURCE;
|
|
126
|
-
if (!isFeedSource(rawSource)) {
|
|
127
|
-
return { message: `Invalid --source "${rawSource}" - expected json, sbs, or beast.` };
|
|
128
|
-
}
|
|
129
|
-
let port = DEFAULT_PORT_BY_SOURCE[rawSource];
|
|
130
|
-
if (values.port !== undefined) {
|
|
131
|
-
const parsedPort = Number(values.port);
|
|
132
|
-
if (!Number.isInteger(parsedPort) || parsedPort <= 0 || parsedPort > 65535) {
|
|
133
|
-
return {
|
|
134
|
-
message: `Invalid --port "${values.port}" - expected an integer between 1 and 65535.`,
|
|
135
|
-
};
|
|
136
|
-
}
|
|
137
|
-
port = parsedPort;
|
|
138
|
-
}
|
|
139
|
-
if (values.url !== undefined && rawSource !== 'json') {
|
|
140
|
-
return { message: '--url is only valid with --source json.' };
|
|
141
|
-
}
|
|
142
|
-
return {
|
|
143
|
-
help: false,
|
|
144
|
-
source: rawSource,
|
|
145
|
-
host: values.host ?? DEFAULT_HOST,
|
|
146
|
-
port,
|
|
147
|
-
url: values.url,
|
|
148
|
-
location: parsedLocation.location,
|
|
149
|
-
};
|
|
150
|
-
}
|
package/dist/cli.d.ts
DELETED
package/dist/cli.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.tsx"],"names":[],"mappings":""}
|
package/dist/cli.js
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
-
import { render } from 'ink';
|
|
4
|
-
import { App } from './app.js';
|
|
5
|
-
import { parseCliArgs, USAGE } from './cli-args.js';
|
|
6
|
-
import { buildFeed } from './create-feed.js';
|
|
7
|
-
const parsed = parseCliArgs(process.argv.slice(2));
|
|
8
|
-
if ('message' in parsed) {
|
|
9
|
-
process.stderr.write(`${parsed.message}\n\n${USAGE}`);
|
|
10
|
-
process.exit(1);
|
|
11
|
-
}
|
|
12
|
-
if (parsed.help) {
|
|
13
|
-
process.stdout.write(USAGE);
|
|
14
|
-
process.exit(0);
|
|
15
|
-
}
|
|
16
|
-
const feed = buildFeed(parsed);
|
|
17
|
-
render(_jsx(App, { feed: feed, source: parsed.source, host: parsed.host, port: parsed.port, location: parsed.location }));
|