@hotlap.ai/telemetry 1.2.0 → 1.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/CONTRIBUTING.md CHANGED
@@ -1,6 +1,6 @@
1
- # Contributing to @hotlap/telemetry
1
+ # Contributing to @hotlap.ai/telemetry
2
2
 
3
- Thank you for your interest in contributing to the HotLap.ai telemetry SDK!
3
+ Thank you for your interest in contributing to the HotLap.ai telemetry library!
4
4
 
5
5
  ## Getting Started
6
6
 
@@ -52,7 +52,7 @@ bun run typecheck
52
52
  src/
53
53
  binary-reader.ts # Low-level binary parsing utilities
54
54
  cli.ts # Command-line interface
55
- constants.ts # iRacing SDK constants and enums
55
+ constants.ts # iRacing telemetry constants and enums
56
56
  ibt.ts # IBT file parser
57
57
  index.ts # Main exports
58
58
  live.ts # Live telemetry via Windows FFI
package/README.md CHANGED
@@ -7,7 +7,9 @@
7
7
  [![TypeScript](https://img.shields.io/badge/TypeScript-5.7-blue.svg)](https://www.typescriptlang.org/)
8
8
  [![Bun](https://img.shields.io/badge/Bun-%3E%3D1.0.0-black.svg)](https://bun.sh/)
9
9
 
10
- High-performance iRacing telemetry SDK for [Bun](https://bun.sh). Parse IBT telemetry files and stream live data from iRacing via Windows shared memory.
10
+ High-performance iRacing telemetry library for [Bun](https://bun.sh), built by the [HotLap.ai](https://hotlap.ai) team. Parse IBT telemetry files and stream live data from iRacing via Windows shared memory.
11
+
12
+ > **Note:** This is an unofficial community library, not affiliated with or endorsed by iRacing.com.
11
13
 
12
14
  ## Features
13
15
 
@@ -193,7 +195,7 @@ ir.pitCommand(pitCommandMode, variable);
193
195
  | `OilTemp` | float | Oil temperature (C) |
194
196
  | `WaterTemp` | float | Water temperature (C) |
195
197
 
196
- See iRacing SDK documentation for the complete list of variables.
198
+ See [iRacing documentation](https://www.iracing.com/) for the complete list of variables.
197
199
 
198
200
  ## Scripts
199
201
 
package/dist/cli.js CHANGED
@@ -5,7 +5,7 @@ import {
5
5
  } from "./index-6wrtvcny.js";
6
6
  import {
7
7
  createIRSDK
8
- } from "./index-t4qz2011.js";
8
+ } from "./index-ndbp3f5t.js";
9
9
  import"./index-hxw291vy.js";
10
10
  import"./index-c5jf6rjt.js";
11
11
  import {
@@ -36,7 +36,7 @@ var { values, positionals } = parseArgs({
36
36
  });
37
37
  function printHelp() {
38
38
  console.log(`
39
- Bun iRacing SDK v${VERSION}
39
+ HotLap.ai Telemetry CLI v${VERSION}
40
40
 
41
41
  Usage: irsdk [options] [file.ibt]
42
42
 
@@ -67,7 +67,7 @@ Examples:
67
67
  }
68
68
  async function main() {
69
69
  if (values.version) {
70
- console.log(`Bun iRacing SDK v${VERSION}`);
70
+ console.log(`HotLap.ai Telemetry CLI v${VERSION}`);
71
71
  return;
72
72
  }
73
73
  if (values.help) {
@@ -104,6 +104,25 @@ function ensureFFILoaded() {
104
104
  return false;
105
105
  }
106
106
  }
107
+ var YAML_SPECIAL_FIELDS_REGEX = /((?:DriverSetupName|UserName|TeamName|AbbrevName|Initials): )(.*)/g;
108
+ var YAML_COMMA_VALUE_REGEX = /(\w+: )(,.*)/g;
109
+ function cleanYamlFast(yaml) {
110
+ let cleaned = "";
111
+ for (let i = 0;i < yaml.length; i++) {
112
+ const code = yaml.charCodeAt(i);
113
+ if (code === 129 || code === 141 || code === 143 || code === 144 || code === 157) {
114
+ cleaned += " ";
115
+ } else if (code === 9 || code === 10 || code === 13 || code >= 32 && code <= 126 || code >= 160 && code <= 255) {
116
+ cleaned += yaml[i];
117
+ }
118
+ }
119
+ cleaned = cleaned.replace(YAML_SPECIAL_FIELDS_REGEX, (_, prefix, value) => {
120
+ const escaped = value.replace(/["\\]/g, "\\$&");
121
+ return `${prefix}"${escaped}"`;
122
+ });
123
+ cleaned = cleaned.replace(YAML_COMMA_VALUE_REGEX, '$1"$2"');
124
+ return cleaned;
125
+ }
107
126
 
108
127
  class IRSDK {
109
128
  parseYamlAsync;
@@ -116,24 +135,74 @@ class IRSDK {
116
135
  varHeaders = null;
117
136
  varHeadersNames = null;
118
137
  sessionInfoCache = new Map;
138
+ static sessionRegexCache = new Map;
119
139
  broadcastMsgId = null;
120
140
  lastSessionInfoUpdate = 0;
121
141
  workaroundConnectedState = 0;
122
142
  frozenBuffer = null;
123
143
  frozenView = null;
124
144
  frozenBufOffset = 0;
145
+ frozenBufferCapacity = 0;
125
146
  testMode = false;
126
147
  isInitialized = false;
127
148
  constructor(options = {}) {
128
149
  this.parseYamlAsync = options.parseYamlAsync ?? false;
129
150
  }
151
+ liveHeaderInt(byteOffset, fallback = 0) {
152
+ if (this.testMode)
153
+ return fallback;
154
+ return this.sharedMemView ? this.sharedMemView.getInt32(byteOffset, true) : fallback;
155
+ }
156
+ get liveStatus() {
157
+ return this.liveHeaderInt(4, this.header?.status ?? 0);
158
+ }
159
+ liveReadBufOffset() {
160
+ const varBufs = this.header.varBuf;
161
+ if (this.testMode || !this.sharedMemView) {
162
+ return getLatestVarBuffer(varBufs).bufOffset;
163
+ }
164
+ let latestTick = -1;
165
+ let latestOffset = varBufs[0].bufOffset;
166
+ let secondTick = -1;
167
+ let secondOffset = varBufs[0].bufOffset;
168
+ for (let i = 0;i < varBufs.length; i++) {
169
+ const tick = this.liveHeaderInt(48 + i * 16);
170
+ const offset = varBufs[i].bufOffset;
171
+ if (tick > latestTick) {
172
+ secondTick = latestTick;
173
+ secondOffset = latestOffset;
174
+ latestTick = tick;
175
+ latestOffset = offset;
176
+ } else if (tick > secondTick) {
177
+ secondTick = tick;
178
+ secondOffset = offset;
179
+ }
180
+ }
181
+ return varBufs.length > 1 && secondTick >= 0 ? secondOffset : latestOffset;
182
+ }
183
+ liveLatestBufOffset() {
184
+ const varBufs = this.header.varBuf;
185
+ if (this.testMode || !this.sharedMemView) {
186
+ return varBufs.reduce((a, b) => a.tickCount > b.tickCount ? a : b).bufOffset;
187
+ }
188
+ let latestTick = -1;
189
+ let latestOffset = varBufs[0].bufOffset;
190
+ for (let i = 0;i < varBufs.length; i++) {
191
+ const tick = this.liveHeaderInt(48 + i * 16);
192
+ if (tick > latestTick) {
193
+ latestTick = tick;
194
+ latestOffset = varBufs[i].bufOffset;
195
+ }
196
+ }
197
+ return latestOffset;
198
+ }
130
199
  get isConnected() {
131
200
  if (!this.header)
132
201
  return false;
133
- if (this.header.status === StatusField.STATUS_CONNECTED) {
202
+ if (this.liveStatus === StatusField.STATUS_CONNECTED) {
134
203
  this.workaroundConnectedState = 0;
135
204
  }
136
- if (this.workaroundConnectedState === 0 && this.header.status !== StatusField.STATUS_CONNECTED) {
205
+ if (this.workaroundConnectedState === 0 && this.liveStatus !== StatusField.STATUS_CONNECTED) {
137
206
  this.workaroundConnectedState = 1;
138
207
  }
139
208
  if (this.workaroundConnectedState === 1 && this.get("SessionNum") === null) {
@@ -142,10 +211,10 @@ class IRSDK {
142
211
  if (this.workaroundConnectedState === 2 && this.get("SessionNum") !== null) {
143
212
  this.workaroundConnectedState = 3;
144
213
  }
145
- return (this.dataValidEvent !== null || this.testMode) && (this.header.status === StatusField.STATUS_CONNECTED || this.workaroundConnectedState === 3);
214
+ return (this.dataValidEvent !== null || this.testMode) && (this.liveStatus === StatusField.STATUS_CONNECTED || this.workaroundConnectedState === 3);
146
215
  }
147
216
  get sessionInfoUpdate() {
148
- return this.header?.sessionInfoUpdate ?? 0;
217
+ return this.liveHeaderInt(12, this.header?.sessionInfoUpdate ?? 0);
149
218
  }
150
219
  get varHeaderNames() {
151
220
  if (!this.varHeaders)
@@ -249,27 +318,119 @@ class IRSDK {
249
318
  if (!varHeader) {
250
319
  return this.getSessionInfo(key);
251
320
  }
252
- const view = this.frozenView ?? this.sharedMemView;
321
+ const isFrozen = this.isFrozen;
322
+ const view = isFrozen ? this.frozenView : this.sharedMemView;
253
323
  if (!view)
254
324
  return null;
255
- const varBuf = this.frozenView ? { bufOffset: this.frozenBufOffset } : getLatestVarBuffer(this.header.varBuf);
256
- const offset = varBuf.bufOffset + varHeader.offset;
257
- return readVarValue(view, offset, varHeader.type, varHeader.count);
325
+ const bufOffset = isFrozen ? this.frozenBufOffset : this.liveReadBufOffset();
326
+ return readVarValue(view, bufOffset + varHeader.offset, varHeader.type, varHeader.count);
327
+ }
328
+ getMultiple(keys) {
329
+ const results = new Map;
330
+ if (!this.header || !this.varHeaders)
331
+ return results;
332
+ const isFrozen = this.isFrozen;
333
+ const view = isFrozen ? this.frozenView : this.sharedMemView;
334
+ if (!view)
335
+ return results;
336
+ const bufOffset = isFrozen ? this.frozenBufOffset : this.liveReadBufOffset();
337
+ for (let i = 0;i < keys.length; i++) {
338
+ const key = keys[i];
339
+ const varHeader = this.varHeaders.get(key);
340
+ if (varHeader) {
341
+ results.set(key, readVarValue(view, bufOffset + varHeader.offset, varHeader.type, varHeader.count));
342
+ }
343
+ }
344
+ return results;
345
+ }
346
+ getMultipleInto(keys, out) {
347
+ if (!this.header || !this.varHeaders) {
348
+ for (let i = 0;i < keys.length; i++) {
349
+ out[keys[i]] = null;
350
+ }
351
+ return out;
352
+ }
353
+ const isFrozen = this.isFrozen;
354
+ const view = isFrozen ? this.frozenView : this.sharedMemView;
355
+ if (!view)
356
+ return out;
357
+ const bufOffset = isFrozen ? this.frozenBufOffset : this.liveReadBufOffset();
358
+ for (let i = 0;i < keys.length; i++) {
359
+ const key = keys[i];
360
+ const varHeader = this.varHeaders.get(key);
361
+ if (varHeader) {
362
+ out[key] = readVarValue(view, bufOffset + varHeader.offset, varHeader.type, varHeader.count);
363
+ } else {
364
+ out[key] = null;
365
+ }
366
+ }
367
+ return out;
368
+ }
369
+ createReader(keys) {
370
+ if (!this.header || !this.varHeaders) {
371
+ return {
372
+ read: () => keys.map(() => null),
373
+ keys
374
+ };
375
+ }
376
+ const varInfos = new Array(keys.length);
377
+ for (let i = 0;i < keys.length; i++) {
378
+ const varHeader = this.varHeaders.get(keys[i]);
379
+ if (varHeader) {
380
+ varInfos[i] = { offset: varHeader.offset, type: varHeader.type, count: varHeader.count };
381
+ } else {
382
+ varInfos[i] = null;
383
+ }
384
+ }
385
+ const self = this;
386
+ const results = new Array(keys.length);
387
+ return {
388
+ keys,
389
+ read() {
390
+ if (!self.header) {
391
+ for (let i = 0;i < keys.length; i++)
392
+ results[i] = null;
393
+ return results;
394
+ }
395
+ const isFrozen = self.isFrozen;
396
+ const view = isFrozen ? self.frozenView : self.sharedMemView;
397
+ if (!view) {
398
+ for (let i = 0;i < keys.length; i++)
399
+ results[i] = null;
400
+ return results;
401
+ }
402
+ const bufOffset = isFrozen ? self.frozenBufOffset : self.liveReadBufOffset();
403
+ for (let i = 0;i < varInfos.length; i++) {
404
+ const info = varInfos[i];
405
+ if (info) {
406
+ results[i] = readVarValue(view, bufOffset + info.offset, info.type, info.count);
407
+ } else {
408
+ results[i] = null;
409
+ }
410
+ }
411
+ return results;
412
+ }
413
+ };
258
414
  }
259
415
  freezeVarBufferLatest() {
260
- this.unfreezeVarBufferLatest();
261
416
  if (!this.header || !this.sharedMemBuffer)
262
417
  return;
263
418
  this.waitValidDataEvent();
264
- const varBuf = this.header.varBuf.reduce((a, b) => a.tickCount > b.tickCount ? a : b);
419
+ const latestBufOffset = this.liveLatestBufOffset();
420
+ const bufLen = this.header.bufLen;
421
+ if (!this.frozenBuffer || this.frozenBufferCapacity < bufLen) {
422
+ this.frozenBuffer = new Uint8Array(bufLen);
423
+ this.frozenBufferCapacity = bufLen;
424
+ this.frozenView = new DataView(this.frozenBuffer.buffer);
425
+ }
426
+ this.frozenBuffer.set(new Uint8Array(this.sharedMemBuffer.buffer, latestBufOffset, bufLen));
265
427
  this.frozenBufOffset = 0;
266
- this.frozenBuffer = this.sharedMemBuffer.slice(varBuf.bufOffset, varBuf.bufOffset + this.header.bufLen);
267
- this.frozenView = new DataView(this.frozenBuffer.buffer);
268
428
  }
269
429
  unfreezeVarBufferLatest() {
270
- this.frozenBuffer = null;
271
- this.frozenView = null;
272
- this.frozenBufOffset = 0;
430
+ this.frozenBufOffset = -1;
431
+ }
432
+ get isFrozen() {
433
+ return this.frozenBufOffset >= 0 && this.frozenView !== null;
273
434
  }
274
435
  async checkSimStatus() {
275
436
  try {
@@ -288,11 +449,23 @@ class IRSDK {
288
449
  }
289
450
  return true;
290
451
  }
452
+ static getSectionRegex(key) {
453
+ let regex = IRSDK.sessionRegexCache.get(key);
454
+ if (!regex) {
455
+ regex = new RegExp(`
456
+ ${key}:
457
+ ([\\s\\S]*?)(?=
458
+
459
+ |$)`);
460
+ IRSDK.sessionRegexCache.set(key, regex);
461
+ }
462
+ return regex;
463
+ }
291
464
  getSessionInfo(key) {
292
465
  if (!this.header || !this.sharedMemBuffer)
293
466
  return null;
294
- if (this.lastSessionInfoUpdate < this.header.sessionInfoUpdate) {
295
- this.lastSessionInfoUpdate = this.header.sessionInfoUpdate;
467
+ if (this.lastSessionInfoUpdate !== this.sessionInfoUpdate) {
468
+ this.lastSessionInfoUpdate = this.sessionInfoUpdate;
296
469
  for (const [k, v] of this.sessionInfoCache) {
297
470
  if (v.data) {
298
471
  this.sessionInfoCache.set(k, { ...v, data: null });
@@ -303,20 +476,16 @@ class IRSDK {
303
476
  if (cached?.data) {
304
477
  return cached.data;
305
478
  }
306
- const start = this.header.sessionInfoOffset;
307
- const len = this.header.sessionInfoLen;
479
+ const start = this.liveHeaderInt(20, this.header.sessionInfoOffset);
480
+ const len = this.liveHeaderInt(16, this.header.sessionInfoLen);
308
481
  const raw = readString(this.sharedMemBuffer, start, len);
309
- const sectionMatch = raw.match(new RegExp(`
310
- ${key}:
311
- ([\\s\\S]*?)(?=
312
-
313
- |$)`));
482
+ const sectionMatch = raw.match(IRSDK.getSectionRegex(key));
314
483
  if (!sectionMatch)
315
484
  return null;
316
485
  try {
317
486
  const sectionYaml = `${key}:
318
487
  ${sectionMatch[1]}`;
319
- const cleaned = this.cleanYaml(sectionYaml);
488
+ const cleaned = cleanYamlFast(sectionYaml);
320
489
  const parsed = $parse(cleaned);
321
490
  const result = parsed?.[key] ?? null;
322
491
  this.sessionInfoCache.set(key, { data: result, update: this.lastSessionInfoUpdate });
@@ -325,15 +494,6 @@ ${sectionMatch[1]}`;
325
494
  return null;
326
495
  }
327
496
  }
328
- cleanYaml(yaml) {
329
- let cleaned = yaml.replace(/[\x81\x8D\x8F\x90\x9D]/g, " ").replace(/[^\x09\x0A\x0D\x20-\x7E\xA0-\xFF]/g, "");
330
- cleaned = cleaned.replace(/((?:DriverSetupName|UserName|TeamName|AbbrevName|Initials): )(.*)/g, (_, prefix, value) => {
331
- const escaped = value.replace(/["\\]/g, "\\$&");
332
- return `${prefix}"${escaped}"`;
333
- });
334
- cleaned = cleaned.replace(/(\w+: )(,.*)/g, '$1"$2"');
335
- return cleaned;
336
- }
337
497
  getBroadcastMsgId() {
338
498
  if (!user32)
339
499
  return 0;
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Canonical lap point extraction — produces the per-sample telemetry series
3
+ * (byDist / byTime) that the UI charts and the backend lap_bundles store.
4
+ *
5
+ * Output shape matches the deployed IbtLapPoint contract exactly
6
+ * (UI/src/lib/ibt-extract.ts and BACKEND process-job convertToIbtLapData):
7
+ * - Speed m/s → km/h, Throttle/Brake 0–1 → 0–100 %, SteeringWheelAngle rad → deg
8
+ * - distanceKm normalized per lap: (LapDist − min LapDist) / 1000
9
+ * - timeSec normalized per lap: SessionTime − min SessionTime
10
+ * - tire temps LFtempM.. with LFtempCM.. carcass fallback (process-job behavior)
11
+ *
12
+ * CANONICAL DECISION — tire wear is percent 0–100 (×100 from the raw 0–1
13
+ * channel), matching process-job and therefore every stored lap bundle. The
14
+ * UI's ibt-extract.ts left wear at raw 0–1; that was the divergent one.
15
+ *
16
+ * RFC-0001 C8: extraction runs at FULL resolution; downsampling to maxPoints
17
+ * happens last and is presentation-only — never an input to metrics.
18
+ *
19
+ * RUNTIME NEUTRALITY: this module must not import Bun/Deno/Node APIs.
20
+ */
21
+ import type { IbtSource, NormalizedLap } from './normalize';
22
+ /** Matches the UI/backend IbtLapPoint contract field-for-field. */
23
+ export interface LapPoint {
24
+ distanceKm: number;
25
+ timeSec: number;
26
+ speedKmh: number | null;
27
+ throttlePct: number | null;
28
+ brakePct: number | null;
29
+ brakeABSactive: boolean | null;
30
+ gear: number | null;
31
+ rpm: number | null;
32
+ steeringDeg: number | null;
33
+ lat: number | null;
34
+ lon: number | null;
35
+ altitudeM: number | null;
36
+ /** Lateral acceleration in m/s² (iRacing LatAccel). Added in 1.4.0. */
37
+ latAccelMs2: number | null;
38
+ /** Longitudinal acceleration in m/s² (iRacing LongAccel). Added in 1.4.0. */
39
+ longAccelMs2: number | null;
40
+ /** Yaw rate in deg/s (iRacing YawRate rad/s → deg/s). Added in 1.4.0. */
41
+ yawRateDegS: number | null;
42
+ tireTempLF: number | null;
43
+ tireTempRF: number | null;
44
+ tireTempLR: number | null;
45
+ tireTempRR: number | null;
46
+ tirePressureLF: number | null;
47
+ tirePressureRF: number | null;
48
+ tirePressureLR: number | null;
49
+ tirePressureRR: number | null;
50
+ tireWearLF: number | null;
51
+ tireWearRF: number | null;
52
+ tireWearLR: number | null;
53
+ tireWearRR: number | null;
54
+ }
55
+ export interface ExtractLapPointsResult {
56
+ /** Points sorted ascending by distanceKm (downsampled). */
57
+ byDist: LapPoint[];
58
+ /** Points sorted ascending by timeSec (downsampled). */
59
+ byTime: LapPoint[];
60
+ /** Number of valid full-resolution points before downsampling. */
61
+ downsampledFrom: number;
62
+ }
63
+ export interface ExtractLapPointsOptions {
64
+ /** Maximum points per series after downsampling. Default 500. */
65
+ maxPoints?: number;
66
+ }
67
+ /**
68
+ * Extract the telemetry point series for one normalized lap.
69
+ *
70
+ * Iterates lap.firstSampleIndex..lastSampleIndex; when the Lap / SessionNum
71
+ * channels are available, only indices whose Lap equals lap.lapNumber (and
72
+ * whose SessionNum matches the lap's own session, read at firstSampleIndex)
73
+ * are honored. Without those channels the whole contiguous range is accepted.
74
+ */
75
+ export declare function extractLapPoints(source: IbtSource, lap: NormalizedLap, opts?: ExtractLapPointsOptions): ExtractLapPointsResult;
76
+ //# sourceMappingURL=lap-points.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lap-points.d.ts","sourceRoot":"","sources":["../src/lap-points.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAM5D,mEAAmE;AACnE,MAAM,WAAW,QAAQ;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,cAAc,EAAE,OAAO,GAAG,IAAI,CAAC;IAC/B,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,uEAAuE;IACvE,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,6EAA6E;IAC7E,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,yEAAyE;IACzE,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,MAAM,WAAW,sBAAsB;IACrC,2DAA2D;IAC3D,MAAM,EAAE,QAAQ,EAAE,CAAC;IACnB,wDAAwD;IACxD,MAAM,EAAE,QAAQ,EAAE,CAAC;IACnB,kEAAkE;IAClE,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,uBAAuB;IACtC,iEAAiE;IACjE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AA8CD;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,SAAS,EACjB,GAAG,EAAE,aAAa,EAClB,IAAI,CAAC,EAAE,uBAAuB,GAC7B,sBAAsB,CAqJxB"}
package/dist/live.d.ts CHANGED
@@ -13,15 +13,23 @@ export declare class IRSDK {
13
13
  private varHeaders;
14
14
  private varHeadersNames;
15
15
  private sessionInfoCache;
16
+ private static readonly sessionRegexCache;
16
17
  private broadcastMsgId;
17
18
  private lastSessionInfoUpdate;
18
19
  private workaroundConnectedState;
19
20
  private frozenBuffer;
20
21
  private frozenView;
21
22
  private frozenBufOffset;
23
+ private frozenBufferCapacity;
22
24
  private testMode;
23
25
  isInitialized: boolean;
24
26
  constructor(options?: IRSDKOptions);
27
+ private liveHeaderInt;
28
+ private get liveStatus();
29
+ /** Tear-safe read target: the second-latest buffer by LIVE tick count. */
30
+ private liveReadBufOffset;
31
+ /** Freeze target: the newest buffer by LIVE tick count. */
32
+ private liveLatestBufOffset;
25
33
  get isConnected(): boolean;
26
34
  get sessionInfoUpdate(): number;
27
35
  get varHeaderNames(): string[] | null;
@@ -29,12 +37,37 @@ export declare class IRSDK {
29
37
  private startupFromFile;
30
38
  shutdown(): void;
31
39
  get(key: string): TelemetryValue | null;
40
+ /**
41
+ * Batch read multiple telemetry variables in a single pass.
42
+ * Much more efficient than calling get() multiple times.
43
+ * @param keys Array of variable names to read
44
+ * @returns Map of variable names to their values
45
+ */
46
+ getMultiple(keys: string[]): Map<string, TelemetryValue>;
47
+ /**
48
+ * Batch read multiple telemetry variables into a pre-allocated object.
49
+ * Zero allocation in the hot path - fastest option for real-time telemetry.
50
+ * @param keys Array of variable names to read
51
+ * @param out Pre-allocated object to write values into
52
+ */
53
+ getMultipleInto<T extends Record<string, TelemetryValue | null>>(keys: string[], out: T): T;
54
+ /**
55
+ * Creates a pre-compiled reader for maximum performance in hot loops.
56
+ * Pre-computes all offsets at creation time, avoiding Map lookups during reads.
57
+ * @param keys Array of variable names to read
58
+ * @returns A reader object with a read() method that returns values as an array
59
+ */
60
+ createReader(keys: string[]): {
61
+ read: () => (TelemetryValue | null)[];
62
+ keys: string[];
63
+ };
32
64
  freezeVarBufferLatest(): void;
33
65
  unfreezeVarBufferLatest(): void;
66
+ private get isFrozen();
34
67
  private checkSimStatus;
35
68
  private waitValidDataEvent;
69
+ private static getSectionRegex;
36
70
  getSessionInfo<T = unknown>(key: string): T | null;
37
- private cleanYaml;
38
71
  private getBroadcastMsgId;
39
72
  private broadcastMsg;
40
73
  private padCarNum;
@@ -1 +1 @@
1
- {"version":3,"file":"live.d.ts","sourceRoot":"","sources":["../src/live.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAqB,cAAc,EAAE,MAAM,SAAS,CAAC;AAuGjE,MAAM,WAAW,YAAY;IAC3B,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,qBAAa,KAAK;IAChB,OAAO,CAAC,cAAc,CAAU;IAChC,OAAO,CAAC,YAAY,CAAiB;IACrC,OAAO,CAAC,eAAe,CAA2B;IAClD,OAAO,CAAC,aAAa,CAAyB;IAC9C,OAAO,CAAC,iBAAiB,CAAiB;IAC1C,OAAO,CAAC,cAAc,CAAiB;IACvC,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,UAAU,CAAuC;IACzD,OAAO,CAAC,eAAe,CAAyB;IAChD,OAAO,CAAC,gBAAgB,CAA6D;IACrF,OAAO,CAAC,cAAc,CAAuB;IAC7C,OAAO,CAAC,qBAAqB,CAAK;IAClC,OAAO,CAAC,wBAAwB,CAAK;IACrC,OAAO,CAAC,YAAY,CAA2B;IAC/C,OAAO,CAAC,UAAU,CAAyB;IAC3C,OAAO,CAAC,eAAe,CAAK;IAC5B,OAAO,CAAC,QAAQ,CAAS;IACzB,aAAa,UAAS;gBAEV,OAAO,GAAE,YAAiB;IAItC,IAAI,WAAW,IAAI,OAAO,CAoBzB;IAED,IAAI,iBAAiB,IAAI,MAAM,CAE9B;IAED,IAAI,cAAc,IAAI,MAAM,EAAE,GAAG,IAAI,CAMpC;IAEK,OAAO,CAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;YAkErD,eAAe;IAqB7B,QAAQ,IAAI,IAAI;IAiChB,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI;IAiBvC,qBAAqB,IAAI,IAAI;IAa7B,uBAAuB,IAAI,IAAI;YAMjB,cAAc;IAU5B,OAAO,CAAC,kBAAkB;IAQ1B,cAAc,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,CAAC,GAAG,IAAI;IAoClD,OAAO,CAAC,SAAS;IAkBjB,OAAO,CAAC,iBAAiB;IAUzB,OAAO,CAAC,YAAY;IAUpB,OAAO,CAAC,SAAS;IAcjB,YAAY,CAAC,QAAQ,SAAI,EAAE,KAAK,SAAI,EAAE,MAAM,SAAI,GAAG,OAAO;IAI1D,YAAY,CAAC,SAAS,GAAE,MAAM,GAAG,MAAY,EAAE,KAAK,SAAI,EAAE,MAAM,SAAI,GAAG,OAAO;IAI9E,WAAW,CAAC,WAAW,IAA8B,GAAG,OAAO;IAI/D,kBAAkB,CAAC,KAAK,SAAI,EAAE,UAAU,UAAQ,GAAG,OAAO;IAI1D,qBAAqB,CAAC,OAAO,IAAmB,EAAE,QAAQ,SAAI,GAAG,OAAO;IAIxE,YAAY,CAAC,UAAU,IAAuB,GAAG,OAAO;IAIxD,cAAc,CAAC,SAAS,IAA0B,GAAG,OAAO;IAI5D,iBAAiB,IAAI,OAAO;IAI5B,aAAa,CAAC,MAAM,SAAI,GAAG,OAAO;IAIlC,WAAW,CAAC,eAAe,IAA6B,GAAG,OAAO;IAIlE,gBAAgB,CAAC,QAAQ,SAAI,GAAG,OAAO;IAIvC,UAAU,CAAC,cAAc,IAAuB,EAAE,QAAQ,SAAI,GAAG,OAAO;IAIxE,YAAY,CAAC,gBAAgB,IAAwB,GAAG,OAAO;IAI/D,UAAU,CAAC,cAAc,IAAuC,EAAE,KAAK,SAAI,GAAG,OAAO;IAIrF,uBAAuB,CAAC,UAAU,SAAI,EAAE,aAAa,SAAI,GAAG,OAAO;IAInE,YAAY,CAAC,gBAAgB,IAAuC,GAAG,OAAO;CAG/E;AAED,wBAAgB,WAAW,CAAC,OAAO,CAAC,EAAE,YAAY,GAAG,KAAK,CAEzD"}
1
+ {"version":3,"file":"live.d.ts","sourceRoot":"","sources":["../src/live.ts"],"names":[],"mappings":"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"}
package/dist/live.js CHANGED
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  IRSDK,
4
4
  createIRSDK
5
- } from "./index-t4qz2011.js";
5
+ } from "./index-ndbp3f5t.js";
6
6
  import"./index-hxw291vy.js";
7
7
  import"./index-c5jf6rjt.js";
8
8
  import"./index-wb6ebd0j.js";