@hotlap.ai/telemetry 1.0.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.
@@ -0,0 +1,135 @@
1
+ # Contributing to @hotlap/telemetry
2
+
3
+ Thank you for your interest in contributing to the HotLap.ai telemetry SDK!
4
+
5
+ ## Getting Started
6
+
7
+ 1. Fork the repository
8
+ 2. Clone your fork:
9
+ ```bash
10
+ git clone https://github.com/YOUR_USERNAME/telemetry.git
11
+ cd telemetry
12
+ ```
13
+ 3. Install dependencies:
14
+ ```bash
15
+ bun install
16
+ ```
17
+ 4. Create a branch for your changes:
18
+ ```bash
19
+ git checkout -b feature/your-feature-name
20
+ ```
21
+
22
+ ## Development
23
+
24
+ ### Prerequisites
25
+
26
+ - [Bun](https://bun.sh) >= 1.0.0
27
+ - Windows (required for live telemetry testing)
28
+ - iRacing (for testing live telemetry features)
29
+
30
+ ### Running the Project
31
+
32
+ ```bash
33
+ # Run the CLI in watch mode
34
+ bun run dev
35
+
36
+ # Run the telemetry server
37
+ bun run server
38
+
39
+ # Run tests
40
+ bun run test
41
+
42
+ # Run benchmarks
43
+ bun run bench
44
+
45
+ # Type check
46
+ bun run typecheck
47
+ ```
48
+
49
+ ### Project Structure
50
+
51
+ ```
52
+ src/
53
+ binary-reader.ts # Low-level binary parsing utilities
54
+ cli.ts # Command-line interface
55
+ constants.ts # iRacing SDK constants and enums
56
+ ibt.ts # IBT file parser
57
+ index.ts # Main exports
58
+ live.ts # Live telemetry via Windows FFI
59
+ telemetry-server.ts # WebSocket server for streaming
60
+ types.ts # TypeScript type definitions
61
+ ```
62
+
63
+ ## Code Style
64
+
65
+ - Use TypeScript for all new code
66
+ - Follow existing code patterns and naming conventions
67
+ - Use meaningful variable and function names
68
+ - Keep functions focused and small
69
+ - Add types for all function parameters and return values
70
+
71
+ ## Commit Messages
72
+
73
+ We use [Conventional Commits](https://www.conventionalcommits.org/):
74
+
75
+ ```
76
+ <type>(<scope>): <description>
77
+
78
+ [optional body]
79
+
80
+ [optional footer]
81
+ ```
82
+
83
+ **Types:**
84
+ - `feat` - New feature
85
+ - `fix` - Bug fix
86
+ - `docs` - Documentation only
87
+ - `style` - Code style (formatting, etc.)
88
+ - `refactor` - Code refactoring
89
+ - `perf` - Performance improvement
90
+ - `test` - Adding/updating tests
91
+ - `chore` - Maintenance tasks
92
+
93
+ **Examples:**
94
+ ```
95
+ feat(ibt): add support for parsing stint data
96
+ fix(live): handle disconnection gracefully
97
+ docs: update API reference for IRSDK class
98
+ perf(binary-reader): optimize variable batch reading
99
+ ```
100
+
101
+ ## Pull Requests
102
+
103
+ 1. Ensure your code follows the existing style
104
+ 2. Update documentation if needed
105
+ 3. Add tests for new functionality
106
+ 4. Make sure all tests pass: `bun run test`
107
+ 5. Make sure types are correct: `bun run typecheck`
108
+ 6. Write a clear PR description explaining your changes
109
+
110
+ ## Reporting Issues
111
+
112
+ When reporting issues, please include:
113
+
114
+ - Bun version (`bun --version`)
115
+ - Operating system and version
116
+ - iRacing version (if applicable)
117
+ - Steps to reproduce the issue
118
+ - Expected vs actual behavior
119
+ - Any error messages or logs
120
+
121
+ ## Feature Requests
122
+
123
+ We welcome feature requests! Please open an issue describing:
124
+
125
+ - The problem you're trying to solve
126
+ - Your proposed solution
127
+ - Any alternatives you've considered
128
+
129
+ ## Questions?
130
+
131
+ Feel free to open an issue for any questions about contributing.
132
+
133
+ ## License
134
+
135
+ By contributing, you agree that your contributions will be licensed under the MIT License.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 HotLap.ai
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,208 @@
1
+ # @hotlap.ai/telemetry
2
+
3
+ High-performance iRacing telemetry SDK for [Bun](https://bun.sh). Parse IBT telemetry files and stream live data from iRacing via Windows shared memory.
4
+
5
+ ## Features
6
+
7
+ - **IBT File Parsing** - Read and analyze iRacing telemetry binary files (.ibt)
8
+ - **Live Telemetry** - Stream real-time data from iRacing via Windows shared memory (FFI)
9
+ - **WebSocket Server** - Built-in telemetry server for real-time data streaming
10
+ - **Full TypeScript Support** - Complete type definitions for all APIs
11
+ - **High Performance** - Optimized binary parsing with minimal allocations
12
+
13
+ ## Requirements
14
+
15
+ - [Bun](https://bun.sh) >= 1.0.0
16
+ - Windows (for live telemetry features)
17
+ - iRacing (for live telemetry)
18
+
19
+ ## Installation
20
+
21
+ ```bash
22
+ bun add @hotlap.ai/telemetry
23
+ ```
24
+
25
+ ## Usage
26
+
27
+ ### Parse IBT Files
28
+
29
+ ```typescript
30
+ import { IBT } from '@hotlap.ai/telemetry';
31
+
32
+ const ibt = new IBT();
33
+ await ibt.open('./telemetry.ibt');
34
+
35
+ // Get session info
36
+ const weekendInfo = ibt.getSessionInfo('WeekendInfo');
37
+ console.log(`Track: ${weekendInfo.TrackDisplayName}`);
38
+
39
+ // Read telemetry data
40
+ const speed = ibt.get('Speed', 0); // Get speed at tick 0
41
+ const allSpeeds = ibt.getAll('Speed'); // Get all speed values
42
+
43
+ // Iterate through records
44
+ for (let i = 0; i < ibt.recordCount; i++) {
45
+ const lap = ibt.get('Lap', i);
46
+ const throttle = ibt.get('Throttle', i);
47
+ const brake = ibt.get('Brake', i);
48
+ }
49
+ ```
50
+
51
+ ### Live Telemetry
52
+
53
+ ```typescript
54
+ import { createIRSDK } from '@hotlap.ai/telemetry';
55
+
56
+ const ir = createIRSDK();
57
+
58
+ // Connect to iRacing
59
+ const connected = await ir.startup();
60
+ if (!connected) {
61
+ console.error('Failed to connect to iRacing');
62
+ process.exit(1);
63
+ }
64
+
65
+ // Read live telemetry
66
+ setInterval(() => {
67
+ if (ir.isConnected) {
68
+ ir.freezeVarBufferLatest();
69
+
70
+ const speed = ir.get('Speed');
71
+ const rpm = ir.get('RPM');
72
+ const gear = ir.get('Gear');
73
+
74
+ console.log(`Speed: ${speed}, RPM: ${rpm}, Gear: ${gear}`);
75
+
76
+ ir.unfreezeVarBufferLatest();
77
+ }
78
+ }, 16); // ~60Hz
79
+
80
+ // Cleanup on exit
81
+ process.on('SIGINT', () => {
82
+ ir.shutdown();
83
+ process.exit(0);
84
+ });
85
+ ```
86
+
87
+ ### WebSocket Telemetry Server
88
+
89
+ ```typescript
90
+ import { spawn } from 'bun';
91
+
92
+ // Start the telemetry server
93
+ const server = spawn(['bun', 'run', 'server'], {
94
+ cwd: './node_modules/@hotlap.ai/telemetry',
95
+ });
96
+
97
+ // Connect from client
98
+ const ws = new WebSocket('ws://127.0.0.1:32100/ws');
99
+
100
+ ws.onmessage = (event) => {
101
+ const msg = JSON.parse(event.data);
102
+
103
+ switch (msg.type) {
104
+ case 'session_info':
105
+ console.log(`Track: ${msg.data.trackDisplayName}`);
106
+ break;
107
+ case 'telemetry':
108
+ console.log(`Speed: ${msg.data.speed}`);
109
+ break;
110
+ case 'lap_change':
111
+ console.log(`Lap ${msg.data.fromLap} completed: ${msg.data.lapTime}`);
112
+ break;
113
+ }
114
+ };
115
+ ```
116
+
117
+ ## API Reference
118
+
119
+ ### IBT Class
120
+
121
+ Parse and read iRacing telemetry binary files.
122
+
123
+ | Method | Description |
124
+ |--------|-------------|
125
+ | `open(path)` | Open an IBT file asynchronously |
126
+ | `get(varName, tick)` | Get a telemetry value at a specific tick |
127
+ | `getAll(varName)` | Get all values for a telemetry variable |
128
+ | `getSessionInfo(key)` | Get parsed session info by key |
129
+ | `getRawSessionInfo()` | Get raw YAML session info string |
130
+
131
+ | Property | Description |
132
+ |----------|-------------|
133
+ | `recordCount` | Number of telemetry records |
134
+ | `sessionLapCount` | Number of laps in session |
135
+ | `tickRate` | Telemetry sample rate (typically 60Hz) |
136
+ | `varHeaderNames` | List of available telemetry variables |
137
+
138
+ ### IRSDK Class
139
+
140
+ Connect to iRacing and read live telemetry data.
141
+
142
+ | Method | Description |
143
+ |--------|-------------|
144
+ | `startup()` | Connect to iRacing shared memory |
145
+ | `shutdown()` | Disconnect and cleanup |
146
+ | `get(varName)` | Get current value of a telemetry variable |
147
+ | `getSessionInfo(key)` | Get parsed session info by key |
148
+ | `freezeVarBufferLatest()` | Freeze telemetry buffer for consistent reads |
149
+ | `unfreezeVarBufferLatest()` | Unfreeze telemetry buffer |
150
+
151
+ | Property | Description |
152
+ |----------|-------------|
153
+ | `isConnected` | Whether connected to iRacing |
154
+ | `isInitialized` | Whether SDK is initialized |
155
+ | `sessionInfoUpdate` | Session info update counter |
156
+
157
+ ### Broadcast Commands
158
+
159
+ Control iRacing camera, replay, and more:
160
+
161
+ ```typescript
162
+ ir.camSwitchPos(position, group, camera);
163
+ ir.camSwitchNum(carNumber, group, camera);
164
+ ir.replaySetPlaySpeed(speed, slowMotion);
165
+ ir.replaySearch(searchMode);
166
+ ir.chatCommand(chatCommandMode);
167
+ ir.pitCommand(pitCommandMode, variable);
168
+ ```
169
+
170
+ ## Common Telemetry Variables
171
+
172
+ | Variable | Type | Description |
173
+ |----------|------|-------------|
174
+ | `Speed` | float | Vehicle speed (m/s) |
175
+ | `RPM` | float | Engine RPM |
176
+ | `Gear` | int | Current gear (-1=R, 0=N, 1-6=gears) |
177
+ | `Throttle` | float | Throttle position (0-1) |
178
+ | `Brake` | float | Brake position (0-1) |
179
+ | `Clutch` | float | Clutch position (0-1) |
180
+ | `SteeringWheelAngle` | float | Steering angle (radians) |
181
+ | `Lap` | int | Current lap number |
182
+ | `LapDistPct` | float | Lap completion percentage (0-1) |
183
+ | `LapCurrentLapTime` | float | Current lap time (seconds) |
184
+ | `SessionTime` | float | Session time (seconds) |
185
+ | `FuelLevel` | float | Fuel remaining (liters) |
186
+ | `OilTemp` | float | Oil temperature (C) |
187
+ | `WaterTemp` | float | Water temperature (C) |
188
+
189
+ See iRacing SDK documentation for the complete list of variables.
190
+
191
+ ## Scripts
192
+
193
+ ```bash
194
+ bun run build # Build the library
195
+ bun run build:server # Build standalone telemetry server executable
196
+ bun run server # Run telemetry WebSocket server
197
+ bun run dev # Run CLI in watch mode
198
+ bun run test # Run tests
199
+ bun run bench # Run benchmarks
200
+ ```
201
+
202
+ ## License
203
+
204
+ MIT - see [LICENSE](LICENSE)
205
+
206
+ ## Contributing
207
+
208
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
@@ -0,0 +1,18 @@
1
+ import type { VarType } from './constants';
2
+ import type { Header, VarBuffer, VarHeader, DiskSubHeader } from './types';
3
+ export declare function readInt32LE(view: DataView, offset: number): number;
4
+ export declare function readUint32LE(view: DataView, offset: number): number;
5
+ export declare function readFloat32LE(view: DataView, offset: number): number;
6
+ export declare function readFloat64LE(view: DataView, offset: number): number;
7
+ export declare function readBigUint64LE(view: DataView, offset: number): bigint;
8
+ export declare function readBoolLE(view: DataView, offset: number): boolean;
9
+ export declare function readCharLE(view: DataView, offset: number): number;
10
+ export declare function readString(buffer: Uint8Array, offset: number, length: number): string;
11
+ export declare function readVarValue(view: DataView, offset: number, type: VarType, count: number): number | number[] | boolean | boolean[];
12
+ export declare function readVarValueBatch<T extends Float32Array | Float64Array | Int32Array | Uint32Array>(view: DataView, baseOffset: number, varOffset: number, type: VarType, bufLen: number, recordCount: number, output: T): void;
13
+ export declare function parseHeader(view: DataView): Header;
14
+ export declare function parseVarHeader(view: DataView, buffer: Uint8Array, offset: number): VarHeader;
15
+ export declare function parseDiskSubHeader(view: DataView, offset: number): DiskSubHeader;
16
+ export declare function parseVarHeaders(view: DataView, buffer: Uint8Array, header: Header): Map<string, VarHeader>;
17
+ export declare function getLatestVarBuffer(varBufs: VarBuffer[]): VarBuffer;
18
+ //# sourceMappingURL=binary-reader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"binary-reader.d.ts","sourceRoot":"","sources":["../src/binary-reader.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAE3C,OAAO,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAI3E,wBAAgB,WAAW,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAElE;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAEnE;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAEpE;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAEpE;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAEtE;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAElE;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAEjE;AAED,wBAAgB,UAAU,CAAC,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAKrF;AAED,wBAAgB,YAAY,CAC1B,IAAI,EAAE,QAAQ,EACd,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,OAAO,EACb,KAAK,EAAE,MAAM,GACZ,MAAM,GAAG,MAAM,EAAE,GAAG,OAAO,GAAG,OAAO,EAAE,CAkCzC;AAED,wBAAgB,iBAAiB,CAAC,CAAC,SAAS,YAAY,GAAG,YAAY,GAAG,UAAU,GAAG,WAAW,EAChG,IAAI,EAAE,QAAQ,EACd,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,OAAO,EACb,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,CAAC,GACR,IAAI,CA0BN;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,CAkClD;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,GAAG,SAAS,CAU5F;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,GAAG,aAAa,CAQhF;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,SAAS,CAAC,CAO1G;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,SAAS,EAAE,GAAG,SAAS,CAmBlE"}
@@ -0,0 +1,206 @@
1
+ // @bun
2
+ import {
3
+ VAR_TYPE_SIZES
4
+ } from "./constants.js";
5
+
6
+ // src/binary-reader.ts
7
+ var textDecoder = new TextDecoder;
8
+ function readInt32LE(view, offset) {
9
+ return view.getInt32(offset, true);
10
+ }
11
+ function readUint32LE(view, offset) {
12
+ return view.getUint32(offset, true);
13
+ }
14
+ function readFloat32LE(view, offset) {
15
+ return view.getFloat32(offset, true);
16
+ }
17
+ function readFloat64LE(view, offset) {
18
+ return view.getFloat64(offset, true);
19
+ }
20
+ function readBigUint64LE(view, offset) {
21
+ return view.getBigUint64(offset, true);
22
+ }
23
+ function readBoolLE(view, offset) {
24
+ return view.getUint8(offset) !== 0;
25
+ }
26
+ function readCharLE(view, offset) {
27
+ return view.getInt8(offset);
28
+ }
29
+ function readString(buffer, offset, length) {
30
+ let end = offset;
31
+ const maxEnd = offset + length;
32
+ while (end < maxEnd && buffer[end] !== 0)
33
+ end++;
34
+ return textDecoder.decode(buffer.subarray(offset, end));
35
+ }
36
+ function readVarValue(view, offset, type, count) {
37
+ const size = VAR_TYPE_SIZES[type];
38
+ if (count === 1) {
39
+ switch (type) {
40
+ case 0:
41
+ return readCharLE(view, offset);
42
+ case 1:
43
+ return readBoolLE(view, offset);
44
+ case 2:
45
+ return readInt32LE(view, offset);
46
+ case 3:
47
+ return readUint32LE(view, offset);
48
+ case 4:
49
+ return readFloat32LE(view, offset);
50
+ case 5:
51
+ return readFloat64LE(view, offset);
52
+ }
53
+ }
54
+ if (type === 1) {
55
+ const result2 = new Array(count);
56
+ for (let i = 0;i < count; i++) {
57
+ result2[i] = readBoolLE(view, offset + i);
58
+ }
59
+ return result2;
60
+ }
61
+ const result = new Array(count);
62
+ for (let i = 0;i < count; i++) {
63
+ const pos = offset + i * size;
64
+ switch (type) {
65
+ case 0:
66
+ result[i] = readCharLE(view, pos);
67
+ break;
68
+ case 2:
69
+ result[i] = readInt32LE(view, pos);
70
+ break;
71
+ case 3:
72
+ result[i] = readUint32LE(view, pos);
73
+ break;
74
+ case 4:
75
+ result[i] = readFloat32LE(view, pos);
76
+ break;
77
+ case 5:
78
+ result[i] = readFloat64LE(view, pos);
79
+ break;
80
+ }
81
+ }
82
+ return result;
83
+ }
84
+ function readVarValueBatch(view, baseOffset, varOffset, type, bufLen, recordCount, output) {
85
+ const size = VAR_TYPE_SIZES[type];
86
+ let writeIdx = 0;
87
+ switch (type) {
88
+ case 4:
89
+ for (let i = 0;i < recordCount; i++) {
90
+ output[writeIdx++] = view.getFloat32(baseOffset + i * bufLen + varOffset, true);
91
+ }
92
+ break;
93
+ case 5:
94
+ for (let i = 0;i < recordCount; i++) {
95
+ output[writeIdx++] = view.getFloat64(baseOffset + i * bufLen + varOffset, true);
96
+ }
97
+ break;
98
+ case 2:
99
+ for (let i = 0;i < recordCount; i++) {
100
+ output[writeIdx++] = view.getInt32(baseOffset + i * bufLen + varOffset, true);
101
+ }
102
+ break;
103
+ case 3:
104
+ for (let i = 0;i < recordCount; i++) {
105
+ output[writeIdx++] = view.getUint32(baseOffset + i * bufLen + varOffset, true);
106
+ }
107
+ break;
108
+ }
109
+ }
110
+ function parseHeader(view) {
111
+ const version = readInt32LE(view, 0);
112
+ const status = readInt32LE(view, 4);
113
+ const tickRate = readInt32LE(view, 8);
114
+ const sessionInfoUpdate = readInt32LE(view, 12);
115
+ const sessionInfoLen = readInt32LE(view, 16);
116
+ const sessionInfoOffset = readInt32LE(view, 20);
117
+ const numVars = readInt32LE(view, 24);
118
+ const varHeaderOffset = readInt32LE(view, 28);
119
+ const numBuf = readInt32LE(view, 32);
120
+ const bufLen = readInt32LE(view, 36);
121
+ const varBuf = new Array(numBuf);
122
+ for (let i = 0;i < numBuf; i++) {
123
+ const offset = 48 + i * 16;
124
+ varBuf[i] = {
125
+ tickCount: readInt32LE(view, offset),
126
+ bufOffset: readInt32LE(view, offset + 4)
127
+ };
128
+ }
129
+ return {
130
+ version,
131
+ status,
132
+ tickRate,
133
+ sessionInfoUpdate,
134
+ sessionInfoLen,
135
+ sessionInfoOffset,
136
+ numVars,
137
+ varHeaderOffset,
138
+ numBuf,
139
+ bufLen,
140
+ varBuf
141
+ };
142
+ }
143
+ function parseVarHeader(view, buffer, offset) {
144
+ return {
145
+ type: readInt32LE(view, offset),
146
+ offset: readInt32LE(view, offset + 4),
147
+ count: readInt32LE(view, offset + 8),
148
+ countAsTime: readBoolLE(view, offset + 12),
149
+ name: readString(buffer, offset + 16, 32),
150
+ desc: readString(buffer, offset + 48, 64),
151
+ unit: readString(buffer, offset + 112, 32)
152
+ };
153
+ }
154
+ function parseDiskSubHeader(view, offset) {
155
+ return {
156
+ sessionStartDate: readBigUint64LE(view, offset),
157
+ sessionStartTime: readFloat64LE(view, offset + 8),
158
+ sessionEndTime: readFloat64LE(view, offset + 16),
159
+ sessionLapCount: readInt32LE(view, offset + 24),
160
+ sessionRecordCount: readInt32LE(view, offset + 28)
161
+ };
162
+ }
163
+ function parseVarHeaders(view, buffer, header) {
164
+ const headers = new Map;
165
+ for (let i = 0;i < header.numVars; i++) {
166
+ const varHeader = parseVarHeader(view, buffer, header.varHeaderOffset + i * 144);
167
+ headers.set(varHeader.name, varHeader);
168
+ }
169
+ return headers;
170
+ }
171
+ function getLatestVarBuffer(varBufs) {
172
+ if (varBufs.length === 0) {
173
+ throw new Error("varBufs array is empty");
174
+ }
175
+ let latest = varBufs[0];
176
+ let secondLatest = varBufs[1] ?? varBufs[0];
177
+ for (let i = 1;i < varBufs.length; i++) {
178
+ const current = varBufs[i];
179
+ if (current.tickCount > latest.tickCount) {
180
+ secondLatest = latest;
181
+ latest = current;
182
+ } else if (current.tickCount > secondLatest.tickCount) {
183
+ secondLatest = current;
184
+ }
185
+ }
186
+ return secondLatest;
187
+ }
188
+ export {
189
+ readVarValueBatch,
190
+ readVarValue,
191
+ readUint32LE,
192
+ readString,
193
+ readInt32LE,
194
+ readFloat64LE,
195
+ readFloat32LE,
196
+ readCharLE,
197
+ readBoolLE,
198
+ readBigUint64LE,
199
+ parseVarHeaders,
200
+ parseVarHeader,
201
+ parseHeader,
202
+ parseDiskSubHeader,
203
+ getLatestVarBuffer
204
+ };
205
+
206
+ export { readInt32LE, readUint32LE, readFloat32LE, readFloat64LE, readBigUint64LE, readBoolLE, readCharLE, readString, readVarValue, readVarValueBatch, parseHeader, parseVarHeader, parseDiskSubHeader, parseVarHeaders, getLatestVarBuffer };
package/dist/cli.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env bun
2
+ export {};
3
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}