fpv-laptimer-client 0.0.0-stage → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +674 -0
- package/README.md +218 -2
- package/package.json +40 -4
- package/src/index.js +16 -0
- package/src/laptimer.js +395 -0
- package/src/types.js +276 -0
- package/types/index.d.ts +13 -0
- package/types/laptimer.d.ts +253 -0
- package/types/types.d.ts +368 -0
package/src/types.js
ADDED
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
// The JSON the server sends (the laptimer's docs/api/datenformate.md and events.md).
|
|
3
|
+
// Only typedefs: this file exports nothing at runtime.
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* A channel (a seat on the page: slot 1 = index 0).
|
|
7
|
+
* @typedef {object} Channel
|
|
8
|
+
* @property {number} index 0, 1, …
|
|
9
|
+
* @property {string} name "R1" … "R8" or the MHz as text
|
|
10
|
+
* @property {number} mhz
|
|
11
|
+
* @property {boolean} enabled false: does not fly along
|
|
12
|
+
* @property {number | null} pilot_id
|
|
13
|
+
* @property {string | null} pilot callsign of the assigned pilot
|
|
14
|
+
* @property {string} label free text instead of a pilot (wins over `pilot`)
|
|
15
|
+
* @property {string} shown what the page shows
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @typedef {object} Pilot
|
|
20
|
+
* @property {number} id
|
|
21
|
+
* @property {string} name callsign (unique)
|
|
22
|
+
* @property {string} full_name may be empty
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* One seat's result in a session (`results`, in seat order).
|
|
27
|
+
* @typedef {object} SeatResult
|
|
28
|
+
* @property {number} channel
|
|
29
|
+
* @property {string} channel_name
|
|
30
|
+
* @property {number} mhz
|
|
31
|
+
* @property {number | null} pilot_id
|
|
32
|
+
* @property {string} pilot shown name: callsign, free text or channel name
|
|
33
|
+
* @property {number | null} position null without a lap
|
|
34
|
+
* @property {number} laps
|
|
35
|
+
* @property {number[]} lap_times_us
|
|
36
|
+
* @property {number | null} last_lap_us
|
|
37
|
+
* @property {number | null} best_lap_us
|
|
38
|
+
* @property {number | null} best_consecutive_us
|
|
39
|
+
* @property {number | null} total_us
|
|
40
|
+
* @property {number | null} holeshot_us
|
|
41
|
+
* @property {boolean} finished
|
|
42
|
+
*/
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* A pass through the gate (only in `sessions.get`).
|
|
46
|
+
* @typedef {object} Pass
|
|
47
|
+
* @property {number} id
|
|
48
|
+
* @property {number} channel
|
|
49
|
+
* @property {number} time_us
|
|
50
|
+
* @property {number} peak_db
|
|
51
|
+
* @property {boolean} manual added by hand
|
|
52
|
+
* @property {boolean} deleted
|
|
53
|
+
* @property {number} gate 0 = finish, 1 = arming gate
|
|
54
|
+
* @property {'no_arming' | null} [rejected] deleted by the server, and why
|
|
55
|
+
* @property {number | null} [detected_us] where the detector put it (null: added by hand; differs from time_us: moved by hand)
|
|
56
|
+
*/
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* A correction of a pass (only in `sessions.get`, as `changes`).
|
|
60
|
+
* @typedef {object} PassChange
|
|
61
|
+
* @property {number | null} pass_id
|
|
62
|
+
* @property {number} channel
|
|
63
|
+
* @property {'add' | 'delete' | 'restore' | 'move' | 'reject'} action
|
|
64
|
+
* @property {number | null} from_us
|
|
65
|
+
* @property {number | null} to_us
|
|
66
|
+
* @property {string | null} rejected
|
|
67
|
+
* @property {boolean} live while the session ran
|
|
68
|
+
* @property {'seite' | 'editor' | 'api' | 'server'} origin
|
|
69
|
+
* @property {number} at_us
|
|
70
|
+
*/
|
|
71
|
+
|
|
72
|
+
/** @typedef {'holeshot' | 'from_start' | 'flying'} StartMode */
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* @typedef {object} Session
|
|
76
|
+
* @property {number} id
|
|
77
|
+
* @property {boolean} race false = training
|
|
78
|
+
* @property {'countdown' | 'running' | 'ended'} phase
|
|
79
|
+
* @property {StartMode} start_mode
|
|
80
|
+
* @property {'open' | 'laps' | 'seconds'} end
|
|
81
|
+
* @property {number | null} end_value
|
|
82
|
+
* @property {number} countdown_s
|
|
83
|
+
* @property {number} min_lap_s
|
|
84
|
+
* @property {number} created_us
|
|
85
|
+
* @property {number} start_us
|
|
86
|
+
* @property {number | null} ended_us
|
|
87
|
+
* @property {number | null} confirmed_us
|
|
88
|
+
* @property {boolean} needs_confirmation
|
|
89
|
+
* @property {SeatResult[]} results
|
|
90
|
+
* @property {Pass[]} [passes] only from `sessions.get`
|
|
91
|
+
* @property {PassChange[]} [changes] only from `sessions.get`: every correction of a pass
|
|
92
|
+
*/
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* @typedef {object} Board
|
|
96
|
+
* @property {string} id
|
|
97
|
+
* @property {string} port
|
|
98
|
+
* @property {string} role finish, arming or off
|
|
99
|
+
* @property {boolean} ok
|
|
100
|
+
* @property {string | null} error
|
|
101
|
+
* @property {number} levels_per_s
|
|
102
|
+
*/
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* @typedef {object} State
|
|
106
|
+
* @property {number} now_us
|
|
107
|
+
* @property {Session | null} session the running session
|
|
108
|
+
* @property {Session | null} unconfirmed the ended race waiting for confirmation
|
|
109
|
+
* @property {Channel[]} channels
|
|
110
|
+
* @property {Pilot[]} pilots
|
|
111
|
+
* @property {Board[]} boards
|
|
112
|
+
*/
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* @typedef {object} PilotStats
|
|
116
|
+
* @property {number | null} pilot_id
|
|
117
|
+
* @property {string} label
|
|
118
|
+
* @property {number | null} best_lap_us
|
|
119
|
+
* @property {number | null} best_consecutive_us
|
|
120
|
+
* @property {number} sessions
|
|
121
|
+
* @property {number} races
|
|
122
|
+
* @property {number} wins
|
|
123
|
+
*/
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* @typedef {object} Stats
|
|
127
|
+
* @property {number} today_races
|
|
128
|
+
* @property {PilotStats[]} all best lap first
|
|
129
|
+
* @property {PilotStats[]} today
|
|
130
|
+
*/
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Arguments of `session.start`.
|
|
134
|
+
* @typedef {object} StartOptions
|
|
135
|
+
* @property {boolean} [race] true: race, false: training (default)
|
|
136
|
+
* @property {number} [laps] race: end after this many laps (1–100)
|
|
137
|
+
* @property {number} [seconds] race: end after this many seconds (10–3600), instead of laps
|
|
138
|
+
* @property {number} [countdown_s] race: 0–60
|
|
139
|
+
* @property {StartMode} [start_mode] race
|
|
140
|
+
*/
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Arguments of `channels.set` (only given fields change).
|
|
144
|
+
* @typedef {object} ChannelChange
|
|
145
|
+
* @property {string | null} [pilot] callsign, null = nobody
|
|
146
|
+
* @property {number | null} [pilot_id] instead of `pilot`
|
|
147
|
+
* @property {boolean} [create] make an unknown callsign a pilot
|
|
148
|
+
* @property {string} [label] free text instead of a pilot ("" = none)
|
|
149
|
+
* @property {boolean} [enabled]
|
|
150
|
+
* @property {string | number} [frequency] "R1" … "R8" or MHz (5300–6000); not during a session
|
|
151
|
+
*/
|
|
152
|
+
|
|
153
|
+
// --- events ---
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Fields every event about a pilot has ("Sitz" in docs/api/events.md).
|
|
157
|
+
* @typedef {object} Seat
|
|
158
|
+
* @property {number} channel
|
|
159
|
+
* @property {string} channel_name
|
|
160
|
+
* @property {number} mhz
|
|
161
|
+
* @property {number | null} pilot_id
|
|
162
|
+
* @property {string} pilot
|
|
163
|
+
*/
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* @typedef {object} SessionStartedData
|
|
167
|
+
* @property {number} session_id
|
|
168
|
+
* @property {boolean} race
|
|
169
|
+
* @property {StartMode} start_mode
|
|
170
|
+
* @property {'open' | 'laps' | 'seconds'} end
|
|
171
|
+
* @property {number | null} end_value
|
|
172
|
+
* @property {number} countdown_s
|
|
173
|
+
* @property {number} created_us
|
|
174
|
+
* @property {number} start_us
|
|
175
|
+
* @property {number} pilot_count
|
|
176
|
+
* @property {Seat[]} seats
|
|
177
|
+
*/
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* @typedef {object} CountdownData
|
|
181
|
+
* @property {number} session_id
|
|
182
|
+
* @property {boolean} race
|
|
183
|
+
* @property {number} seconds_left
|
|
184
|
+
*/
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* @typedef {object} StartSignalData
|
|
188
|
+
* @property {number} session_id
|
|
189
|
+
* @property {boolean} race
|
|
190
|
+
* @property {number} start_us
|
|
191
|
+
*/
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* @typedef {Seat & {
|
|
195
|
+
* session_id: number, race: boolean, pass_id: number, time_us: number, manual: boolean,
|
|
196
|
+
* kind: 'lap' | 'holeshot' | 'start' | 'ignored',
|
|
197
|
+
* lap: number | null, lap_time_us: number | null, lap_time_s: number | null, laps: number,
|
|
198
|
+
* best_lap_us: number | null, best_lap_s: number | null, personal_best: boolean, session_best: boolean,
|
|
199
|
+
* holeshot_us: number | null, finished: boolean, position: number | null
|
|
200
|
+
* }} PilotPassData
|
|
201
|
+
*/
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* @typedef {Seat & {
|
|
205
|
+
* session_id: number, laps: number, total_us: number | null, total_s: number | null,
|
|
206
|
+
* best_lap_us: number | null, best_lap_s: number | null, position: number | null
|
|
207
|
+
* }} PilotFinishedData
|
|
208
|
+
*/
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* @typedef {object} SessionStoppedData
|
|
212
|
+
* @property {number} session_id
|
|
213
|
+
* @property {boolean} race
|
|
214
|
+
* @property {'stopped' | 'finished' | 'cancelled'} reason
|
|
215
|
+
* @property {number} ended_us
|
|
216
|
+
* @property {boolean} needs_confirmation
|
|
217
|
+
* @property {string | null} winner race only
|
|
218
|
+
* @property {SeatResult[]} results
|
|
219
|
+
*/
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* @typedef {object} RaceConfirmedData
|
|
223
|
+
* @property {number} session_id
|
|
224
|
+
* @property {boolean} race
|
|
225
|
+
* @property {number} confirmed_us
|
|
226
|
+
* @property {string | null} winner
|
|
227
|
+
* @property {SeatResult[]} results
|
|
228
|
+
*/
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* @typedef {object} PilotEventData
|
|
232
|
+
* @property {number} pilot_id
|
|
233
|
+
* @property {string} name
|
|
234
|
+
* @property {string} [full_name] not in pilot_deleted
|
|
235
|
+
*/
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Event name → its `data`.
|
|
239
|
+
* @typedef {object} EventMap
|
|
240
|
+
* @property {SessionStartedData} session_started
|
|
241
|
+
* @property {CountdownData} countdown
|
|
242
|
+
* @property {StartSignalData} start_signal
|
|
243
|
+
* @property {PilotPassData} pilot_pass
|
|
244
|
+
* @property {PilotFinishedData} pilot_finished
|
|
245
|
+
* @property {SessionStoppedData} session_stopped
|
|
246
|
+
* @property {RaceConfirmedData} race_confirmed
|
|
247
|
+
* @property {{session_id: number, race: boolean}} session_deleted
|
|
248
|
+
* @property {{deleted: number}} sessions_reset
|
|
249
|
+
* @property {{session_id: number}} passes_changed
|
|
250
|
+
* @property {{channels: Channel[]}} channels_changed
|
|
251
|
+
* @property {PilotEventData} pilot_added
|
|
252
|
+
* @property {PilotEventData} pilot_updated
|
|
253
|
+
* @property {PilotEventData} pilot_deleted
|
|
254
|
+
*/
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* An event as it comes over the WebSocket.
|
|
258
|
+
* @template [D=any]
|
|
259
|
+
* @typedef {object} EventMessage
|
|
260
|
+
* @property {'event'} type
|
|
261
|
+
* @property {string} event
|
|
262
|
+
* @property {number} seq counts from 1 since the server started; a gap = missed events
|
|
263
|
+
* @property {number} time_us
|
|
264
|
+
* @property {D} data
|
|
265
|
+
*/
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* The first message after connecting.
|
|
269
|
+
* @typedef {object} Hello
|
|
270
|
+
* @property {'hello'} type
|
|
271
|
+
* @property {number} api
|
|
272
|
+
* @property {State | null} state
|
|
273
|
+
* @property {string[]} commands
|
|
274
|
+
*/
|
|
275
|
+
|
|
276
|
+
export {}
|
package/types/index.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export type Channel = import("./types.js").Channel;
|
|
2
|
+
export type ChannelChange = import("./types.js").ChannelChange;
|
|
3
|
+
export type Pilot = import("./types.js").Pilot;
|
|
4
|
+
export type Session = import("./types.js").Session;
|
|
5
|
+
export type SeatResult = import("./types.js").SeatResult;
|
|
6
|
+
export type Pass = import("./types.js").Pass;
|
|
7
|
+
export type State = import("./types.js").State;
|
|
8
|
+
export type Stats = import("./types.js").Stats;
|
|
9
|
+
export type StartOptions = import("./types.js").StartOptions;
|
|
10
|
+
export type EventMap = import("./types.js").EventMap;
|
|
11
|
+
export type PilotPassData = import("./types.js").PilotPassData;
|
|
12
|
+
export type SessionStoppedData = import("./types.js").SessionStoppedData;
|
|
13
|
+
export { Laptimer, LaptimerError } from "./laptimer.js";
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
/** @import { Channel, ChannelChange, EventMap, EventMessage, Hello, Pilot, Session, StartOptions, State, Stats } from './types.js' */
|
|
2
|
+
/**
|
|
3
|
+
* An error from the server (its message is German, meant to be shown as is)
|
|
4
|
+
* or from the connection.
|
|
5
|
+
*/
|
|
6
|
+
export class LaptimerError extends Error {
|
|
7
|
+
/**
|
|
8
|
+
* @param {string} message
|
|
9
|
+
* @param {object} [info]
|
|
10
|
+
* @param {number | null} [info.status] HTTP status (400 bad arguments, 401 key, 404 unknown command), the same over the WebSocket; null without an answer
|
|
11
|
+
* @param {string} [info.command]
|
|
12
|
+
* @param {unknown} [info.cause]
|
|
13
|
+
*/
|
|
14
|
+
constructor(message: string, { status, command, cause }?: {
|
|
15
|
+
status?: number | null | undefined;
|
|
16
|
+
command?: string | undefined;
|
|
17
|
+
cause?: unknown;
|
|
18
|
+
});
|
|
19
|
+
/** @type {number | null} */
|
|
20
|
+
status: number | null;
|
|
21
|
+
/** @type {string | undefined} */
|
|
22
|
+
command: string | undefined;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* What `on` can listen to: the server's events, `'*'` (every event) and
|
|
26
|
+
* three of the client's own: `hello` (connected, with the state),
|
|
27
|
+
* `lagged` (events were lost: fetch the state again) and `disconnected`.
|
|
28
|
+
* @typedef {EventMap & {
|
|
29
|
+
* '*': any,
|
|
30
|
+
* hello: Hello,
|
|
31
|
+
* lagged: {missed: number},
|
|
32
|
+
* disconnected: {code: number, reason: string},
|
|
33
|
+
* }} ClientEvents
|
|
34
|
+
*/
|
|
35
|
+
/**
|
|
36
|
+
* @template {string} K
|
|
37
|
+
* @typedef {(data: K extends keyof ClientEvents ? ClientEvents[K] : any, msg: EventMessage<any> | any) => void} Listener
|
|
38
|
+
*/
|
|
39
|
+
/**
|
|
40
|
+
* @typedef {object} LaptimerOptions
|
|
41
|
+
* @property {string} url e.g. "http://pi:5800"
|
|
42
|
+
* @property {string} key API key "lt_…"
|
|
43
|
+
* @property {'auto' | 'http'} [transport] auto (default): commands over the WebSocket while it is connected, else HTTP
|
|
44
|
+
* @property {number} [timeout_ms] per command, default 15000
|
|
45
|
+
* @property {number} [max_backoff_ms] longest wait between reconnects, default 10000
|
|
46
|
+
*/
|
|
47
|
+
export class Laptimer {
|
|
48
|
+
/** @param {LaptimerOptions} options */
|
|
49
|
+
constructor({ url, key, transport, timeout_ms, max_backoff_ms }: LaptimerOptions);
|
|
50
|
+
url: string;
|
|
51
|
+
key: string;
|
|
52
|
+
transport: "auto" | "http";
|
|
53
|
+
timeout_ms: number;
|
|
54
|
+
max_backoff_ms: number;
|
|
55
|
+
channels: {
|
|
56
|
+
/** @returns {Promise<Channel[]>} */
|
|
57
|
+
list: () => Promise<Channel[]>;
|
|
58
|
+
/**
|
|
59
|
+
* One channel; only the given fields change. A pilot or frequency another
|
|
60
|
+
* channel has is swapped with it.
|
|
61
|
+
* @param {number} index 0 = first channel
|
|
62
|
+
* @param {ChannelChange} change
|
|
63
|
+
* @returns {Promise<Channel>}
|
|
64
|
+
*/
|
|
65
|
+
set: (index: number, change: ChannelChange) => Promise<Channel>;
|
|
66
|
+
/**
|
|
67
|
+
* All channels at once, in order: callsign, pilot id or null per channel.
|
|
68
|
+
* @param {(string | number | null)[]} pilots
|
|
69
|
+
* @param {{create?: boolean}} [options] create: make unknown callsigns pilots
|
|
70
|
+
* @returns {Promise<Channel[]>}
|
|
71
|
+
*/
|
|
72
|
+
assign: (pilots: (string | number | null)[], { create }?: {
|
|
73
|
+
create?: boolean;
|
|
74
|
+
}) => Promise<Channel[]>;
|
|
75
|
+
};
|
|
76
|
+
pilots: {
|
|
77
|
+
/** @returns {Promise<Pilot[]>} */
|
|
78
|
+
list: () => Promise<Pilot[]>;
|
|
79
|
+
/** @param {number} id @returns {Promise<Pilot>} */
|
|
80
|
+
get: (id: number) => Promise<Pilot>;
|
|
81
|
+
/** @param {string} name callsign @param {string} [full_name] @returns {Promise<Pilot>} */
|
|
82
|
+
create: (name: string, full_name?: string) => Promise<Pilot>;
|
|
83
|
+
/** @param {number} id @param {{name?: string, full_name?: string}} change @returns {Promise<Pilot>} */
|
|
84
|
+
update: (id: number, change: {
|
|
85
|
+
name?: string;
|
|
86
|
+
full_name?: string;
|
|
87
|
+
}) => Promise<Pilot>;
|
|
88
|
+
/** @param {number} id @returns {Promise<{}>} */
|
|
89
|
+
delete: (id: number) => Promise<{}>;
|
|
90
|
+
};
|
|
91
|
+
session: {
|
|
92
|
+
/**
|
|
93
|
+
* Starts a training (default) or a race. Refused while a session runs or
|
|
94
|
+
* a race waits for confirmation.
|
|
95
|
+
* @param {StartOptions} [options]
|
|
96
|
+
* @returns {Promise<Session>}
|
|
97
|
+
*/
|
|
98
|
+
start: (options?: StartOptions) => Promise<Session>;
|
|
99
|
+
/** @returns {Promise<{session_id: number}>} */
|
|
100
|
+
stop: () => Promise<{
|
|
101
|
+
session_id: number;
|
|
102
|
+
}>;
|
|
103
|
+
/** Stops and deletes the running session. @returns {Promise<{session_id: number}>} */
|
|
104
|
+
cancel: () => Promise<{
|
|
105
|
+
session_id: number;
|
|
106
|
+
}>;
|
|
107
|
+
/** @param {number} [id] default: the race waiting for confirmation @returns {Promise<{session_id: number}>} */
|
|
108
|
+
confirm: (id?: number) => Promise<{
|
|
109
|
+
session_id: number;
|
|
110
|
+
}>;
|
|
111
|
+
/** Deletes an ended session. @param {number} [id] default: the race waiting for confirmation @returns {Promise<{session_id: number}>} */
|
|
112
|
+
discard: (id?: number) => Promise<{
|
|
113
|
+
session_id: number;
|
|
114
|
+
}>;
|
|
115
|
+
};
|
|
116
|
+
sessions: {
|
|
117
|
+
/** @param {{limit?: number}} [options] default 20 @returns {Promise<Session[]>} */
|
|
118
|
+
list: ({ limit }?: {
|
|
119
|
+
limit?: number;
|
|
120
|
+
}) => Promise<Session[]>;
|
|
121
|
+
/** With every pass (`passes`). @param {number} id @returns {Promise<Session>} */
|
|
122
|
+
get: (id: number) => Promise<Session>;
|
|
123
|
+
};
|
|
124
|
+
passes: {
|
|
125
|
+
/**
|
|
126
|
+
* A pass by hand: counts like a detected one (announcement, `pilot_pass`).
|
|
127
|
+
* @param {number} channel index
|
|
128
|
+
* @param {{time_us?: number, session_id?: number}} [options] default: now, the running session
|
|
129
|
+
* @returns {Promise<{pass_id: number, session_id: number, time_us: number}>}
|
|
130
|
+
*/
|
|
131
|
+
add: (channel: number, { time_us, session_id }?: {
|
|
132
|
+
time_us?: number;
|
|
133
|
+
session_id?: number;
|
|
134
|
+
}) => Promise<{
|
|
135
|
+
pass_id: number;
|
|
136
|
+
session_id: number;
|
|
137
|
+
time_us: number;
|
|
138
|
+
}>;
|
|
139
|
+
/** Detected passes are only marked (restorable). @param {number} id @returns {Promise<{session_id: number}>} */
|
|
140
|
+
delete: (id: number) => Promise<{
|
|
141
|
+
session_id: number;
|
|
142
|
+
}>;
|
|
143
|
+
/** @param {number} id @returns {Promise<{session_id: number}>} */
|
|
144
|
+
restore: (id: number) => Promise<{
|
|
145
|
+
session_id: number;
|
|
146
|
+
}>;
|
|
147
|
+
};
|
|
148
|
+
/** @returns {Promise<{pong: true, now_us: number}>} */
|
|
149
|
+
ping(): Promise<{
|
|
150
|
+
pong: true;
|
|
151
|
+
now_us: number;
|
|
152
|
+
}>;
|
|
153
|
+
/** @returns {Promise<State>} */
|
|
154
|
+
state(): Promise<State>;
|
|
155
|
+
/** @returns {Promise<Stats>} */
|
|
156
|
+
stats(): Promise<Stats>;
|
|
157
|
+
/** True while the WebSocket is connected (and commands go over it with transport "auto"). */
|
|
158
|
+
get connected(): boolean;
|
|
159
|
+
/**
|
|
160
|
+
* Any command by name, e.g. `command('session.start', {race: true})`.
|
|
161
|
+
* Arguments that are `undefined` are left out.
|
|
162
|
+
* @param {string} name
|
|
163
|
+
* @param {Record<string, unknown>} [args]
|
|
164
|
+
* @returns {Promise<any>}
|
|
165
|
+
*/
|
|
166
|
+
command(name: string, args?: Record<string, unknown>): Promise<any>;
|
|
167
|
+
/**
|
|
168
|
+
* Listens to an event (`pilot_pass`, …), to `'*'` (every server event) or
|
|
169
|
+
* to `hello`, `lagged`, `disconnected`. The listener gets `data` and the
|
|
170
|
+
* whole message. Returns a function that removes the listener.
|
|
171
|
+
* @template {string} K
|
|
172
|
+
* @param {K} name
|
|
173
|
+
* @param {Listener<K>} fn
|
|
174
|
+
* @returns {() => void}
|
|
175
|
+
*/
|
|
176
|
+
on<K extends string>(name: K, fn: Listener<K>): () => void;
|
|
177
|
+
/** @template {string} K @param {K} name @param {Listener<K>} fn */
|
|
178
|
+
off<K extends string>(name: K, fn: Listener<K>): void;
|
|
179
|
+
/**
|
|
180
|
+
* Waits for the next event `name` for which `filter` is true (or for any,
|
|
181
|
+
* without a filter); resolves with its data. Rejects after `timeout_ms`.
|
|
182
|
+
* Needs a connected WebSocket (`connect()`).
|
|
183
|
+
* @template {string} K
|
|
184
|
+
* @param {K} name
|
|
185
|
+
* @param {{filter?: (data: K extends keyof ClientEvents ? ClientEvents[K] : any) => boolean, timeout_ms?: number}} [options]
|
|
186
|
+
* @returns {Promise<K extends keyof ClientEvents ? ClientEvents[K] : any>}
|
|
187
|
+
*/
|
|
188
|
+
waitFor<K extends string>(name: K, { filter, timeout_ms }?: {
|
|
189
|
+
filter?: (data: K extends keyof ClientEvents ? ClientEvents[K] : any) => boolean;
|
|
190
|
+
timeout_ms?: number;
|
|
191
|
+
}): Promise<K extends keyof ClientEvents ? ClientEvents[K] : any>;
|
|
192
|
+
/**
|
|
193
|
+
* Opens the WebSocket; resolves with the `hello` (contains `state`).
|
|
194
|
+
* Once connected, a dropped connection is opened again by itself (waiting
|
|
195
|
+
* 0.5 s, 1 s, 2 s, … up to `max_backoff_ms`) until `close()`. If the very
|
|
196
|
+
* first attempt fails (wrong address or key), the promise rejects and
|
|
197
|
+
* nothing is retried.
|
|
198
|
+
* @returns {Promise<Hello>}
|
|
199
|
+
*/
|
|
200
|
+
connect(): Promise<Hello>;
|
|
201
|
+
/** Closes the WebSocket for good (no reconnect). Commands go over HTTP again. */
|
|
202
|
+
close(): void;
|
|
203
|
+
#private;
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* What `on` can listen to: the server's events, `'*'` (every event) and
|
|
207
|
+
* three of the client's own: `hello` (connected, with the state),
|
|
208
|
+
* `lagged` (events were lost: fetch the state again) and `disconnected`.
|
|
209
|
+
*/
|
|
210
|
+
export type ClientEvents = EventMap & {
|
|
211
|
+
"*": any;
|
|
212
|
+
hello: Hello;
|
|
213
|
+
lagged: {
|
|
214
|
+
missed: number;
|
|
215
|
+
};
|
|
216
|
+
disconnected: {
|
|
217
|
+
code: number;
|
|
218
|
+
reason: string;
|
|
219
|
+
};
|
|
220
|
+
};
|
|
221
|
+
export type Listener<K extends string> = (data: K extends keyof ClientEvents ? ClientEvents[K] : any, msg: EventMessage<any> | any) => void;
|
|
222
|
+
export type LaptimerOptions = {
|
|
223
|
+
/**
|
|
224
|
+
* e.g. "http://pi:5800"
|
|
225
|
+
*/
|
|
226
|
+
url: string;
|
|
227
|
+
/**
|
|
228
|
+
* API key "lt_…"
|
|
229
|
+
*/
|
|
230
|
+
key: string;
|
|
231
|
+
/**
|
|
232
|
+
* auto (default): commands over the WebSocket while it is connected, else HTTP
|
|
233
|
+
*/
|
|
234
|
+
transport?: "auto" | "http" | undefined;
|
|
235
|
+
/**
|
|
236
|
+
* per command, default 15000
|
|
237
|
+
*/
|
|
238
|
+
timeout_ms?: number | undefined;
|
|
239
|
+
/**
|
|
240
|
+
* longest wait between reconnects, default 10000
|
|
241
|
+
*/
|
|
242
|
+
max_backoff_ms?: number | undefined;
|
|
243
|
+
};
|
|
244
|
+
import type { Channel } from './types.js';
|
|
245
|
+
import type { ChannelChange } from './types.js';
|
|
246
|
+
import type { Pilot } from './types.js';
|
|
247
|
+
import type { StartOptions } from './types.js';
|
|
248
|
+
import type { Session } from './types.js';
|
|
249
|
+
import type { State } from './types.js';
|
|
250
|
+
import type { Stats } from './types.js';
|
|
251
|
+
import type { Hello } from './types.js';
|
|
252
|
+
import type { EventMap } from './types.js';
|
|
253
|
+
import type { EventMessage } from './types.js';
|