@sixfathoms/lplex 0.2.1 → 0.4.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/dist/index.cjs +167 -12
- package/dist/index.d.cts +139 -2
- package/dist/index.d.ts +139 -2
- package/dist/index.js +167 -12
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -89,8 +89,16 @@ async function* parseSSE(body) {
|
|
|
89
89
|
}
|
|
90
90
|
}
|
|
91
91
|
function classify(obj) {
|
|
92
|
-
if ("type" in obj
|
|
93
|
-
|
|
92
|
+
if ("type" in obj) {
|
|
93
|
+
if (obj.type === "device") {
|
|
94
|
+
return { type: "device", device: obj };
|
|
95
|
+
}
|
|
96
|
+
if (obj.type === "device_removed") {
|
|
97
|
+
return {
|
|
98
|
+
type: "device_removed",
|
|
99
|
+
deviceRemoved: obj
|
|
100
|
+
};
|
|
101
|
+
}
|
|
94
102
|
}
|
|
95
103
|
if ("seq" in obj) {
|
|
96
104
|
return { type: "frame", frame: obj };
|
|
@@ -185,14 +193,27 @@ var Client = class {
|
|
|
185
193
|
/**
|
|
186
194
|
* Open an ephemeral SSE stream with optional filtering.
|
|
187
195
|
* No session, no replay, no ACK.
|
|
196
|
+
*
|
|
197
|
+
* Accepts either a Filter (for backwards compatibility) or
|
|
198
|
+
* SubscribeOptions for additional control (decode, signal).
|
|
188
199
|
*/
|
|
189
|
-
async subscribe(
|
|
200
|
+
async subscribe(filterOrOptions, signal) {
|
|
201
|
+
let filter;
|
|
202
|
+
let decode;
|
|
203
|
+
let sig = signal;
|
|
204
|
+
if (filterOrOptions && isSubscribeOptions(filterOrOptions)) {
|
|
205
|
+
filter = filterOrOptions.filter;
|
|
206
|
+
decode = filterOrOptions.decode;
|
|
207
|
+
sig = filterOrOptions.signal ?? sig;
|
|
208
|
+
} else {
|
|
209
|
+
filter = filterOrOptions;
|
|
210
|
+
}
|
|
190
211
|
let url = `${this.#baseURL}/events`;
|
|
191
|
-
const qs = filterToQueryString(filter);
|
|
212
|
+
const qs = filterToQueryString(filter, decode);
|
|
192
213
|
if (qs) url += `?${qs}`;
|
|
193
214
|
const resp = await this.#fetch(url, {
|
|
194
215
|
headers: { Accept: "text/event-stream" },
|
|
195
|
-
signal
|
|
216
|
+
signal: sig
|
|
196
217
|
});
|
|
197
218
|
if (!resp.ok) {
|
|
198
219
|
const body = await resp.text();
|
|
@@ -203,6 +224,18 @@ var Client = class {
|
|
|
203
224
|
}
|
|
204
225
|
return parseSSE(resp.body);
|
|
205
226
|
}
|
|
227
|
+
/** Fetch the last-seen decoded values for each (device, PGN) pair. */
|
|
228
|
+
async decodedValues(filter, signal) {
|
|
229
|
+
let url = `${this.#baseURL}/values/decoded`;
|
|
230
|
+
const qs = filterToQueryString(filter);
|
|
231
|
+
if (qs) url += `?${qs}`;
|
|
232
|
+
const resp = await this.#fetch(url, { signal });
|
|
233
|
+
if (!resp.ok) {
|
|
234
|
+
const body = await resp.text();
|
|
235
|
+
throw new HttpError("GET", url, resp.status, body);
|
|
236
|
+
}
|
|
237
|
+
return resp.json();
|
|
238
|
+
}
|
|
206
239
|
/** Transmit a CAN frame through the server. */
|
|
207
240
|
async send(params, signal) {
|
|
208
241
|
const url = `${this.#baseURL}/send`;
|
|
@@ -217,6 +250,85 @@ var Client = class {
|
|
|
217
250
|
throw new HttpError("POST", url, resp.status, body);
|
|
218
251
|
}
|
|
219
252
|
}
|
|
253
|
+
/**
|
|
254
|
+
* Send an ISO Request (PGN 59904) and wait for the response frame.
|
|
255
|
+
* Returns the response frame, or throws HttpError on timeout (408).
|
|
256
|
+
*/
|
|
257
|
+
async query(params, signal) {
|
|
258
|
+
const url = `${this.#baseURL}/query`;
|
|
259
|
+
const resp = await this.#fetch(url, {
|
|
260
|
+
method: "POST",
|
|
261
|
+
headers: { "Content-Type": "application/json" },
|
|
262
|
+
body: JSON.stringify(params),
|
|
263
|
+
signal
|
|
264
|
+
});
|
|
265
|
+
if (!resp.ok) {
|
|
266
|
+
const body = await resp.text();
|
|
267
|
+
throw new HttpError("POST", url, resp.status, body);
|
|
268
|
+
}
|
|
269
|
+
return resp.json();
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Query historical frames (requires journaling on the server).
|
|
273
|
+
* Returns an array of frames matching the query parameters.
|
|
274
|
+
*/
|
|
275
|
+
async history(params, signal) {
|
|
276
|
+
const qs = new URLSearchParams();
|
|
277
|
+
qs.set("from", params.from);
|
|
278
|
+
if (params.to) qs.set("to", params.to);
|
|
279
|
+
for (const p of params.pgn ?? []) qs.append("pgn", p.toString());
|
|
280
|
+
for (const s of params.src ?? []) qs.append("src", s.toString());
|
|
281
|
+
if (params.limit !== void 0) qs.set("limit", params.limit.toString());
|
|
282
|
+
if (params.interval) qs.set("interval", params.interval);
|
|
283
|
+
if (params.decode) qs.set("decode", "true");
|
|
284
|
+
const url = `${this.#baseURL}/history?${qs.toString()}`;
|
|
285
|
+
const resp = await this.#fetch(url, { signal });
|
|
286
|
+
if (!resp.ok) {
|
|
287
|
+
const body = await resp.text();
|
|
288
|
+
throw new HttpError("GET", url, resp.status, body);
|
|
289
|
+
}
|
|
290
|
+
return resp.json();
|
|
291
|
+
}
|
|
292
|
+
/** Check server health (GET /healthz). */
|
|
293
|
+
async health(signal) {
|
|
294
|
+
const url = `${this.#baseURL}/healthz`;
|
|
295
|
+
const resp = await this.#fetch(url, { signal });
|
|
296
|
+
if (!resp.ok) {
|
|
297
|
+
const body = await resp.text();
|
|
298
|
+
throw new HttpError("GET", url, resp.status, body);
|
|
299
|
+
}
|
|
300
|
+
return resp.json();
|
|
301
|
+
}
|
|
302
|
+
/** Liveness probe (GET /livez). */
|
|
303
|
+
async liveness(signal) {
|
|
304
|
+
const url = `${this.#baseURL}/livez`;
|
|
305
|
+
const resp = await this.#fetch(url, { signal });
|
|
306
|
+
if (!resp.ok) {
|
|
307
|
+
const body = await resp.text();
|
|
308
|
+
throw new HttpError("GET", url, resp.status, body);
|
|
309
|
+
}
|
|
310
|
+
return resp.json();
|
|
311
|
+
}
|
|
312
|
+
/** Readiness probe (GET /readyz). */
|
|
313
|
+
async readiness(signal) {
|
|
314
|
+
const url = `${this.#baseURL}/readyz`;
|
|
315
|
+
const resp = await this.#fetch(url, { signal });
|
|
316
|
+
if (!resp.ok) {
|
|
317
|
+
const body = await resp.text();
|
|
318
|
+
throw new HttpError("GET", url, resp.status, body);
|
|
319
|
+
}
|
|
320
|
+
return resp.json();
|
|
321
|
+
}
|
|
322
|
+
/** Fetch boat-side replication status (only available when replication is configured). */
|
|
323
|
+
async replicationStatus(signal) {
|
|
324
|
+
const url = `${this.#baseURL}/replication/status`;
|
|
325
|
+
const resp = await this.#fetch(url, { signal });
|
|
326
|
+
if (!resp.ok) {
|
|
327
|
+
const body = await resp.text();
|
|
328
|
+
throw new HttpError("GET", url, resp.status, body);
|
|
329
|
+
}
|
|
330
|
+
return resp.json();
|
|
331
|
+
}
|
|
220
332
|
/** Create or reconnect a buffered session on the server. */
|
|
221
333
|
async createSession(config, signal) {
|
|
222
334
|
const url = `${this.#baseURL}/clients/${config.clientId}`;
|
|
@@ -240,24 +352,37 @@ var Client = class {
|
|
|
240
352
|
return new Session(this.#baseURL, this.#fetch, info);
|
|
241
353
|
}
|
|
242
354
|
};
|
|
355
|
+
function isSubscribeOptions(obj) {
|
|
356
|
+
return "decode" in obj || "signal" in obj || "filter" in obj;
|
|
357
|
+
}
|
|
243
358
|
function filterIsEmpty(f) {
|
|
244
|
-
return !f.pgn?.length && !f.manufacturer?.length && !f.instance?.length && !f.name?.length;
|
|
359
|
+
return !f.pgn?.length && !f.exclude_pgn?.length && !f.manufacturer?.length && !f.instance?.length && !f.name?.length && !f.exclude_name?.length && !f.bus?.length;
|
|
245
360
|
}
|
|
246
|
-
function filterToQueryString(f) {
|
|
247
|
-
if (!f || filterIsEmpty(f)) return "";
|
|
361
|
+
function filterToQueryString(f, decode) {
|
|
362
|
+
if ((!f || filterIsEmpty(f)) && !decode) return "";
|
|
248
363
|
const params = new URLSearchParams();
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
364
|
+
if (f) {
|
|
365
|
+
for (const p of f.pgn ?? []) params.append("pgn", p.toString());
|
|
366
|
+
for (const p of f.exclude_pgn ?? [])
|
|
367
|
+
params.append("exclude_pgn", p.toString());
|
|
368
|
+
for (const m of f.manufacturer ?? []) params.append("manufacturer", m);
|
|
369
|
+
for (const i of f.instance ?? []) params.append("instance", i.toString());
|
|
370
|
+
for (const n of f.name ?? []) params.append("name", n);
|
|
371
|
+
for (const n of f.exclude_name ?? []) params.append("exclude_name", n);
|
|
372
|
+
for (const b of f.bus ?? []) params.append("bus", b);
|
|
373
|
+
}
|
|
374
|
+
if (decode) params.set("decode", "true");
|
|
253
375
|
return params.toString();
|
|
254
376
|
}
|
|
255
377
|
function filterToJSON(f) {
|
|
256
378
|
const m = {};
|
|
257
379
|
if (f.pgn?.length) m.pgn = f.pgn;
|
|
380
|
+
if (f.exclude_pgn?.length) m.exclude_pgn = f.exclude_pgn;
|
|
258
381
|
if (f.manufacturer?.length) m.manufacturer = f.manufacturer;
|
|
259
382
|
if (f.instance?.length) m.instance = f.instance;
|
|
260
383
|
if (f.name?.length) m.name = f.name;
|
|
384
|
+
if (f.exclude_name?.length) m.exclude_name = f.exclude_name;
|
|
385
|
+
if (f.bus?.length) m.bus = f.bus;
|
|
261
386
|
return m;
|
|
262
387
|
}
|
|
263
388
|
|
|
@@ -313,6 +438,36 @@ var CloudClient = class {
|
|
|
313
438
|
}
|
|
314
439
|
return resp.json();
|
|
315
440
|
}
|
|
441
|
+
/** Check cloud server health (GET /healthz). */
|
|
442
|
+
async health(signal) {
|
|
443
|
+
const url = `${this.#baseURL}/healthz`;
|
|
444
|
+
const resp = await this.#fetch(url, { signal });
|
|
445
|
+
if (!resp.ok) {
|
|
446
|
+
const body = await resp.text();
|
|
447
|
+
throw new HttpError("GET", url, resp.status, body);
|
|
448
|
+
}
|
|
449
|
+
return resp.json();
|
|
450
|
+
}
|
|
451
|
+
/** Liveness probe (GET /livez). */
|
|
452
|
+
async liveness(signal) {
|
|
453
|
+
const url = `${this.#baseURL}/livez`;
|
|
454
|
+
const resp = await this.#fetch(url, { signal });
|
|
455
|
+
if (!resp.ok) {
|
|
456
|
+
const body = await resp.text();
|
|
457
|
+
throw new HttpError("GET", url, resp.status, body);
|
|
458
|
+
}
|
|
459
|
+
return resp.json();
|
|
460
|
+
}
|
|
461
|
+
/** Readiness probe (GET /readyz). */
|
|
462
|
+
async readiness(signal) {
|
|
463
|
+
const url = `${this.#baseURL}/readyz`;
|
|
464
|
+
const resp = await this.#fetch(url, { signal });
|
|
465
|
+
if (!resp.ok) {
|
|
466
|
+
const body = await resp.text();
|
|
467
|
+
throw new HttpError("GET", url, resp.status, body);
|
|
468
|
+
}
|
|
469
|
+
return resp.json();
|
|
470
|
+
}
|
|
316
471
|
};
|
|
317
472
|
// Annotate the CommonJS export names for ESM import in node:
|
|
318
473
|
0 && (module.exports = {
|
package/dist/index.d.cts
CHANGED
|
@@ -2,14 +2,17 @@
|
|
|
2
2
|
interface Frame {
|
|
3
3
|
seq: number;
|
|
4
4
|
ts: string;
|
|
5
|
+
bus?: string;
|
|
5
6
|
prio: number;
|
|
6
7
|
pgn: number;
|
|
7
8
|
src: number;
|
|
8
9
|
dst: number;
|
|
9
10
|
data: string;
|
|
11
|
+
decoded?: Record<string, unknown>;
|
|
10
12
|
}
|
|
11
13
|
/** An NMEA 2000 device discovered on the bus. */
|
|
12
14
|
interface Device {
|
|
15
|
+
bus?: string;
|
|
13
16
|
src: number;
|
|
14
17
|
name: string;
|
|
15
18
|
manufacturer: string;
|
|
@@ -28,6 +31,12 @@ interface Device {
|
|
|
28
31
|
packet_count: number;
|
|
29
32
|
byte_count: number;
|
|
30
33
|
}
|
|
34
|
+
/** A device-removed notification from the bus. */
|
|
35
|
+
interface DeviceRemoved {
|
|
36
|
+
type: "device_removed";
|
|
37
|
+
bus?: string;
|
|
38
|
+
src: number;
|
|
39
|
+
}
|
|
31
40
|
/** Discriminated union for SSE events. */
|
|
32
41
|
type Event = {
|
|
33
42
|
type: "frame";
|
|
@@ -35,6 +44,9 @@ type Event = {
|
|
|
35
44
|
} | {
|
|
36
45
|
type: "device";
|
|
37
46
|
device: Device;
|
|
47
|
+
} | {
|
|
48
|
+
type: "device_removed";
|
|
49
|
+
deviceRemoved: DeviceRemoved;
|
|
38
50
|
};
|
|
39
51
|
/**
|
|
40
52
|
* Filter for CAN frames.
|
|
@@ -42,9 +54,19 @@ type Event = {
|
|
|
42
54
|
*/
|
|
43
55
|
interface Filter {
|
|
44
56
|
pgn?: number[];
|
|
57
|
+
exclude_pgn?: number[];
|
|
45
58
|
manufacturer?: string[];
|
|
46
59
|
instance?: number[];
|
|
47
60
|
name?: string[];
|
|
61
|
+
exclude_name?: string[];
|
|
62
|
+
bus?: string[];
|
|
63
|
+
}
|
|
64
|
+
/** Options for ephemeral SSE subscription. */
|
|
65
|
+
interface SubscribeOptions {
|
|
66
|
+
filter?: Filter;
|
|
67
|
+
/** When true, frames include decoded field values. */
|
|
68
|
+
decode?: boolean;
|
|
69
|
+
signal?: AbortSignal;
|
|
48
70
|
}
|
|
49
71
|
/** Configuration for creating a buffered session. */
|
|
50
72
|
interface SessionConfig {
|
|
@@ -66,6 +88,7 @@ interface SendParams {
|
|
|
66
88
|
dst: number;
|
|
67
89
|
prio: number;
|
|
68
90
|
data: string;
|
|
91
|
+
bus?: string;
|
|
69
92
|
}
|
|
70
93
|
/** A single PGN's last-known value for a device. */
|
|
71
94
|
interface PGNValue {
|
|
@@ -82,6 +105,91 @@ interface DeviceValues {
|
|
|
82
105
|
model_id?: string;
|
|
83
106
|
values: PGNValue[];
|
|
84
107
|
}
|
|
108
|
+
/** A single PGN's decoded value for a device. */
|
|
109
|
+
interface DecodedPGNValue {
|
|
110
|
+
pgn: number;
|
|
111
|
+
ts: string;
|
|
112
|
+
data: string;
|
|
113
|
+
seq: number;
|
|
114
|
+
decoded: Record<string, unknown>;
|
|
115
|
+
}
|
|
116
|
+
/** Decoded values grouped by device. */
|
|
117
|
+
interface DecodedDeviceValues {
|
|
118
|
+
name: string;
|
|
119
|
+
src: number;
|
|
120
|
+
manufacturer?: string;
|
|
121
|
+
model_id?: string;
|
|
122
|
+
values: DecodedPGNValue[];
|
|
123
|
+
}
|
|
124
|
+
/** Parameters for an ISO Request query (POST /query). */
|
|
125
|
+
interface QueryParams {
|
|
126
|
+
pgn: number;
|
|
127
|
+
/** Destination address. Defaults to 0xFF (broadcast) on the server. */
|
|
128
|
+
dst?: number;
|
|
129
|
+
timeout?: string;
|
|
130
|
+
bus?: string;
|
|
131
|
+
}
|
|
132
|
+
/** Parameters for historical data query (GET /history). */
|
|
133
|
+
interface HistoryParams {
|
|
134
|
+
/** Start timestamp (RFC 3339). */
|
|
135
|
+
from: string;
|
|
136
|
+
/** End timestamp (RFC 3339). Defaults to now. */
|
|
137
|
+
to?: string;
|
|
138
|
+
/** Filter by PGN(s). */
|
|
139
|
+
pgn?: number[];
|
|
140
|
+
/** Filter by source address(es). */
|
|
141
|
+
src?: number[];
|
|
142
|
+
/** Max frames to return. Defaults to 10000. */
|
|
143
|
+
limit?: number;
|
|
144
|
+
/** Downsample interval (e.g. "1s", "PT1M"). */
|
|
145
|
+
interval?: string;
|
|
146
|
+
/** Include decoded values in response. */
|
|
147
|
+
decode?: boolean;
|
|
148
|
+
}
|
|
149
|
+
/** Broker health details. */
|
|
150
|
+
interface BrokerHealth {
|
|
151
|
+
status: string;
|
|
152
|
+
frames_total: number;
|
|
153
|
+
head_seq: number;
|
|
154
|
+
last_frame_time: string;
|
|
155
|
+
device_count: number;
|
|
156
|
+
ring_entries: number;
|
|
157
|
+
ring_capacity: number;
|
|
158
|
+
}
|
|
159
|
+
/** Replication component health (within health response). */
|
|
160
|
+
interface ReplicationHealth {
|
|
161
|
+
status: string;
|
|
162
|
+
connected: boolean;
|
|
163
|
+
live_lag: number;
|
|
164
|
+
backfill_remaining_seqs: number;
|
|
165
|
+
last_ack: string;
|
|
166
|
+
}
|
|
167
|
+
/** Health check response from GET /healthz or /readyz. */
|
|
168
|
+
interface HealthStatus {
|
|
169
|
+
status: string;
|
|
170
|
+
broker?: BrokerHealth;
|
|
171
|
+
replication?: ReplicationHealth;
|
|
172
|
+
components?: Record<string, unknown>;
|
|
173
|
+
/** Cloud-only: total known instances. */
|
|
174
|
+
instances_total?: number;
|
|
175
|
+
/** Cloud-only: currently connected instances. */
|
|
176
|
+
instances_connected?: number;
|
|
177
|
+
}
|
|
178
|
+
/** Boat-side replication status from GET /replication/status. */
|
|
179
|
+
interface ReplicationStatus {
|
|
180
|
+
connected: boolean;
|
|
181
|
+
instance_id: string;
|
|
182
|
+
local_head_seq: number;
|
|
183
|
+
cloud_cursor: number;
|
|
184
|
+
holes: SeqRange[];
|
|
185
|
+
live_lag: number;
|
|
186
|
+
backfill_remaining_seqs: number;
|
|
187
|
+
last_ack: string;
|
|
188
|
+
live_frames_sent: number;
|
|
189
|
+
backfill_blocks_sent: number;
|
|
190
|
+
backfill_bytes_sent: number;
|
|
191
|
+
reconnects: number;
|
|
192
|
+
}
|
|
85
193
|
/** Summary of a cloud instance, returned by GET /instances. */
|
|
86
194
|
interface InstanceSummary {
|
|
87
195
|
id: string;
|
|
@@ -147,10 +255,33 @@ declare class Client {
|
|
|
147
255
|
/**
|
|
148
256
|
* Open an ephemeral SSE stream with optional filtering.
|
|
149
257
|
* No session, no replay, no ACK.
|
|
258
|
+
*
|
|
259
|
+
* Accepts either a Filter (for backwards compatibility) or
|
|
260
|
+
* SubscribeOptions for additional control (decode, signal).
|
|
150
261
|
*/
|
|
151
|
-
subscribe(
|
|
262
|
+
subscribe(filterOrOptions?: Filter | SubscribeOptions, signal?: AbortSignal): Promise<AsyncIterable<Event>>;
|
|
263
|
+
/** Fetch the last-seen decoded values for each (device, PGN) pair. */
|
|
264
|
+
decodedValues(filter?: Filter, signal?: AbortSignal): Promise<DecodedDeviceValues[]>;
|
|
152
265
|
/** Transmit a CAN frame through the server. */
|
|
153
266
|
send(params: SendParams, signal?: AbortSignal): Promise<void>;
|
|
267
|
+
/**
|
|
268
|
+
* Send an ISO Request (PGN 59904) and wait for the response frame.
|
|
269
|
+
* Returns the response frame, or throws HttpError on timeout (408).
|
|
270
|
+
*/
|
|
271
|
+
query(params: QueryParams, signal?: AbortSignal): Promise<Frame>;
|
|
272
|
+
/**
|
|
273
|
+
* Query historical frames (requires journaling on the server).
|
|
274
|
+
* Returns an array of frames matching the query parameters.
|
|
275
|
+
*/
|
|
276
|
+
history(params: HistoryParams, signal?: AbortSignal): Promise<Frame[]>;
|
|
277
|
+
/** Check server health (GET /healthz). */
|
|
278
|
+
health(signal?: AbortSignal): Promise<HealthStatus>;
|
|
279
|
+
/** Liveness probe (GET /livez). */
|
|
280
|
+
liveness(signal?: AbortSignal): Promise<HealthStatus>;
|
|
281
|
+
/** Readiness probe (GET /readyz). */
|
|
282
|
+
readiness(signal?: AbortSignal): Promise<HealthStatus>;
|
|
283
|
+
/** Fetch boat-side replication status (only available when replication is configured). */
|
|
284
|
+
replicationStatus(signal?: AbortSignal): Promise<ReplicationStatus>;
|
|
154
285
|
/** Create or reconnect a buffered session on the server. */
|
|
155
286
|
createSession(config: SessionConfig, signal?: AbortSignal): Promise<Session>;
|
|
156
287
|
}
|
|
@@ -180,6 +311,12 @@ declare class CloudClient {
|
|
|
180
311
|
status(instanceId: string, signal?: AbortSignal): Promise<InstanceStatus>;
|
|
181
312
|
/** Fetch recent replication diagnostic events for an instance. */
|
|
182
313
|
replicationEvents(instanceId: string, limit?: number, signal?: AbortSignal): Promise<ReplicationEvent[]>;
|
|
314
|
+
/** Check cloud server health (GET /healthz). */
|
|
315
|
+
health(signal?: AbortSignal): Promise<HealthStatus>;
|
|
316
|
+
/** Liveness probe (GET /livez). */
|
|
317
|
+
liveness(signal?: AbortSignal): Promise<HealthStatus>;
|
|
318
|
+
/** Readiness probe (GET /readyz). */
|
|
319
|
+
readiness(signal?: AbortSignal): Promise<HealthStatus>;
|
|
183
320
|
}
|
|
184
321
|
|
|
185
322
|
declare class LplexError extends Error {
|
|
@@ -191,4 +328,4 @@ declare class HttpError extends LplexError {
|
|
|
191
328
|
constructor(method: string, path: string, status: number, body: string);
|
|
192
329
|
}
|
|
193
330
|
|
|
194
|
-
export { Client, type ClientOptions, CloudClient, type CloudClientOptions, type Device, type DeviceValues, type Event, type Filter, type Frame, HttpError, type InstanceStatus, type InstanceSummary, LplexError, type PGNValue, type ReplicationEvent, type ReplicationEventType, type SendParams, type SeqRange, Session, type SessionConfig, type SessionInfo };
|
|
331
|
+
export { type BrokerHealth, Client, type ClientOptions, CloudClient, type CloudClientOptions, type DecodedDeviceValues, type DecodedPGNValue, type Device, type DeviceRemoved, type DeviceValues, type Event, type Filter, type Frame, type HealthStatus, type HistoryParams, HttpError, type InstanceStatus, type InstanceSummary, LplexError, type PGNValue, type QueryParams, type ReplicationEvent, type ReplicationEventType, type ReplicationHealth, type ReplicationStatus, type SendParams, type SeqRange, Session, type SessionConfig, type SessionInfo, type SubscribeOptions };
|
package/dist/index.d.ts
CHANGED
|
@@ -2,14 +2,17 @@
|
|
|
2
2
|
interface Frame {
|
|
3
3
|
seq: number;
|
|
4
4
|
ts: string;
|
|
5
|
+
bus?: string;
|
|
5
6
|
prio: number;
|
|
6
7
|
pgn: number;
|
|
7
8
|
src: number;
|
|
8
9
|
dst: number;
|
|
9
10
|
data: string;
|
|
11
|
+
decoded?: Record<string, unknown>;
|
|
10
12
|
}
|
|
11
13
|
/** An NMEA 2000 device discovered on the bus. */
|
|
12
14
|
interface Device {
|
|
15
|
+
bus?: string;
|
|
13
16
|
src: number;
|
|
14
17
|
name: string;
|
|
15
18
|
manufacturer: string;
|
|
@@ -28,6 +31,12 @@ interface Device {
|
|
|
28
31
|
packet_count: number;
|
|
29
32
|
byte_count: number;
|
|
30
33
|
}
|
|
34
|
+
/** A device-removed notification from the bus. */
|
|
35
|
+
interface DeviceRemoved {
|
|
36
|
+
type: "device_removed";
|
|
37
|
+
bus?: string;
|
|
38
|
+
src: number;
|
|
39
|
+
}
|
|
31
40
|
/** Discriminated union for SSE events. */
|
|
32
41
|
type Event = {
|
|
33
42
|
type: "frame";
|
|
@@ -35,6 +44,9 @@ type Event = {
|
|
|
35
44
|
} | {
|
|
36
45
|
type: "device";
|
|
37
46
|
device: Device;
|
|
47
|
+
} | {
|
|
48
|
+
type: "device_removed";
|
|
49
|
+
deviceRemoved: DeviceRemoved;
|
|
38
50
|
};
|
|
39
51
|
/**
|
|
40
52
|
* Filter for CAN frames.
|
|
@@ -42,9 +54,19 @@ type Event = {
|
|
|
42
54
|
*/
|
|
43
55
|
interface Filter {
|
|
44
56
|
pgn?: number[];
|
|
57
|
+
exclude_pgn?: number[];
|
|
45
58
|
manufacturer?: string[];
|
|
46
59
|
instance?: number[];
|
|
47
60
|
name?: string[];
|
|
61
|
+
exclude_name?: string[];
|
|
62
|
+
bus?: string[];
|
|
63
|
+
}
|
|
64
|
+
/** Options for ephemeral SSE subscription. */
|
|
65
|
+
interface SubscribeOptions {
|
|
66
|
+
filter?: Filter;
|
|
67
|
+
/** When true, frames include decoded field values. */
|
|
68
|
+
decode?: boolean;
|
|
69
|
+
signal?: AbortSignal;
|
|
48
70
|
}
|
|
49
71
|
/** Configuration for creating a buffered session. */
|
|
50
72
|
interface SessionConfig {
|
|
@@ -66,6 +88,7 @@ interface SendParams {
|
|
|
66
88
|
dst: number;
|
|
67
89
|
prio: number;
|
|
68
90
|
data: string;
|
|
91
|
+
bus?: string;
|
|
69
92
|
}
|
|
70
93
|
/** A single PGN's last-known value for a device. */
|
|
71
94
|
interface PGNValue {
|
|
@@ -82,6 +105,91 @@ interface DeviceValues {
|
|
|
82
105
|
model_id?: string;
|
|
83
106
|
values: PGNValue[];
|
|
84
107
|
}
|
|
108
|
+
/** A single PGN's decoded value for a device. */
|
|
109
|
+
interface DecodedPGNValue {
|
|
110
|
+
pgn: number;
|
|
111
|
+
ts: string;
|
|
112
|
+
data: string;
|
|
113
|
+
seq: number;
|
|
114
|
+
decoded: Record<string, unknown>;
|
|
115
|
+
}
|
|
116
|
+
/** Decoded values grouped by device. */
|
|
117
|
+
interface DecodedDeviceValues {
|
|
118
|
+
name: string;
|
|
119
|
+
src: number;
|
|
120
|
+
manufacturer?: string;
|
|
121
|
+
model_id?: string;
|
|
122
|
+
values: DecodedPGNValue[];
|
|
123
|
+
}
|
|
124
|
+
/** Parameters for an ISO Request query (POST /query). */
|
|
125
|
+
interface QueryParams {
|
|
126
|
+
pgn: number;
|
|
127
|
+
/** Destination address. Defaults to 0xFF (broadcast) on the server. */
|
|
128
|
+
dst?: number;
|
|
129
|
+
timeout?: string;
|
|
130
|
+
bus?: string;
|
|
131
|
+
}
|
|
132
|
+
/** Parameters for historical data query (GET /history). */
|
|
133
|
+
interface HistoryParams {
|
|
134
|
+
/** Start timestamp (RFC 3339). */
|
|
135
|
+
from: string;
|
|
136
|
+
/** End timestamp (RFC 3339). Defaults to now. */
|
|
137
|
+
to?: string;
|
|
138
|
+
/** Filter by PGN(s). */
|
|
139
|
+
pgn?: number[];
|
|
140
|
+
/** Filter by source address(es). */
|
|
141
|
+
src?: number[];
|
|
142
|
+
/** Max frames to return. Defaults to 10000. */
|
|
143
|
+
limit?: number;
|
|
144
|
+
/** Downsample interval (e.g. "1s", "PT1M"). */
|
|
145
|
+
interval?: string;
|
|
146
|
+
/** Include decoded values in response. */
|
|
147
|
+
decode?: boolean;
|
|
148
|
+
}
|
|
149
|
+
/** Broker health details. */
|
|
150
|
+
interface BrokerHealth {
|
|
151
|
+
status: string;
|
|
152
|
+
frames_total: number;
|
|
153
|
+
head_seq: number;
|
|
154
|
+
last_frame_time: string;
|
|
155
|
+
device_count: number;
|
|
156
|
+
ring_entries: number;
|
|
157
|
+
ring_capacity: number;
|
|
158
|
+
}
|
|
159
|
+
/** Replication component health (within health response). */
|
|
160
|
+
interface ReplicationHealth {
|
|
161
|
+
status: string;
|
|
162
|
+
connected: boolean;
|
|
163
|
+
live_lag: number;
|
|
164
|
+
backfill_remaining_seqs: number;
|
|
165
|
+
last_ack: string;
|
|
166
|
+
}
|
|
167
|
+
/** Health check response from GET /healthz or /readyz. */
|
|
168
|
+
interface HealthStatus {
|
|
169
|
+
status: string;
|
|
170
|
+
broker?: BrokerHealth;
|
|
171
|
+
replication?: ReplicationHealth;
|
|
172
|
+
components?: Record<string, unknown>;
|
|
173
|
+
/** Cloud-only: total known instances. */
|
|
174
|
+
instances_total?: number;
|
|
175
|
+
/** Cloud-only: currently connected instances. */
|
|
176
|
+
instances_connected?: number;
|
|
177
|
+
}
|
|
178
|
+
/** Boat-side replication status from GET /replication/status. */
|
|
179
|
+
interface ReplicationStatus {
|
|
180
|
+
connected: boolean;
|
|
181
|
+
instance_id: string;
|
|
182
|
+
local_head_seq: number;
|
|
183
|
+
cloud_cursor: number;
|
|
184
|
+
holes: SeqRange[];
|
|
185
|
+
live_lag: number;
|
|
186
|
+
backfill_remaining_seqs: number;
|
|
187
|
+
last_ack: string;
|
|
188
|
+
live_frames_sent: number;
|
|
189
|
+
backfill_blocks_sent: number;
|
|
190
|
+
backfill_bytes_sent: number;
|
|
191
|
+
reconnects: number;
|
|
192
|
+
}
|
|
85
193
|
/** Summary of a cloud instance, returned by GET /instances. */
|
|
86
194
|
interface InstanceSummary {
|
|
87
195
|
id: string;
|
|
@@ -147,10 +255,33 @@ declare class Client {
|
|
|
147
255
|
/**
|
|
148
256
|
* Open an ephemeral SSE stream with optional filtering.
|
|
149
257
|
* No session, no replay, no ACK.
|
|
258
|
+
*
|
|
259
|
+
* Accepts either a Filter (for backwards compatibility) or
|
|
260
|
+
* SubscribeOptions for additional control (decode, signal).
|
|
150
261
|
*/
|
|
151
|
-
subscribe(
|
|
262
|
+
subscribe(filterOrOptions?: Filter | SubscribeOptions, signal?: AbortSignal): Promise<AsyncIterable<Event>>;
|
|
263
|
+
/** Fetch the last-seen decoded values for each (device, PGN) pair. */
|
|
264
|
+
decodedValues(filter?: Filter, signal?: AbortSignal): Promise<DecodedDeviceValues[]>;
|
|
152
265
|
/** Transmit a CAN frame through the server. */
|
|
153
266
|
send(params: SendParams, signal?: AbortSignal): Promise<void>;
|
|
267
|
+
/**
|
|
268
|
+
* Send an ISO Request (PGN 59904) and wait for the response frame.
|
|
269
|
+
* Returns the response frame, or throws HttpError on timeout (408).
|
|
270
|
+
*/
|
|
271
|
+
query(params: QueryParams, signal?: AbortSignal): Promise<Frame>;
|
|
272
|
+
/**
|
|
273
|
+
* Query historical frames (requires journaling on the server).
|
|
274
|
+
* Returns an array of frames matching the query parameters.
|
|
275
|
+
*/
|
|
276
|
+
history(params: HistoryParams, signal?: AbortSignal): Promise<Frame[]>;
|
|
277
|
+
/** Check server health (GET /healthz). */
|
|
278
|
+
health(signal?: AbortSignal): Promise<HealthStatus>;
|
|
279
|
+
/** Liveness probe (GET /livez). */
|
|
280
|
+
liveness(signal?: AbortSignal): Promise<HealthStatus>;
|
|
281
|
+
/** Readiness probe (GET /readyz). */
|
|
282
|
+
readiness(signal?: AbortSignal): Promise<HealthStatus>;
|
|
283
|
+
/** Fetch boat-side replication status (only available when replication is configured). */
|
|
284
|
+
replicationStatus(signal?: AbortSignal): Promise<ReplicationStatus>;
|
|
154
285
|
/** Create or reconnect a buffered session on the server. */
|
|
155
286
|
createSession(config: SessionConfig, signal?: AbortSignal): Promise<Session>;
|
|
156
287
|
}
|
|
@@ -180,6 +311,12 @@ declare class CloudClient {
|
|
|
180
311
|
status(instanceId: string, signal?: AbortSignal): Promise<InstanceStatus>;
|
|
181
312
|
/** Fetch recent replication diagnostic events for an instance. */
|
|
182
313
|
replicationEvents(instanceId: string, limit?: number, signal?: AbortSignal): Promise<ReplicationEvent[]>;
|
|
314
|
+
/** Check cloud server health (GET /healthz). */
|
|
315
|
+
health(signal?: AbortSignal): Promise<HealthStatus>;
|
|
316
|
+
/** Liveness probe (GET /livez). */
|
|
317
|
+
liveness(signal?: AbortSignal): Promise<HealthStatus>;
|
|
318
|
+
/** Readiness probe (GET /readyz). */
|
|
319
|
+
readiness(signal?: AbortSignal): Promise<HealthStatus>;
|
|
183
320
|
}
|
|
184
321
|
|
|
185
322
|
declare class LplexError extends Error {
|
|
@@ -191,4 +328,4 @@ declare class HttpError extends LplexError {
|
|
|
191
328
|
constructor(method: string, path: string, status: number, body: string);
|
|
192
329
|
}
|
|
193
330
|
|
|
194
|
-
export { Client, type ClientOptions, CloudClient, type CloudClientOptions, type Device, type DeviceValues, type Event, type Filter, type Frame, HttpError, type InstanceStatus, type InstanceSummary, LplexError, type PGNValue, type ReplicationEvent, type ReplicationEventType, type SendParams, type SeqRange, Session, type SessionConfig, type SessionInfo };
|
|
331
|
+
export { type BrokerHealth, Client, type ClientOptions, CloudClient, type CloudClientOptions, type DecodedDeviceValues, type DecodedPGNValue, type Device, type DeviceRemoved, type DeviceValues, type Event, type Filter, type Frame, type HealthStatus, type HistoryParams, HttpError, type InstanceStatus, type InstanceSummary, LplexError, type PGNValue, type QueryParams, type ReplicationEvent, type ReplicationEventType, type ReplicationHealth, type ReplicationStatus, type SendParams, type SeqRange, Session, type SessionConfig, type SessionInfo, type SubscribeOptions };
|
package/dist/index.js
CHANGED
|
@@ -59,8 +59,16 @@ async function* parseSSE(body) {
|
|
|
59
59
|
}
|
|
60
60
|
}
|
|
61
61
|
function classify(obj) {
|
|
62
|
-
if ("type" in obj
|
|
63
|
-
|
|
62
|
+
if ("type" in obj) {
|
|
63
|
+
if (obj.type === "device") {
|
|
64
|
+
return { type: "device", device: obj };
|
|
65
|
+
}
|
|
66
|
+
if (obj.type === "device_removed") {
|
|
67
|
+
return {
|
|
68
|
+
type: "device_removed",
|
|
69
|
+
deviceRemoved: obj
|
|
70
|
+
};
|
|
71
|
+
}
|
|
64
72
|
}
|
|
65
73
|
if ("seq" in obj) {
|
|
66
74
|
return { type: "frame", frame: obj };
|
|
@@ -155,14 +163,27 @@ var Client = class {
|
|
|
155
163
|
/**
|
|
156
164
|
* Open an ephemeral SSE stream with optional filtering.
|
|
157
165
|
* No session, no replay, no ACK.
|
|
166
|
+
*
|
|
167
|
+
* Accepts either a Filter (for backwards compatibility) or
|
|
168
|
+
* SubscribeOptions for additional control (decode, signal).
|
|
158
169
|
*/
|
|
159
|
-
async subscribe(
|
|
170
|
+
async subscribe(filterOrOptions, signal) {
|
|
171
|
+
let filter;
|
|
172
|
+
let decode;
|
|
173
|
+
let sig = signal;
|
|
174
|
+
if (filterOrOptions && isSubscribeOptions(filterOrOptions)) {
|
|
175
|
+
filter = filterOrOptions.filter;
|
|
176
|
+
decode = filterOrOptions.decode;
|
|
177
|
+
sig = filterOrOptions.signal ?? sig;
|
|
178
|
+
} else {
|
|
179
|
+
filter = filterOrOptions;
|
|
180
|
+
}
|
|
160
181
|
let url = `${this.#baseURL}/events`;
|
|
161
|
-
const qs = filterToQueryString(filter);
|
|
182
|
+
const qs = filterToQueryString(filter, decode);
|
|
162
183
|
if (qs) url += `?${qs}`;
|
|
163
184
|
const resp = await this.#fetch(url, {
|
|
164
185
|
headers: { Accept: "text/event-stream" },
|
|
165
|
-
signal
|
|
186
|
+
signal: sig
|
|
166
187
|
});
|
|
167
188
|
if (!resp.ok) {
|
|
168
189
|
const body = await resp.text();
|
|
@@ -173,6 +194,18 @@ var Client = class {
|
|
|
173
194
|
}
|
|
174
195
|
return parseSSE(resp.body);
|
|
175
196
|
}
|
|
197
|
+
/** Fetch the last-seen decoded values for each (device, PGN) pair. */
|
|
198
|
+
async decodedValues(filter, signal) {
|
|
199
|
+
let url = `${this.#baseURL}/values/decoded`;
|
|
200
|
+
const qs = filterToQueryString(filter);
|
|
201
|
+
if (qs) url += `?${qs}`;
|
|
202
|
+
const resp = await this.#fetch(url, { signal });
|
|
203
|
+
if (!resp.ok) {
|
|
204
|
+
const body = await resp.text();
|
|
205
|
+
throw new HttpError("GET", url, resp.status, body);
|
|
206
|
+
}
|
|
207
|
+
return resp.json();
|
|
208
|
+
}
|
|
176
209
|
/** Transmit a CAN frame through the server. */
|
|
177
210
|
async send(params, signal) {
|
|
178
211
|
const url = `${this.#baseURL}/send`;
|
|
@@ -187,6 +220,85 @@ var Client = class {
|
|
|
187
220
|
throw new HttpError("POST", url, resp.status, body);
|
|
188
221
|
}
|
|
189
222
|
}
|
|
223
|
+
/**
|
|
224
|
+
* Send an ISO Request (PGN 59904) and wait for the response frame.
|
|
225
|
+
* Returns the response frame, or throws HttpError on timeout (408).
|
|
226
|
+
*/
|
|
227
|
+
async query(params, signal) {
|
|
228
|
+
const url = `${this.#baseURL}/query`;
|
|
229
|
+
const resp = await this.#fetch(url, {
|
|
230
|
+
method: "POST",
|
|
231
|
+
headers: { "Content-Type": "application/json" },
|
|
232
|
+
body: JSON.stringify(params),
|
|
233
|
+
signal
|
|
234
|
+
});
|
|
235
|
+
if (!resp.ok) {
|
|
236
|
+
const body = await resp.text();
|
|
237
|
+
throw new HttpError("POST", url, resp.status, body);
|
|
238
|
+
}
|
|
239
|
+
return resp.json();
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Query historical frames (requires journaling on the server).
|
|
243
|
+
* Returns an array of frames matching the query parameters.
|
|
244
|
+
*/
|
|
245
|
+
async history(params, signal) {
|
|
246
|
+
const qs = new URLSearchParams();
|
|
247
|
+
qs.set("from", params.from);
|
|
248
|
+
if (params.to) qs.set("to", params.to);
|
|
249
|
+
for (const p of params.pgn ?? []) qs.append("pgn", p.toString());
|
|
250
|
+
for (const s of params.src ?? []) qs.append("src", s.toString());
|
|
251
|
+
if (params.limit !== void 0) qs.set("limit", params.limit.toString());
|
|
252
|
+
if (params.interval) qs.set("interval", params.interval);
|
|
253
|
+
if (params.decode) qs.set("decode", "true");
|
|
254
|
+
const url = `${this.#baseURL}/history?${qs.toString()}`;
|
|
255
|
+
const resp = await this.#fetch(url, { signal });
|
|
256
|
+
if (!resp.ok) {
|
|
257
|
+
const body = await resp.text();
|
|
258
|
+
throw new HttpError("GET", url, resp.status, body);
|
|
259
|
+
}
|
|
260
|
+
return resp.json();
|
|
261
|
+
}
|
|
262
|
+
/** Check server health (GET /healthz). */
|
|
263
|
+
async health(signal) {
|
|
264
|
+
const url = `${this.#baseURL}/healthz`;
|
|
265
|
+
const resp = await this.#fetch(url, { signal });
|
|
266
|
+
if (!resp.ok) {
|
|
267
|
+
const body = await resp.text();
|
|
268
|
+
throw new HttpError("GET", url, resp.status, body);
|
|
269
|
+
}
|
|
270
|
+
return resp.json();
|
|
271
|
+
}
|
|
272
|
+
/** Liveness probe (GET /livez). */
|
|
273
|
+
async liveness(signal) {
|
|
274
|
+
const url = `${this.#baseURL}/livez`;
|
|
275
|
+
const resp = await this.#fetch(url, { signal });
|
|
276
|
+
if (!resp.ok) {
|
|
277
|
+
const body = await resp.text();
|
|
278
|
+
throw new HttpError("GET", url, resp.status, body);
|
|
279
|
+
}
|
|
280
|
+
return resp.json();
|
|
281
|
+
}
|
|
282
|
+
/** Readiness probe (GET /readyz). */
|
|
283
|
+
async readiness(signal) {
|
|
284
|
+
const url = `${this.#baseURL}/readyz`;
|
|
285
|
+
const resp = await this.#fetch(url, { signal });
|
|
286
|
+
if (!resp.ok) {
|
|
287
|
+
const body = await resp.text();
|
|
288
|
+
throw new HttpError("GET", url, resp.status, body);
|
|
289
|
+
}
|
|
290
|
+
return resp.json();
|
|
291
|
+
}
|
|
292
|
+
/** Fetch boat-side replication status (only available when replication is configured). */
|
|
293
|
+
async replicationStatus(signal) {
|
|
294
|
+
const url = `${this.#baseURL}/replication/status`;
|
|
295
|
+
const resp = await this.#fetch(url, { signal });
|
|
296
|
+
if (!resp.ok) {
|
|
297
|
+
const body = await resp.text();
|
|
298
|
+
throw new HttpError("GET", url, resp.status, body);
|
|
299
|
+
}
|
|
300
|
+
return resp.json();
|
|
301
|
+
}
|
|
190
302
|
/** Create or reconnect a buffered session on the server. */
|
|
191
303
|
async createSession(config, signal) {
|
|
192
304
|
const url = `${this.#baseURL}/clients/${config.clientId}`;
|
|
@@ -210,24 +322,37 @@ var Client = class {
|
|
|
210
322
|
return new Session(this.#baseURL, this.#fetch, info);
|
|
211
323
|
}
|
|
212
324
|
};
|
|
325
|
+
function isSubscribeOptions(obj) {
|
|
326
|
+
return "decode" in obj || "signal" in obj || "filter" in obj;
|
|
327
|
+
}
|
|
213
328
|
function filterIsEmpty(f) {
|
|
214
|
-
return !f.pgn?.length && !f.manufacturer?.length && !f.instance?.length && !f.name?.length;
|
|
329
|
+
return !f.pgn?.length && !f.exclude_pgn?.length && !f.manufacturer?.length && !f.instance?.length && !f.name?.length && !f.exclude_name?.length && !f.bus?.length;
|
|
215
330
|
}
|
|
216
|
-
function filterToQueryString(f) {
|
|
217
|
-
if (!f || filterIsEmpty(f)) return "";
|
|
331
|
+
function filterToQueryString(f, decode) {
|
|
332
|
+
if ((!f || filterIsEmpty(f)) && !decode) return "";
|
|
218
333
|
const params = new URLSearchParams();
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
334
|
+
if (f) {
|
|
335
|
+
for (const p of f.pgn ?? []) params.append("pgn", p.toString());
|
|
336
|
+
for (const p of f.exclude_pgn ?? [])
|
|
337
|
+
params.append("exclude_pgn", p.toString());
|
|
338
|
+
for (const m of f.manufacturer ?? []) params.append("manufacturer", m);
|
|
339
|
+
for (const i of f.instance ?? []) params.append("instance", i.toString());
|
|
340
|
+
for (const n of f.name ?? []) params.append("name", n);
|
|
341
|
+
for (const n of f.exclude_name ?? []) params.append("exclude_name", n);
|
|
342
|
+
for (const b of f.bus ?? []) params.append("bus", b);
|
|
343
|
+
}
|
|
344
|
+
if (decode) params.set("decode", "true");
|
|
223
345
|
return params.toString();
|
|
224
346
|
}
|
|
225
347
|
function filterToJSON(f) {
|
|
226
348
|
const m = {};
|
|
227
349
|
if (f.pgn?.length) m.pgn = f.pgn;
|
|
350
|
+
if (f.exclude_pgn?.length) m.exclude_pgn = f.exclude_pgn;
|
|
228
351
|
if (f.manufacturer?.length) m.manufacturer = f.manufacturer;
|
|
229
352
|
if (f.instance?.length) m.instance = f.instance;
|
|
230
353
|
if (f.name?.length) m.name = f.name;
|
|
354
|
+
if (f.exclude_name?.length) m.exclude_name = f.exclude_name;
|
|
355
|
+
if (f.bus?.length) m.bus = f.bus;
|
|
231
356
|
return m;
|
|
232
357
|
}
|
|
233
358
|
|
|
@@ -283,6 +408,36 @@ var CloudClient = class {
|
|
|
283
408
|
}
|
|
284
409
|
return resp.json();
|
|
285
410
|
}
|
|
411
|
+
/** Check cloud server health (GET /healthz). */
|
|
412
|
+
async health(signal) {
|
|
413
|
+
const url = `${this.#baseURL}/healthz`;
|
|
414
|
+
const resp = await this.#fetch(url, { signal });
|
|
415
|
+
if (!resp.ok) {
|
|
416
|
+
const body = await resp.text();
|
|
417
|
+
throw new HttpError("GET", url, resp.status, body);
|
|
418
|
+
}
|
|
419
|
+
return resp.json();
|
|
420
|
+
}
|
|
421
|
+
/** Liveness probe (GET /livez). */
|
|
422
|
+
async liveness(signal) {
|
|
423
|
+
const url = `${this.#baseURL}/livez`;
|
|
424
|
+
const resp = await this.#fetch(url, { signal });
|
|
425
|
+
if (!resp.ok) {
|
|
426
|
+
const body = await resp.text();
|
|
427
|
+
throw new HttpError("GET", url, resp.status, body);
|
|
428
|
+
}
|
|
429
|
+
return resp.json();
|
|
430
|
+
}
|
|
431
|
+
/** Readiness probe (GET /readyz). */
|
|
432
|
+
async readiness(signal) {
|
|
433
|
+
const url = `${this.#baseURL}/readyz`;
|
|
434
|
+
const resp = await this.#fetch(url, { signal });
|
|
435
|
+
if (!resp.ok) {
|
|
436
|
+
const body = await resp.text();
|
|
437
|
+
throw new HttpError("GET", url, resp.status, body);
|
|
438
|
+
}
|
|
439
|
+
return resp.json();
|
|
440
|
+
}
|
|
286
441
|
};
|
|
287
442
|
export {
|
|
288
443
|
Client,
|