tinytsdk 0.2.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 +130 -0
- package/dist/client.d.ts +58 -0
- package/dist/index.cjs.js +382 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.esm.js +355 -0
- package/dist/proto.d.ts +111 -0
- package/dist/react/MetricsBar.d.ts +13 -0
- package/dist/react/MetricsPanel.d.ts +8 -0
- package/dist/react/TinyTrackProvider.d.ts +30 -0
- package/dist/react/dashboard/Dashboard.d.ts +11 -0
- package/dist/react/dashboard/Sparkline.d.ts +10 -0
- package/dist/react/dashboard/TimeSeriesChart.d.ts +12 -0
- package/dist/react/dashboard/Timeline.d.ts +13 -0
- package/dist/react/dashboard/utils.d.ts +16 -0
- package/dist/react/index.d.ts +13 -0
- package/dist/react/theme.d.ts +111 -0
- package/dist/react.cjs.js +1217 -0
- package/dist/react.esm.js +1203 -0
- package/dist/stories/Dashboard.stories.d.ts +9 -0
- package/dist/stories/MetricsBar.stories.d.ts +12 -0
- package/dist/stories/MetricsPanel.stories.d.ts +8 -0
- package/dist/stories/MockProvider.d.ts +20 -0
- package/dist/stories/TimeSeriesChart.stories.d.ts +12 -0
- package/dist/stories/Timeline.stories.d.ts +11 -0
- package/package.json +76 -0
package/README.md
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# tinytsdk
|
|
2
|
+
|
|
3
|
+
JavaScript/TypeScript SDK for the [TinyTrack](https://github.com/jetallavache/tiny-track) monitoring system.
|
|
4
|
+
|
|
5
|
+
## Packages
|
|
6
|
+
|
|
7
|
+
| Entry point | Contents |
|
|
8
|
+
|-------------|----------|
|
|
9
|
+
| `tinytsdk` | Core WebSocket client + protocol parser (no framework deps) |
|
|
10
|
+
| `tinytsdk/react` | React components + `TinyTrackProvider` context |
|
|
11
|
+
|
|
12
|
+
## Installation
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm install tinytsdk
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Vanilla JS / TypeScript
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
import { TinyTrackClient, RING_L1 } from 'tinytsdk';
|
|
22
|
+
|
|
23
|
+
const client = new TinyTrackClient('ws://localhost:4026');
|
|
24
|
+
|
|
25
|
+
client.on('metrics', m => {
|
|
26
|
+
console.log(`CPU: ${m.cpu / 100}% MEM: ${m.mem / 100}%`);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
client.on('open', () => {
|
|
30
|
+
// request history from L1 ring (last 60 samples)
|
|
31
|
+
client.getHistory(RING_L1, 60);
|
|
32
|
+
// set push interval to 5 seconds
|
|
33
|
+
client.setInterval(5000);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
client.connect();
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### CDN (browser)
|
|
40
|
+
|
|
41
|
+
```html
|
|
42
|
+
<script type="module">
|
|
43
|
+
import { TinyTrackClient } from 'https://cdn.jsdelivr.net/npm/tinytsdk/dist/index.esm.js';
|
|
44
|
+
const client = new TinyTrackClient('ws://your-server:4026');
|
|
45
|
+
client.on('metrics', m => console.log(m));
|
|
46
|
+
client.connect();
|
|
47
|
+
</script>
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## React
|
|
51
|
+
|
|
52
|
+
```tsx
|
|
53
|
+
import { TinyTrackProvider, useTinyTrack, MetricsPanel } from 'tinytsdk/react';
|
|
54
|
+
|
|
55
|
+
function App() {
|
|
56
|
+
return (
|
|
57
|
+
<TinyTrackProvider url="ws://localhost:4026">
|
|
58
|
+
<Dashboard />
|
|
59
|
+
</TinyTrackProvider>
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function Dashboard() {
|
|
64
|
+
const { metrics, connected } = useTinyTrack();
|
|
65
|
+
return (
|
|
66
|
+
<>
|
|
67
|
+
<p>Status: {connected ? 'connected' : 'disconnected'}</p>
|
|
68
|
+
<MetricsPanel />
|
|
69
|
+
</>
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## API
|
|
75
|
+
|
|
76
|
+
### `TinyTrackClient`
|
|
77
|
+
|
|
78
|
+
```ts
|
|
79
|
+
const client = new TinyTrackClient(url, options?);
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
| Option | Type | Default | Description |
|
|
83
|
+
|--------|------|---------|-------------|
|
|
84
|
+
| `reconnect` | boolean | `true` | Auto-reconnect on close |
|
|
85
|
+
| `reconnectDelay` | number | `2000` | Delay between reconnects (ms) |
|
|
86
|
+
| `maxRetries` | number | `0` | Max retries (0 = unlimited) |
|
|
87
|
+
| `path` | string | `'/websocket'` | WebSocket path |
|
|
88
|
+
|
|
89
|
+
**Methods:**
|
|
90
|
+
|
|
91
|
+
| Method | Description |
|
|
92
|
+
|--------|-------------|
|
|
93
|
+
| `connect()` | Open WebSocket connection |
|
|
94
|
+
| `disconnect()` | Close connection |
|
|
95
|
+
| `getSnapshot()` | Request current metrics snapshot |
|
|
96
|
+
| `getStats()` | Request ring buffer statistics |
|
|
97
|
+
| `setInterval(ms)` | Change push interval |
|
|
98
|
+
| `getHistory(level, maxCount?, fromTs?, toTs?)` | Request historical data |
|
|
99
|
+
| `subscribe(level, intervalMs?)` | Subscribe to a ring level |
|
|
100
|
+
| `on(event, fn)` / `off(event, fn)` | Add/remove event listener |
|
|
101
|
+
|
|
102
|
+
**Events:** `open`, `close`, `error`, `metrics`, `config`, `ack`, `stats`, `history`
|
|
103
|
+
|
|
104
|
+
### Metrics fields (`TtMetrics`)
|
|
105
|
+
|
|
106
|
+
| Field | Type | Description |
|
|
107
|
+
|-------|------|-------------|
|
|
108
|
+
| `timestamp` | number | ms since epoch |
|
|
109
|
+
| `cpu` | number | CPU usage × 100 (e.g. 5000 = 50%) |
|
|
110
|
+
| `mem` | number | Memory usage × 100 |
|
|
111
|
+
| `netRx` / `netTx` | number | Network bytes/sec |
|
|
112
|
+
| `load1` / `load5` / `load15` | number | Load average × 100 |
|
|
113
|
+
| `nrRunning` / `nrTotal` | number | Process counts |
|
|
114
|
+
| `duUsage` | number | Disk usage × 100 |
|
|
115
|
+
| `duTotal` / `duFree` | number | Disk bytes |
|
|
116
|
+
|
|
117
|
+
### Ring levels
|
|
118
|
+
|
|
119
|
+
| Constant | Value | Resolution | Retention |
|
|
120
|
+
|----------|-------|------------|-----------|
|
|
121
|
+
| `RING_L1` | 1 | 1 second | 1 hour |
|
|
122
|
+
| `RING_L2` | 2 | 1 minute | 24 hours |
|
|
123
|
+
| `RING_L3` | 3 | 1 hour | 7 days |
|
|
124
|
+
|
|
125
|
+
## Development
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
npm test # run tests
|
|
129
|
+
npm run build # build dist/
|
|
130
|
+
```
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TinyTrackClient — WebSocket client for the tinytrack gateway.
|
|
3
|
+
*
|
|
4
|
+
* Usage:
|
|
5
|
+
* const client = new TinyTrackClient('ws://localhost:4026');
|
|
6
|
+
* client.on('metrics', m => console.log(m.cpu / 100, '%'));
|
|
7
|
+
* client.connect();
|
|
8
|
+
*/
|
|
9
|
+
import { RING_L1, RING_L2, RING_L3, TtMetrics, TtConfig, TtAck, TtStats, TtHistoryResp, TtSysInfo } from './proto.js';
|
|
10
|
+
export type { TtMetrics, TtConfig, TtAck, TtStats, TtHistoryResp, TtSysInfo };
|
|
11
|
+
export { RING_L1, RING_L2, RING_L3 };
|
|
12
|
+
export type ClientEventMap = {
|
|
13
|
+
open: [];
|
|
14
|
+
close: [code: number, reason: string];
|
|
15
|
+
error: [err: Event];
|
|
16
|
+
metrics: [m: TtMetrics];
|
|
17
|
+
config: [c: TtConfig];
|
|
18
|
+
ack: [a: TtAck];
|
|
19
|
+
stats: [s: TtStats];
|
|
20
|
+
history: [r: TtHistoryResp];
|
|
21
|
+
sysinfo: [s: TtSysInfo];
|
|
22
|
+
};
|
|
23
|
+
type Listener<K extends keyof ClientEventMap> = (...args: ClientEventMap[K]) => void;
|
|
24
|
+
export interface TinyTrackClientOptions {
|
|
25
|
+
/** Auto-reconnect on close. Default: true */
|
|
26
|
+
reconnect?: boolean;
|
|
27
|
+
/** Reconnect delay ms. Default: 2000 */
|
|
28
|
+
reconnectDelay?: number;
|
|
29
|
+
/** Max reconnect attempts (0 = unlimited). Default: 0 */
|
|
30
|
+
maxRetries?: number;
|
|
31
|
+
/** WebSocket path. Default: '/websocket' */
|
|
32
|
+
path?: string;
|
|
33
|
+
}
|
|
34
|
+
export declare class TinyTrackClient {
|
|
35
|
+
private url;
|
|
36
|
+
private opts;
|
|
37
|
+
private ws;
|
|
38
|
+
private listeners;
|
|
39
|
+
private retries;
|
|
40
|
+
private _closed;
|
|
41
|
+
constructor(url: string, opts?: TinyTrackClientOptions);
|
|
42
|
+
connect(): this;
|
|
43
|
+
disconnect(): void;
|
|
44
|
+
get connected(): boolean;
|
|
45
|
+
getSnapshot(): void;
|
|
46
|
+
getStats(): void;
|
|
47
|
+
getSysInfo(): void;
|
|
48
|
+
setInterval(ms: number): void;
|
|
49
|
+
start(): void;
|
|
50
|
+
stop(): void;
|
|
51
|
+
getHistory(level: number, maxCount?: number, fromTs?: number, toTs?: number): void;
|
|
52
|
+
subscribe(level: number, intervalMs?: number): void;
|
|
53
|
+
on<K extends keyof ClientEventMap>(event: K, fn: Listener<K>): this;
|
|
54
|
+
off<K extends keyof ClientEventMap>(event: K, fn: Listener<K>): this;
|
|
55
|
+
private _open;
|
|
56
|
+
private _send;
|
|
57
|
+
private _emit;
|
|
58
|
+
}
|
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* TinyTrack binary protocol parser (v1 + v2).
|
|
5
|
+
*
|
|
6
|
+
* Wire format (big-endian):
|
|
7
|
+
* [magic:1][version:1][type:1][length:2][timestamp:4][checksum:1] + payload
|
|
8
|
+
*
|
|
9
|
+
* tt_metrics payload (52 bytes, little-endian packed struct):
|
|
10
|
+
* timestamp:8 cpu:2 mem:2 net_rx:4 net_tx:4
|
|
11
|
+
* load1:2 load5:2 load15:2 nr_running:4 nr_total:4
|
|
12
|
+
* du_usage:2 du_total:8 du_free:8
|
|
13
|
+
*/
|
|
14
|
+
const PROTO_MAGIC = 0xaa;
|
|
15
|
+
const HEADER_SIZE = 10;
|
|
16
|
+
// Packet types
|
|
17
|
+
const PKT_METRICS = 0x01;
|
|
18
|
+
const PKT_CONFIG = 0x02;
|
|
19
|
+
const PKT_CMD = 0x04;
|
|
20
|
+
const PKT_ACK = 0x05;
|
|
21
|
+
const PKT_HISTORY_REQ = 0x10;
|
|
22
|
+
const PKT_HISTORY_RESP = 0x11;
|
|
23
|
+
const PKT_SUBSCRIBE = 0x12;
|
|
24
|
+
const PKT_RING_STATS = 0x13;
|
|
25
|
+
const PKT_SYS_INFO = 0x14;
|
|
26
|
+
// Ring levels
|
|
27
|
+
const RING_L1 = 0x01;
|
|
28
|
+
const RING_L2 = 0x02;
|
|
29
|
+
const RING_L3 = 0x03;
|
|
30
|
+
// Commands
|
|
31
|
+
const CMD_SET_INTERVAL = 0x01;
|
|
32
|
+
const CMD_GET_SNAPSHOT = 0x03;
|
|
33
|
+
const CMD_GET_RING_STATS = 0x10;
|
|
34
|
+
const CMD_GET_SYS_INFO = 0x11;
|
|
35
|
+
const CMD_START = 0x12;
|
|
36
|
+
const CMD_STOP = 0x13;
|
|
37
|
+
/** Parse the 10-byte header. Returns null if buffer is too short or magic is wrong. */
|
|
38
|
+
function parseHeader(buf, offset = 0) {
|
|
39
|
+
if (buf.byteLength - offset < HEADER_SIZE)
|
|
40
|
+
return null;
|
|
41
|
+
const v = new DataView(buf, offset);
|
|
42
|
+
if (v.getUint8(0) !== PROTO_MAGIC)
|
|
43
|
+
return null;
|
|
44
|
+
const version = v.getUint8(1);
|
|
45
|
+
const type = v.getUint8(2);
|
|
46
|
+
const length = v.getUint16(3, false); // big-endian
|
|
47
|
+
const timestamp = v.getUint32(5, false);
|
|
48
|
+
if (buf.byteLength - offset < HEADER_SIZE + length)
|
|
49
|
+
return null;
|
|
50
|
+
return {
|
|
51
|
+
type,
|
|
52
|
+
version,
|
|
53
|
+
timestamp,
|
|
54
|
+
payload: new DataView(buf, offset + HEADER_SIZE, length),
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
/** Parse PKT_METRICS payload (52 bytes, little-endian). */
|
|
58
|
+
function parseMetrics(p) {
|
|
59
|
+
// uint64 LE: lo word at offset 0, hi word at offset 4
|
|
60
|
+
const tsLo = p.getUint32(0, true);
|
|
61
|
+
const tsHi = p.getUint32(4, true);
|
|
62
|
+
const timestamp = tsHi * 0x100000000 + tsLo;
|
|
63
|
+
return {
|
|
64
|
+
timestamp,
|
|
65
|
+
cpu: p.getUint16(8, true),
|
|
66
|
+
mem: p.getUint16(10, true),
|
|
67
|
+
netRx: p.getUint32(12, true),
|
|
68
|
+
netTx: p.getUint32(16, true),
|
|
69
|
+
load1: p.getUint16(20, true),
|
|
70
|
+
load5: p.getUint16(22, true),
|
|
71
|
+
load15: p.getUint16(24, true),
|
|
72
|
+
nrRunning: p.getUint32(26, true),
|
|
73
|
+
nrTotal: p.getUint32(30, true),
|
|
74
|
+
duUsage: p.getUint16(34, true),
|
|
75
|
+
duTotal: readUint64LE(p, 36),
|
|
76
|
+
duFree: readUint64LE(p, 44),
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
/** Parse PKT_CONFIG payload (5 bytes). */
|
|
80
|
+
function parseConfig(p) {
|
|
81
|
+
return {
|
|
82
|
+
intervalMs: p.getUint32(0, false),
|
|
83
|
+
alertsEnabled: p.getUint8(4) !== 0,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
/** Parse PKT_ACK payload (2 bytes). */
|
|
87
|
+
function parseAck(p) {
|
|
88
|
+
return { cmdType: p.getUint8(0), status: p.getUint8(1) };
|
|
89
|
+
}
|
|
90
|
+
/** Parse PKT_STATS payload (75 bytes = 3 × 25). */
|
|
91
|
+
function parseStats(p) {
|
|
92
|
+
return {
|
|
93
|
+
l1: parseRingStat(p, 0),
|
|
94
|
+
l2: parseRingStat(p, 29),
|
|
95
|
+
l3: parseRingStat(p, 58),
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
/** Parse PKT_HISTORY_RESP payload. */
|
|
99
|
+
function parseHistoryResp(p) {
|
|
100
|
+
const level = p.getUint8(0);
|
|
101
|
+
const count = p.getUint16(1, false);
|
|
102
|
+
const flags = p.getUint8(3);
|
|
103
|
+
const samples = [];
|
|
104
|
+
for (let i = 0; i < count; i++) {
|
|
105
|
+
const off = 4 + i * 52;
|
|
106
|
+
samples.push(parseMetrics(new DataView(p.buffer, p.byteOffset + off, 52)));
|
|
107
|
+
}
|
|
108
|
+
return { level, count, last: (flags & 0x01) !== 0, samples };
|
|
109
|
+
}
|
|
110
|
+
/** Parse PKT_SYS_INFO payload (168 bytes). */
|
|
111
|
+
function parseSysInfo(p) {
|
|
112
|
+
const dec = new TextDecoder();
|
|
113
|
+
const hostname = dec.decode(new Uint8Array(p.buffer, p.byteOffset, 64)).replace(/\0.*/, '');
|
|
114
|
+
const osType = dec.decode(new Uint8Array(p.buffer, p.byteOffset + 64, 64)).replace(/\0.*/, '');
|
|
115
|
+
return {
|
|
116
|
+
hostname,
|
|
117
|
+
osType,
|
|
118
|
+
uptimeSec: readUint64BE(p, 128), // server: htobe64
|
|
119
|
+
slotsL1: p.getUint32(136, false), // server: htonl (BE)
|
|
120
|
+
slotsL2: p.getUint32(140, false),
|
|
121
|
+
slotsL3: p.getUint32(144, false),
|
|
122
|
+
intervalMs: p.getUint32(148, false),
|
|
123
|
+
aggL2Ms: p.getUint32(152, false),
|
|
124
|
+
aggL3Ms: p.getUint32(156, false),
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
// ---------------------------------------------------------------------------
|
|
128
|
+
// Command builders
|
|
129
|
+
// ---------------------------------------------------------------------------
|
|
130
|
+
function buildCmd(cmdType, arg = 0) {
|
|
131
|
+
const buf = new ArrayBuffer(HEADER_SIZE + 9);
|
|
132
|
+
const h = new DataView(buf);
|
|
133
|
+
const ts = Math.floor(Date.now() / 1000);
|
|
134
|
+
h.setUint8(0, PROTO_MAGIC);
|
|
135
|
+
h.setUint8(1, 0x01); // v1
|
|
136
|
+
h.setUint8(2, PKT_CMD);
|
|
137
|
+
h.setUint16(3, 9, false);
|
|
138
|
+
h.setUint32(5, ts, false);
|
|
139
|
+
h.setUint8(9, calcChecksum(h));
|
|
140
|
+
// payload
|
|
141
|
+
h.setUint8(10, cmdType);
|
|
142
|
+
h.setUint32(11, arg, false);
|
|
143
|
+
return buf;
|
|
144
|
+
}
|
|
145
|
+
function buildHistoryReq(level, maxCount = 0, fromTs = 0, toTs = 0) {
|
|
146
|
+
const buf = new ArrayBuffer(HEADER_SIZE + 19);
|
|
147
|
+
const h = new DataView(buf);
|
|
148
|
+
const ts = Math.floor(Date.now() / 1000);
|
|
149
|
+
h.setUint8(0, PROTO_MAGIC);
|
|
150
|
+
h.setUint8(1, 0x02); // v2
|
|
151
|
+
h.setUint8(2, PKT_HISTORY_REQ);
|
|
152
|
+
h.setUint16(3, 19, false);
|
|
153
|
+
h.setUint32(5, ts, false);
|
|
154
|
+
h.setUint8(9, calcChecksum(h));
|
|
155
|
+
// payload: level(1) from_ts(8) to_ts(8) max_count(2)
|
|
156
|
+
h.setUint8(10, level);
|
|
157
|
+
writeUint64BE(h, 11, fromTs);
|
|
158
|
+
writeUint64BE(h, 19, toTs);
|
|
159
|
+
h.setUint16(27, maxCount, false);
|
|
160
|
+
return buf;
|
|
161
|
+
}
|
|
162
|
+
function buildSubscribe(level, intervalMs = 0) {
|
|
163
|
+
const buf = new ArrayBuffer(HEADER_SIZE + 6);
|
|
164
|
+
const h = new DataView(buf);
|
|
165
|
+
const ts = Math.floor(Date.now() / 1000);
|
|
166
|
+
h.setUint8(0, PROTO_MAGIC);
|
|
167
|
+
h.setUint8(1, 0x02);
|
|
168
|
+
h.setUint8(2, PKT_SUBSCRIBE);
|
|
169
|
+
h.setUint16(3, 6, false);
|
|
170
|
+
h.setUint32(5, ts, false);
|
|
171
|
+
h.setUint8(9, calcChecksum(h));
|
|
172
|
+
h.setUint8(10, level);
|
|
173
|
+
h.setUint32(11, intervalMs, false);
|
|
174
|
+
h.setUint8(15, 0);
|
|
175
|
+
return buf;
|
|
176
|
+
}
|
|
177
|
+
// ---------------------------------------------------------------------------
|
|
178
|
+
// Helpers
|
|
179
|
+
// ---------------------------------------------------------------------------
|
|
180
|
+
function calcChecksum(h) {
|
|
181
|
+
let cs = 0;
|
|
182
|
+
for (let i = 0; i < 9; i++)
|
|
183
|
+
cs ^= h.getUint8(i);
|
|
184
|
+
return cs;
|
|
185
|
+
}
|
|
186
|
+
function parseRingStat(p, off) {
|
|
187
|
+
return {
|
|
188
|
+
level: p.getUint8(off),
|
|
189
|
+
capacity: p.getUint32(off + 1, false), // htonl (BE)
|
|
190
|
+
head: p.getUint32(off + 5, false), // htonl (BE)
|
|
191
|
+
filled: p.getUint32(off + 9, false), // htonl (BE)
|
|
192
|
+
firstTs: readUint64LE(p, off + 13), // no hton, native LE
|
|
193
|
+
lastTs: readUint64LE(p, off + 21), // no hton, native LE
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
function readUint64LE(v, off) {
|
|
197
|
+
const lo = v.getUint32(off, true);
|
|
198
|
+
const hi = v.getUint32(off + 4, true);
|
|
199
|
+
return hi * 0x100000000 + lo;
|
|
200
|
+
}
|
|
201
|
+
function readUint64BE(v, off) {
|
|
202
|
+
const hi = v.getUint32(off, false);
|
|
203
|
+
const lo = v.getUint32(off + 4, false);
|
|
204
|
+
return hi * 0x100000000 + lo;
|
|
205
|
+
}
|
|
206
|
+
function writeUint64BE(v, off, val) {
|
|
207
|
+
const hi = Math.floor(val / 0x100000000);
|
|
208
|
+
const lo = val >>> 0;
|
|
209
|
+
v.setUint32(off, hi, false);
|
|
210
|
+
v.setUint32(off + 4, lo, false);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* TinyTrackClient — WebSocket client for the tinytrack gateway.
|
|
215
|
+
*
|
|
216
|
+
* Usage:
|
|
217
|
+
* const client = new TinyTrackClient('ws://localhost:4026');
|
|
218
|
+
* client.on('metrics', m => console.log(m.cpu / 100, '%'));
|
|
219
|
+
* client.connect();
|
|
220
|
+
*/
|
|
221
|
+
class TinyTrackClient {
|
|
222
|
+
constructor(url, opts = {}) {
|
|
223
|
+
var _a, _b, _c, _d;
|
|
224
|
+
this.ws = null;
|
|
225
|
+
this.listeners = new Map();
|
|
226
|
+
this.retries = 0;
|
|
227
|
+
this._closed = false;
|
|
228
|
+
// Normalise: strip trailing path, we'll append opts.path
|
|
229
|
+
this.url = url.replace(/\/websocket\/?$/, '');
|
|
230
|
+
this.opts = {
|
|
231
|
+
reconnect: (_a = opts.reconnect) !== null && _a !== void 0 ? _a : true,
|
|
232
|
+
reconnectDelay: (_b = opts.reconnectDelay) !== null && _b !== void 0 ? _b : 2000,
|
|
233
|
+
maxRetries: (_c = opts.maxRetries) !== null && _c !== void 0 ? _c : 0,
|
|
234
|
+
path: (_d = opts.path) !== null && _d !== void 0 ? _d : '/websocket',
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
// ---------------------------------------------------------------------------
|
|
238
|
+
// Lifecycle
|
|
239
|
+
// ---------------------------------------------------------------------------
|
|
240
|
+
connect() {
|
|
241
|
+
this._closed = false;
|
|
242
|
+
this._open();
|
|
243
|
+
return this;
|
|
244
|
+
}
|
|
245
|
+
disconnect() {
|
|
246
|
+
var _a;
|
|
247
|
+
this._closed = true;
|
|
248
|
+
(_a = this.ws) === null || _a === void 0 ? void 0 : _a.close();
|
|
249
|
+
this.ws = null;
|
|
250
|
+
}
|
|
251
|
+
get connected() {
|
|
252
|
+
var _a;
|
|
253
|
+
return ((_a = this.ws) === null || _a === void 0 ? void 0 : _a.readyState) === WebSocket.OPEN;
|
|
254
|
+
}
|
|
255
|
+
// ---------------------------------------------------------------------------
|
|
256
|
+
// Commands
|
|
257
|
+
// ---------------------------------------------------------------------------
|
|
258
|
+
getSnapshot() {
|
|
259
|
+
this._send(buildCmd(CMD_GET_SNAPSHOT));
|
|
260
|
+
}
|
|
261
|
+
getStats() {
|
|
262
|
+
this._send(buildCmd(CMD_GET_RING_STATS));
|
|
263
|
+
}
|
|
264
|
+
getSysInfo() {
|
|
265
|
+
this._send(buildCmd(CMD_GET_SYS_INFO));
|
|
266
|
+
}
|
|
267
|
+
setInterval(ms) {
|
|
268
|
+
this._send(buildCmd(CMD_SET_INTERVAL, ms));
|
|
269
|
+
}
|
|
270
|
+
start() {
|
|
271
|
+
this._send(buildCmd(CMD_START));
|
|
272
|
+
}
|
|
273
|
+
stop() {
|
|
274
|
+
this._send(buildCmd(CMD_STOP));
|
|
275
|
+
}
|
|
276
|
+
getHistory(level, maxCount = 60, fromTs = 0, toTs = 0) {
|
|
277
|
+
this._send(buildHistoryReq(level, maxCount, fromTs, toTs));
|
|
278
|
+
}
|
|
279
|
+
subscribe(level, intervalMs = 0) {
|
|
280
|
+
this._send(buildSubscribe(level, intervalMs));
|
|
281
|
+
}
|
|
282
|
+
// ---------------------------------------------------------------------------
|
|
283
|
+
// Events
|
|
284
|
+
// ---------------------------------------------------------------------------
|
|
285
|
+
on(event, fn) {
|
|
286
|
+
if (!this.listeners.has(event))
|
|
287
|
+
this.listeners.set(event, new Set());
|
|
288
|
+
this.listeners.get(event).add(fn);
|
|
289
|
+
return this;
|
|
290
|
+
}
|
|
291
|
+
off(event, fn) {
|
|
292
|
+
var _a;
|
|
293
|
+
(_a = this.listeners.get(event)) === null || _a === void 0 ? void 0 : _a.delete(fn);
|
|
294
|
+
return this;
|
|
295
|
+
}
|
|
296
|
+
// ---------------------------------------------------------------------------
|
|
297
|
+
// Private
|
|
298
|
+
// ---------------------------------------------------------------------------
|
|
299
|
+
_open() {
|
|
300
|
+
const wsUrl = this.url + this.opts.path;
|
|
301
|
+
const ws = new WebSocket(wsUrl);
|
|
302
|
+
ws.binaryType = 'arraybuffer';
|
|
303
|
+
this.ws = ws;
|
|
304
|
+
ws.onopen = () => {
|
|
305
|
+
this.retries = 0;
|
|
306
|
+
this._emit('open');
|
|
307
|
+
};
|
|
308
|
+
ws.onclose = (e) => {
|
|
309
|
+
this._emit('close', e.code, e.reason);
|
|
310
|
+
if (!this._closed && this.opts.reconnect) {
|
|
311
|
+
const max = this.opts.maxRetries;
|
|
312
|
+
if (max === 0 || this.retries < max) {
|
|
313
|
+
this.retries++;
|
|
314
|
+
setTimeout(() => this._open(), this.opts.reconnectDelay);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
};
|
|
318
|
+
ws.onerror = (e) => this._emit('error', e);
|
|
319
|
+
ws.onmessage = (e) => {
|
|
320
|
+
const frame = parseHeader(e.data);
|
|
321
|
+
if (!frame)
|
|
322
|
+
return;
|
|
323
|
+
switch (frame.type) {
|
|
324
|
+
case PKT_METRICS:
|
|
325
|
+
this._emit('metrics', parseMetrics(frame.payload));
|
|
326
|
+
break;
|
|
327
|
+
case PKT_CONFIG:
|
|
328
|
+
this._emit('config', parseConfig(frame.payload));
|
|
329
|
+
break;
|
|
330
|
+
case PKT_ACK:
|
|
331
|
+
this._emit('ack', parseAck(frame.payload));
|
|
332
|
+
break;
|
|
333
|
+
case PKT_RING_STATS:
|
|
334
|
+
this._emit('stats', parseStats(frame.payload));
|
|
335
|
+
break;
|
|
336
|
+
case PKT_HISTORY_RESP:
|
|
337
|
+
this._emit('history', parseHistoryResp(frame.payload));
|
|
338
|
+
break;
|
|
339
|
+
case PKT_SYS_INFO:
|
|
340
|
+
this._emit('sysinfo', parseSysInfo(frame.payload));
|
|
341
|
+
break;
|
|
342
|
+
}
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
_send(buf) {
|
|
346
|
+
var _a;
|
|
347
|
+
if (((_a = this.ws) === null || _a === void 0 ? void 0 : _a.readyState) === WebSocket.OPEN) {
|
|
348
|
+
this.ws.send(buf);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
_emit(event, ...args) {
|
|
352
|
+
var _a;
|
|
353
|
+
(_a = this.listeners.get(event)) === null || _a === void 0 ? void 0 : _a.forEach((fn) => fn(...args));
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
exports.CMD_GET_RING_STATS = CMD_GET_RING_STATS;
|
|
358
|
+
exports.CMD_GET_SNAPSHOT = CMD_GET_SNAPSHOT;
|
|
359
|
+
exports.CMD_GET_SYS_INFO = CMD_GET_SYS_INFO;
|
|
360
|
+
exports.CMD_SET_INTERVAL = CMD_SET_INTERVAL;
|
|
361
|
+
exports.CMD_START = CMD_START;
|
|
362
|
+
exports.CMD_STOP = CMD_STOP;
|
|
363
|
+
exports.PKT_ACK = PKT_ACK;
|
|
364
|
+
exports.PKT_CONFIG = PKT_CONFIG;
|
|
365
|
+
exports.PKT_HISTORY_RESP = PKT_HISTORY_RESP;
|
|
366
|
+
exports.PKT_METRICS = PKT_METRICS;
|
|
367
|
+
exports.PKT_RING_STATS = PKT_RING_STATS;
|
|
368
|
+
exports.PKT_SYS_INFO = PKT_SYS_INFO;
|
|
369
|
+
exports.RING_L1 = RING_L1;
|
|
370
|
+
exports.RING_L2 = RING_L2;
|
|
371
|
+
exports.RING_L3 = RING_L3;
|
|
372
|
+
exports.TinyTrackClient = TinyTrackClient;
|
|
373
|
+
exports.buildCmd = buildCmd;
|
|
374
|
+
exports.buildHistoryReq = buildHistoryReq;
|
|
375
|
+
exports.buildSubscribe = buildSubscribe;
|
|
376
|
+
exports.parseAck = parseAck;
|
|
377
|
+
exports.parseConfig = parseConfig;
|
|
378
|
+
exports.parseHeader = parseHeader;
|
|
379
|
+
exports.parseHistoryResp = parseHistoryResp;
|
|
380
|
+
exports.parseMetrics = parseMetrics;
|
|
381
|
+
exports.parseStats = parseStats;
|
|
382
|
+
exports.parseSysInfo = parseSysInfo;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { TinyTrackClient } from './client.js';
|
|
2
|
+
export type { TinyTrackClientOptions, ClientEventMap } from './client.js';
|
|
3
|
+
export type { TtMetrics, TtConfig, TtAck, TtStats, TtHistoryResp, TtSysInfo } from './client.js';
|
|
4
|
+
export { RING_L1, RING_L2, RING_L3, PKT_METRICS, PKT_CONFIG, PKT_ACK, PKT_RING_STATS, PKT_HISTORY_RESP, PKT_SYS_INFO, CMD_GET_SNAPSHOT, CMD_GET_RING_STATS, CMD_GET_SYS_INFO, CMD_SET_INTERVAL, CMD_START, CMD_STOP, buildCmd, buildHistoryReq, buildSubscribe, parseHeader, parseMetrics, parseConfig, parseAck, parseStats, parseHistoryResp, parseSysInfo, } from './proto.js';
|