@aeroswarm/client 0.1.11 → 0.1.13

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.
@@ -1,582 +1,3 @@
1
- import { CurrentTrack, SourceStatus, AeroConnectionStatus, AeroRecoverableError, Bbox } from '@aeroswarm/core/types';
2
- import * as _trpc_client from '@trpc/client';
3
- import '@aeroswarm/core/utils';
4
-
5
- type WsEncoding = "json" | "msgpack";
6
- interface SubscribeOptions {
7
- bbox?: [number, number, number, number];
8
- /** Scope the stream to tracks stamped with this ICAO airport code, e.g. "WARR" */
9
- airportId?: string;
10
- source?: "fused" | "normalized";
11
- minConfidence?: number;
12
- maxTracks?: number;
13
- mode?: "delta" | "full";
14
- maxUpdateHz?: number;
15
- encoding?: WsEncoding;
16
- staleAfterMs?: number;
17
- removeAfterMs?: number;
18
- }
19
- interface TrackCallbacks {
20
- onTrackUpdate?: (track: CurrentTrack) => void;
21
- onTrackRemoved?: (event: {
22
- trackId: string;
23
- reason: string;
24
- }) => void;
25
- onSnapshot?: (tracks: CurrentTrack[]) => void;
26
- onSourceStatus?: (event: {
27
- sourceId: string;
28
- status: SourceStatus;
29
- }) => void;
30
- onStatusChange?: (status: AeroConnectionStatus) => void;
31
- onError?: (err: AeroRecoverableError) => void;
32
- onDegraded?: (reason: string) => void;
33
- }
34
- declare class AeroWsClient {
35
- private readonly url;
36
- private readonly token;
37
- private readonly maxReconnectDelay;
38
- private readonly heartbeatTimeoutMs;
39
- private ws;
40
- private attempt;
41
- private stopped;
42
- private lastSubscribe;
43
- private callbacks;
44
- private lastSeq;
45
- private lastHeartbeat;
46
- private heartbeatTimer;
47
- private status;
48
- constructor(url: string, token: string, maxReconnectDelay?: number, heartbeatTimeoutMs?: number);
49
- connect(opts: SubscribeOptions, callbacks: TrackCallbacks): void;
50
- updateFilter(opts: Partial<Pick<SubscribeOptions, 'bbox' | 'minConfidence' | 'maxUpdateHz' | 'maxTracks'>>): void;
51
- disconnect(): void;
52
- getStatus(): AeroConnectionStatus;
53
- private _setStatus;
54
- private _connect;
55
- private _handleRaw;
56
- private _requestSnapshot;
57
- private _resetHeartbeatTimer;
58
- private _clearHeartbeatTimer;
59
- private _scheduleReconnect;
60
- }
61
-
62
- /**
63
- * Server-paced WS replay client for `/ws/tracks/replay`.
64
- *
65
- * The server streams historical `fused_tracks` events at `speed`× real
66
- * time (paced server-side against a wall-clock anchor), so the client
67
- * receives replay frames exactly like live traffic: a `snapshot`
68
- * envelope with the state at `from`, then `track.updated` /
69
- * `track.removed` envelopes, plus `replay.progress` heartbeats and a
70
- * final `replay.done`.
71
- *
72
- * The wire protocol is plain JSON, so consumers that don't use this SDK
73
- * can speak it manually:
74
- *
75
- * → connect wss://host/ws/tracks/replay?token=TOKEN[&airportId=WARR][&sessionId=ID]
76
- * → send { "type": "replay.start", "from": ISO, "to": ISO, "speed": 2 }
77
- * → send { "type": "replay.pause" } · { "type": "replay.resume" }
78
- * → send { "type": "replay.seek", "at": ISO }
79
- * → send { "type": "replay.stop" }
80
- * ← recv { "type": "snapshot", "payload": { "tracks": [...], "sessionId": ID } }
81
- * ← recv { "type": "track.updated", "payload": { "track": {...} } }
82
- * ← recv { "type": "track.removed", "payload": { "trackId": ID, "reason": "dropped" } }
83
- * ← recv { "type": "replay.progress", "payload": { "at": ISO, "pct": 0-100, "sessionId": ID } }
84
- * ← recv { "type": "replay.done", "payload": { "sessionId": ID } }
85
- *
86
- * Reconnecting with the same `sessionId` (within 1 h) resumes a replay
87
- * from its last saved position.
88
- */
89
- interface ReplayStreamOptions {
90
- from: Date | string;
91
- to: Date | string;
92
- /** Playback speed multiplier (1 = realtime). Default 1. */
93
- speed?: number;
94
- /** Which airport to replay. Federated airports (with a historyEndpoint)
95
- * are resolved by AeroSwarm.replay.stream to their own backend; against
96
- * AeroSwarm's own endpoint this is a row filter on the local DB. */
97
- airportId?: string;
98
- /** Resume a previous session instead of starting fresh. */
99
- sessionId?: string;
100
- }
101
- interface ReplayProgress {
102
- /** Replay-time position (ISO timestamp) */
103
- at: string;
104
- /** 0–100 within the requested range */
105
- pct: number;
106
- sessionId: string;
107
- }
108
- interface ReplayCallbacks {
109
- /** State at `from` — arrives once per start/seek. */
110
- onSnapshot?: (tracks: CurrentTrack[], sessionId: string) => void;
111
- onTrackUpdate?: (track: CurrentTrack) => void;
112
- onTrackRemoved?: (event: {
113
- trackId: string;
114
- reason: string;
115
- }) => void;
116
- onProgress?: (progress: ReplayProgress) => void;
117
- onDone?: (sessionId: string) => void;
118
- onError?: (error: Error) => void;
119
- }
120
- interface ReplayHandle {
121
- readonly sessionId: string | null;
122
- pause(): void;
123
- resume(): void;
124
- /** Jump to a replay-time position; the server restarts the paced stream there. */
125
- seek(at: Date | string): void;
126
- /** End the replay and close the socket. */
127
- stop(): void;
128
- }
129
- declare class AeroReplayClient {
130
- private readonly wsBase;
131
- private readonly token;
132
- /** Default "/ws/tracks/replay" (AeroSwarm's own endpoint). A federated
133
- * airport backend running @aeroswarm/replay-server's default mount
134
- * uses "/replay" instead — see AeroSwarm.replay.stream's endpoint
135
- * resolution. */
136
- private readonly wsPath;
137
- constructor(wsBase: string, token: string,
138
- /** Default "/ws/tracks/replay" (AeroSwarm's own endpoint). A federated
139
- * airport backend running @aeroswarm/replay-server's default mount
140
- * uses "/replay" instead — see AeroSwarm.replay.stream's endpoint
141
- * resolution. */
142
- wsPath?: string);
143
- /** Open a replay stream. Returns a handle for transport control. */
144
- stream(opts: ReplayStreamOptions, callbacks: ReplayCallbacks): ReplayHandle;
145
- }
146
-
147
- declare function createAeroTrpcClient(endpoint: string, token: string): _trpc_client.TRPCClient<any>;
148
- type AeroTrpcClient = ReturnType<typeof createAeroTrpcClient>;
149
-
150
- interface SourceRow {
151
- id: string;
152
- name: string;
153
- type: string;
154
- sourceToken: string;
155
- createdAt: string;
156
- /** Airport this source's data belongs to. When set, anything that
157
- * authenticates with this source's token (WS live/replay, tRPC calls)
158
- * is scoped to this airport automatically. */
159
- airportId?: string | null;
160
- }
161
- declare class AeroRestClient {
162
- private readonly endpoint;
163
- private readonly token;
164
- constructor(endpoint: string, token: string);
165
- private req;
166
- createSource(body: Record<string, unknown>): Promise<{
167
- ok: boolean;
168
- data: {
169
- sourceId: string;
170
- sourceToken: string;
171
- airportId?: string | null;
172
- };
173
- }>;
174
- listSources(): Promise<{
175
- ok: boolean;
176
- data: {
177
- sources: SourceRow[];
178
- };
179
- }>;
180
- deleteSource(id: string): Promise<{
181
- ok: boolean;
182
- }>;
183
- createWebhook(body: {
184
- url: string;
185
- events: string[];
186
- secret?: string;
187
- }): Promise<{
188
- webhookId: string;
189
- }>;
190
- getTracksCurrent(params?: {
191
- bbox?: [number, number, number, number];
192
- minConfidence?: number;
193
- }): Promise<{
194
- tracks: unknown[];
195
- count: number;
196
- }>;
197
- health(): Promise<{
198
- status: string;
199
- }>;
200
- }
201
-
202
- declare class AeroGraphQLClient {
203
- private readonly endpoint;
204
- private readonly token;
205
- constructor(endpoint: string, token: string);
206
- query<T = unknown>(query: string, variables?: Record<string, unknown>): Promise<T>;
207
- }
208
-
209
- type Listener<T> = (payload: T) => void;
210
- declare class TypedEmitter<T extends Record<string, unknown>> {
211
- private listeners;
212
- on<K extends keyof T>(event: K, fn: Listener<T[K]>): () => void;
213
- emit<K extends keyof T>(event: K, payload: T[K]): void;
214
- }
215
- declare class TrackCache extends TypedEmitter<{
216
- update: CurrentTrack;
217
- removed: {
218
- trackId: string;
219
- };
220
- snapshot: CurrentTrack[];
221
- status: AeroConnectionStatus;
222
- degraded: {
223
- reason: string;
224
- };
225
- error: AeroRecoverableError;
226
- "source.status": {
227
- sourceId: string;
228
- status: SourceStatus;
229
- };
230
- }> {
231
- private tracks;
232
- applyUpdate(track: CurrentTrack): void;
233
- applyRemoved(ev: {
234
- trackId: string;
235
- }): void;
236
- applySnapshot(tracks: CurrentTrack[]): void;
237
- getAll(): CurrentTrack[];
238
- get(id: string): CurrentTrack | undefined;
239
- get size(): number;
240
- clear(): void;
241
- getRenderable(): (CurrentTrack & {
242
- lat: number;
243
- lon: number;
244
- })[];
245
- }
246
-
247
- interface AeroSwarmOptions {
248
- endpoint: string;
249
- token: string;
250
- wsEndpoint?: string;
251
- reconnect?: boolean;
252
- maxReconnectDelay?: number;
253
- encoding?: "json" | "msgpack";
254
- staleAfterMs?: number;
255
- removeAfterMs?: number;
256
- onTokenExpired?: () => Promise<string>;
257
- }
258
- interface UseAeroTracksResult {
259
- tracks: CurrentTrack[];
260
- status: AeroConnectionStatus;
261
- sourceStatus: Record<string, string>;
262
- lastUpdateAt: number | null;
263
- error: AeroRecoverableError | null;
264
- }
265
- interface TrackHistoryPoint {
266
- t: number;
267
- lat: number | null;
268
- lon: number | null;
269
- altFt: number | null;
270
- speedKt: number | null;
271
- headingDeg: number | null;
272
- }
273
- interface DayAvailability {
274
- day: string;
275
- eventCount: number;
276
- trackCount: number;
277
- firstAt: string | null;
278
- lastAt: string | null;
279
- }
280
- declare class AeroSwarm {
281
- private readonly opts;
282
- readonly trpc: AeroTrpcClient;
283
- readonly rest: AeroRestClient;
284
- readonly graphql: AeroGraphQLClient;
285
- readonly cache: TrackCache;
286
- private readonly wsClient;
287
- private _status;
288
- private _lastUpdateAt;
289
- private _error;
290
- private _sourceStatus;
291
- private airportsCache;
292
- private static readonly AIRPORTS_CACHE_TTL_MS;
293
- /** Resolves which base URL (if any) should serve a federated call.
294
- * Returns null to mean "use AeroSwarm's own endpoint" — the normal,
295
- * unfederated path, which is also what a lookup failure falls back to
296
- * (a broken airports.list() call must never brick replay/history for
297
- * everyone else). */
298
- private resolveHistoryEndpoint;
299
- private federatedFetch;
300
- constructor(opts: AeroSwarmOptions);
301
- readonly tracks: {
302
- subscribe: (opts: SubscribeOptions, callbacks: TrackCallbacks) => (() => void);
303
- updateFilter: (opts: Partial<SubscribeOptions>) => void;
304
- stream: (opts: SubscribeOptions) => AsyncIterable<CurrentTrack>;
305
- snapshot: (input?: {
306
- airportId?: string;
307
- bbox?: Bbox;
308
- source?: "fused" | "normalized";
309
- minConfidence?: number;
310
- limit?: number;
311
- offset?: number;
312
- }) => any;
313
- /** Persisted position history for one track. If the airport has a
314
- * `historyEndpoint` (or `endpoint` is passed explicitly), this calls
315
- * that backend's `@aeroswarm/replay-server` instead of AeroSwarm's
316
- * own local database — see docs/development/federation/003-*.md. */
317
- history: (input: {
318
- trackId: string;
319
- airportId?: string;
320
- limit?: number;
321
- endpoint?: string;
322
- }) => Promise<TrackHistoryPoint[]>;
323
- };
324
- readonly sources: {
325
- /** Registers a source and returns its token, tied to `airportId` — a
326
- * source is a sensor physically at one airport, so this is required.
327
- * Anything that later authenticates with the returned token (WS
328
- * live/replay, tRPC calls) is scoped to that airport automatically,
329
- * with no need to keep passing `airportId` on every subsequent call.
330
- * For access spanning multiple airports, use the viewer/dashboard
331
- * token instead — it's never auto-scoped. */
332
- create: (body: {
333
- name: string;
334
- airportId: string;
335
- sourceType?: string;
336
- location?: {
337
- lat: number;
338
- lon: number;
339
- };
340
- config?: Record<string, unknown>;
341
- }) => Promise<{
342
- ok: boolean;
343
- data: {
344
- sourceId: string;
345
- sourceToken: string;
346
- airportId?: string | null;
347
- };
348
- }>;
349
- health: (sourceId: string) => any;
350
- list: (input?: {
351
- airportId?: string;
352
- }) => any;
353
- /** Reassigning `airportId` (including to `null` to clear it) re-caches
354
- * the source's token immediately, so the new scoping takes effect
355
- * without waiting out the token cache's TTL. */
356
- update: (input: {
357
- id: string;
358
- name?: string;
359
- airportId?: string | null;
360
- location?: {
361
- lat: number;
362
- lon: number;
363
- };
364
- config?: Record<string, unknown>;
365
- }) => any;
366
- delete: (id: string) => any;
367
- };
368
- /** Airport registry (multi-airport tenancy). */
369
- readonly airports: {
370
- list: () => Promise<{
371
- airports: {
372
- id: string;
373
- name: string;
374
- city: string | null;
375
- lat: number;
376
- lon: number;
377
- bbox: [number, number, number, number];
378
- timezone: string;
379
- /** Set = this airport serves its own replay/history (federated —
380
- * see docs/development/federation/003-*.md); `replay.*`/
381
- * `tracks.history` calls for it connect there directly instead of
382
- * AeroSwarm's own endpoint. null = AeroSwarm serves it (default). */
383
- historyEndpoint: string | null;
384
- }[];
385
- }>;
386
- create: (input: {
387
- id: string;
388
- name: string;
389
- city?: string;
390
- lat: number;
391
- lon: number;
392
- bbox: [number, number, number, number];
393
- timezone?: string;
394
- historyEndpoint?: string;
395
- }) => Promise<{
396
- ok: boolean;
397
- id: string;
398
- }>;
399
- update: (input: {
400
- id: string;
401
- name?: string;
402
- city?: string;
403
- lat?: number;
404
- lon?: number;
405
- bbox?: [number, number, number, number];
406
- timezone?: string;
407
- /** null clears it (reverts to AeroSwarm's own replay); omit to leave unchanged. */
408
- historyEndpoint?: string | null;
409
- }) => Promise<{
410
- ok: boolean;
411
- }>;
412
- delete: (input: {
413
- id: string;
414
- }) => Promise<{
415
- ok: boolean;
416
- }>;
417
- /** Surface geometry (runway/taxiway/apron GeoJSON) for the map. */
418
- surface: (input: {
419
- id: string;
420
- }) => Promise<{
421
- surface: unknown | null;
422
- }>;
423
- setSurface: (input: {
424
- id: string;
425
- surface: Record<string, unknown>;
426
- }) => Promise<{
427
- ok: boolean;
428
- }>;
429
- };
430
- readonly replay: {
431
- /** Data-availability calendar: which days have recorded tracks.
432
- * Use this to populate/validate replay date pickers — days not in
433
- * the list have no data. Federates to the airport's own backend when
434
- * it has a `historyEndpoint` (or `endpoint` is passed explicitly) —
435
- * see docs/development/federation/003-*.md. */
436
- days: (input?: {
437
- airportId?: string;
438
- lookbackDays?: number;
439
- endpoint?: string;
440
- }) => Promise<{
441
- days: DayAvailability[];
442
- }>;
443
- range: (input: {
444
- from: string;
445
- to: string;
446
- airportId?: string;
447
- endpoint?: string;
448
- }) => Promise<{
449
- earliest: string | null;
450
- latest: string | null;
451
- eventCount: number;
452
- trackCount: number;
453
- from: string;
454
- to: string;
455
- }>;
456
- /** Server-paced WS playback: streams historical tracks at `speed`× in
457
- * the same envelope format as the live stream. Returns a transport
458
- * handle (pause/resume/seek/stop) — a Promise, since resolving which
459
- * backend serves this airport (see docs/development/federation/003-*.md)
460
- * is async. See `AeroReplayClient` for the raw wire protocol if you
461
- * want to speak WS manually instead. */
462
- stream: (opts: ReplayStreamOptions & {
463
- endpoint?: string;
464
- }, callbacks: ReplayCallbacks) => Promise<ReplayHandle>;
465
- };
466
- /** Movement analytics: takeoffs/landings per hour + peak hour, recorded
467
- * by the pipeline's movement detector. */
468
- readonly analytics: {
469
- movements: (input?: {
470
- airportId?: string;
471
- day?: string;
472
- }) => Promise<{
473
- day: string;
474
- hours: {
475
- hour: string;
476
- takeoffs: number;
477
- landings: number;
478
- }[];
479
- totals: {
480
- takeoffs: number;
481
- landings: number;
482
- };
483
- peak: {
484
- hour: string;
485
- count: number;
486
- } | null;
487
- }>;
488
- };
489
- /** Route conformance: pre-defined ground routes + live track clearances.
490
- * Assign a route to a track and the safety engine can detect when it
491
- * strays beyond the route's corridor. */
492
- readonly routes: {
493
- list: (input?: {
494
- airportId?: string;
495
- }) => Promise<{
496
- routes: {
497
- routeId: string;
498
- airportId: string;
499
- name: string;
500
- kind: string;
501
- points: [number, number][];
502
- maxDeviationM: number;
503
- enabled: boolean;
504
- }[];
505
- }>;
506
- create: (input: {
507
- airportId: string;
508
- name: string;
509
- kind?: "taxi" | "departure" | "arrival";
510
- points: [number, number][];
511
- maxDeviationM?: number;
512
- }) => Promise<{
513
- id: string;
514
- }>;
515
- delete: (input: {
516
- id: string;
517
- }) => Promise<{
518
- ok: boolean;
519
- }>;
520
- assign: (input: {
521
- trackId: string;
522
- routeId: string;
523
- }) => Promise<{
524
- ok: boolean;
525
- }>;
526
- unassign: (input: {
527
- trackId: string;
528
- }) => Promise<{
529
- ok: boolean;
530
- }>;
531
- assignments: () => Promise<{
532
- assignments: {
533
- trackId: string;
534
- routeId: string;
535
- assignedAt: number;
536
- }[];
537
- }>;
538
- };
539
- readonly webhooks: {
540
- list: () => any;
541
- create: (body: {
542
- url: string;
543
- events: string[];
544
- secret?: string;
545
- }) => Promise<{
546
- ok: boolean;
547
- webhookId: string;
548
- }>;
549
- delete: (input: {
550
- id: string;
551
- }) => any;
552
- };
553
- readonly system: {
554
- stats: () => any;
555
- infraHealth: () => any;
556
- ingestHistory: () => any;
557
- getConfig: () => Promise<{
558
- key: string;
559
- value: string;
560
- updatedAt: string;
561
- }[]>;
562
- setConfig: (input: {
563
- key: string;
564
- value: string;
565
- }) => Promise<{
566
- ok: boolean;
567
- }>;
568
- clearTable: (input: {
569
- table: "fused_tracks" | "source_health";
570
- mode: "all" | "range";
571
- from?: string;
572
- to?: string;
573
- }) => any;
574
- };
575
- getStatus(): AeroConnectionStatus;
576
- getLastUpdateAt(): number | null;
577
- getError(): AeroRecoverableError | null;
578
- getSourceStatus(): Record<string, string>;
579
- disconnect(): void;
580
- }
581
-
582
- export { AeroReplayClient, AeroSwarm, type AeroSwarmOptions, AeroWsClient, type DayAvailability, type ReplayCallbacks, type ReplayHandle, type ReplayProgress, type ReplayStreamOptions, type SourceRow, type SubscribeOptions, TrackCache, type TrackCallbacks, type TrackHistoryPoint, type UseAeroTracksResult };
1
+ import '@aeroswarm/core/types';
2
+ export { A as AeroReplayClient, a as AeroSwarm, b as AeroSwarmOptions, c as AeroWsClient, d as AftnMessageRow, D as DayAvailability, F as FlightPlanRow, g as LiveTrackCallbacks, R as ReplayCallbacks, h as ReplayHandle, i as ReplayProgress, j as ReplayStreamOptions, S as SourceRow, k as StreamMeta, l as SubscribeOptions, T as TrackCache, m as TrackCallbacks, n as TrackHistoryPoint, U as UseAeroTracksResult } from '../index-BXiTmKdS.js';
3
+ import '@trpc/client';