@v5x/serial 0.5.5 → 0.5.7

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 CHANGED
@@ -1,5 +1,3 @@
1
- var __create = Object.create;
2
- var __getProtoOf = Object.getPrototypeOf;
3
1
  var __defProp = Object.defineProperty;
4
2
  var __getOwnPropNames = Object.getOwnPropertyNames;
5
3
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -7,28 +5,6 @@ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
5
  function __accessProp(key) {
8
6
  return this[key];
9
7
  }
10
- var __toESMCache_node;
11
- var __toESMCache_esm;
12
- var __toESM = (mod, isNodeMode, target) => {
13
- var canCache = mod != null && typeof mod === "object";
14
- if (canCache) {
15
- var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
16
- var cached = cache.get(mod);
17
- if (cached)
18
- return cached;
19
- }
20
- target = mod != null ? __create(__getProtoOf(mod)) : {};
21
- const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
22
- for (let key of __getOwnPropNames(mod))
23
- if (!__hasOwnProp.call(to, key))
24
- __defProp(to, key, {
25
- get: __accessProp.bind(mod, key),
26
- enumerable: true
27
- });
28
- if (canCache)
29
- cache.set(mod, to);
30
- return to;
31
- };
32
8
  var __toCommonJS = (from) => {
33
9
  var entry = (__moduleCache ??= new WeakMap).get(from), desc;
34
10
  if (entry)
@@ -46,7 +22,6 @@ var __toCommonJS = (from) => {
46
22
  return entry;
47
23
  };
48
24
  var __moduleCache;
49
- var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
50
25
  var __returnValue = (v) => v;
51
26
  function __exportSetter(name, newValue) {
52
27
  this[name] = __returnValue.bind(null, newValue);
@@ -61,483 +36,6 @@ var __export = (target, all) => {
61
36
  });
62
37
  };
63
38
 
64
- // ../../node_modules/.bun/neverthrow@8.2.0/node_modules/neverthrow/dist/index.cjs.js
65
- var require_index_cjs = __commonJS((exports2) => {
66
- var defaultErrorConfig = {
67
- withStackTrace: false
68
- };
69
- var createNeverThrowError = (message, result, config = defaultErrorConfig) => {
70
- const data = result.isOk() ? { type: "Ok", value: result.value } : { type: "Err", value: result.error };
71
- const maybeStack = config.withStackTrace ? new Error().stack : undefined;
72
- return {
73
- data,
74
- message,
75
- stack: maybeStack
76
- };
77
- };
78
- function __awaiter(thisArg, _arguments, P, generator) {
79
- function adopt(value) {
80
- return value instanceof P ? value : new P(function(resolve) {
81
- resolve(value);
82
- });
83
- }
84
- return new (P || (P = Promise))(function(resolve, reject) {
85
- function fulfilled(value) {
86
- try {
87
- step(generator.next(value));
88
- } catch (e) {
89
- reject(e);
90
- }
91
- }
92
- function rejected(value) {
93
- try {
94
- step(generator["throw"](value));
95
- } catch (e) {
96
- reject(e);
97
- }
98
- }
99
- function step(result) {
100
- result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected);
101
- }
102
- step((generator = generator.apply(thisArg, _arguments || [])).next());
103
- });
104
- }
105
- function __values(o) {
106
- var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
107
- if (m)
108
- return m.call(o);
109
- if (o && typeof o.length === "number")
110
- return {
111
- next: function() {
112
- if (o && i >= o.length)
113
- o = undefined;
114
- return { value: o && o[i++], done: !o };
115
- }
116
- };
117
- throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
118
- }
119
- function __await(v) {
120
- return this instanceof __await ? (this.v = v, this) : new __await(v);
121
- }
122
- function __asyncGenerator(thisArg, _arguments, generator) {
123
- if (!Symbol.asyncIterator)
124
- throw new TypeError("Symbol.asyncIterator is not defined.");
125
- var g = generator.apply(thisArg, _arguments || []), i, q = [];
126
- return i = Object.create((typeof AsyncIterator === "function" ? AsyncIterator : Object).prototype), verb("next"), verb("throw"), verb("return", awaitReturn), i[Symbol.asyncIterator] = function() {
127
- return this;
128
- }, i;
129
- function awaitReturn(f) {
130
- return function(v) {
131
- return Promise.resolve(v).then(f, reject);
132
- };
133
- }
134
- function verb(n, f) {
135
- if (g[n]) {
136
- i[n] = function(v) {
137
- return new Promise(function(a, b) {
138
- q.push([n, v, a, b]) > 1 || resume(n, v);
139
- });
140
- };
141
- if (f)
142
- i[n] = f(i[n]);
143
- }
144
- }
145
- function resume(n, v) {
146
- try {
147
- step(g[n](v));
148
- } catch (e) {
149
- settle(q[0][3], e);
150
- }
151
- }
152
- function step(r) {
153
- r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r);
154
- }
155
- function fulfill(value) {
156
- resume("next", value);
157
- }
158
- function reject(value) {
159
- resume("throw", value);
160
- }
161
- function settle(f, v) {
162
- if (f(v), q.shift(), q.length)
163
- resume(q[0][0], q[0][1]);
164
- }
165
- }
166
- function __asyncDelegator(o) {
167
- var i, p;
168
- return i = {}, verb("next"), verb("throw", function(e) {
169
- throw e;
170
- }), verb("return"), i[Symbol.iterator] = function() {
171
- return this;
172
- }, i;
173
- function verb(n, f) {
174
- i[n] = o[n] ? function(v) {
175
- return (p = !p) ? { value: __await(o[n](v)), done: false } : f ? f(v) : v;
176
- } : f;
177
- }
178
- }
179
- function __asyncValues(o) {
180
- if (!Symbol.asyncIterator)
181
- throw new TypeError("Symbol.asyncIterator is not defined.");
182
- var m = o[Symbol.asyncIterator], i;
183
- return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function() {
184
- return this;
185
- }, i);
186
- function verb(n) {
187
- i[n] = o[n] && function(v) {
188
- return new Promise(function(resolve, reject) {
189
- v = o[n](v), settle(resolve, reject, v.done, v.value);
190
- });
191
- };
192
- }
193
- function settle(resolve, reject, d, v) {
194
- Promise.resolve(v).then(function(v2) {
195
- resolve({ value: v2, done: d });
196
- }, reject);
197
- }
198
- }
199
-
200
- class ResultAsync {
201
- constructor(res) {
202
- this._promise = res;
203
- }
204
- static fromSafePromise(promise) {
205
- const newPromise = promise.then((value) => new Ok(value));
206
- return new ResultAsync(newPromise);
207
- }
208
- static fromPromise(promise, errorFn) {
209
- const newPromise = promise.then((value) => new Ok(value)).catch((e) => new Err(errorFn(e)));
210
- return new ResultAsync(newPromise);
211
- }
212
- static fromThrowable(fn, errorFn) {
213
- return (...args) => {
214
- return new ResultAsync((() => __awaiter(this, undefined, undefined, function* () {
215
- try {
216
- return new Ok(yield fn(...args));
217
- } catch (error) {
218
- return new Err(errorFn ? errorFn(error) : error);
219
- }
220
- }))());
221
- };
222
- }
223
- static combine(asyncResultList) {
224
- return combineResultAsyncList(asyncResultList);
225
- }
226
- static combineWithAllErrors(asyncResultList) {
227
- return combineResultAsyncListWithAllErrors(asyncResultList);
228
- }
229
- map(f) {
230
- return new ResultAsync(this._promise.then((res) => __awaiter(this, undefined, undefined, function* () {
231
- if (res.isErr()) {
232
- return new Err(res.error);
233
- }
234
- return new Ok(yield f(res.value));
235
- })));
236
- }
237
- andThrough(f) {
238
- return new ResultAsync(this._promise.then((res) => __awaiter(this, undefined, undefined, function* () {
239
- if (res.isErr()) {
240
- return new Err(res.error);
241
- }
242
- const newRes = yield f(res.value);
243
- if (newRes.isErr()) {
244
- return new Err(newRes.error);
245
- }
246
- return new Ok(res.value);
247
- })));
248
- }
249
- andTee(f) {
250
- return new ResultAsync(this._promise.then((res) => __awaiter(this, undefined, undefined, function* () {
251
- if (res.isErr()) {
252
- return new Err(res.error);
253
- }
254
- try {
255
- yield f(res.value);
256
- } catch (e) {}
257
- return new Ok(res.value);
258
- })));
259
- }
260
- orTee(f) {
261
- return new ResultAsync(this._promise.then((res) => __awaiter(this, undefined, undefined, function* () {
262
- if (res.isOk()) {
263
- return new Ok(res.value);
264
- }
265
- try {
266
- yield f(res.error);
267
- } catch (e) {}
268
- return new Err(res.error);
269
- })));
270
- }
271
- mapErr(f) {
272
- return new ResultAsync(this._promise.then((res) => __awaiter(this, undefined, undefined, function* () {
273
- if (res.isOk()) {
274
- return new Ok(res.value);
275
- }
276
- return new Err(yield f(res.error));
277
- })));
278
- }
279
- andThen(f) {
280
- return new ResultAsync(this._promise.then((res) => {
281
- if (res.isErr()) {
282
- return new Err(res.error);
283
- }
284
- const newValue = f(res.value);
285
- return newValue instanceof ResultAsync ? newValue._promise : newValue;
286
- }));
287
- }
288
- orElse(f) {
289
- return new ResultAsync(this._promise.then((res) => __awaiter(this, undefined, undefined, function* () {
290
- if (res.isErr()) {
291
- return f(res.error);
292
- }
293
- return new Ok(res.value);
294
- })));
295
- }
296
- match(ok2, _err) {
297
- return this._promise.then((res) => res.match(ok2, _err));
298
- }
299
- unwrapOr(t) {
300
- return this._promise.then((res) => res.unwrapOr(t));
301
- }
302
- safeUnwrap() {
303
- return __asyncGenerator(this, arguments, function* safeUnwrap_1() {
304
- return yield __await(yield __await(yield* __asyncDelegator(__asyncValues(yield __await(this._promise.then((res) => res.safeUnwrap()))))));
305
- });
306
- }
307
- then(successCallback, failureCallback) {
308
- return this._promise.then(successCallback, failureCallback);
309
- }
310
- [Symbol.asyncIterator]() {
311
- return __asyncGenerator(this, arguments, function* _a() {
312
- const result = yield __await(this._promise);
313
- if (result.isErr()) {
314
- yield yield __await(errAsync(result.error));
315
- }
316
- return yield __await(result.value);
317
- });
318
- }
319
- }
320
- function okAsync(value) {
321
- return new ResultAsync(Promise.resolve(new Ok(value)));
322
- }
323
- function errAsync(err2) {
324
- return new ResultAsync(Promise.resolve(new Err(err2)));
325
- }
326
- var fromPromise = ResultAsync.fromPromise;
327
- var fromSafePromise = ResultAsync.fromSafePromise;
328
- var fromAsyncThrowable = ResultAsync.fromThrowable;
329
- var combineResultList = (resultList) => {
330
- let acc = ok([]);
331
- for (const result of resultList) {
332
- if (result.isErr()) {
333
- acc = err(result.error);
334
- break;
335
- } else {
336
- acc.map((list) => list.push(result.value));
337
- }
338
- }
339
- return acc;
340
- };
341
- var combineResultAsyncList = (asyncResultList) => ResultAsync.fromSafePromise(Promise.all(asyncResultList)).andThen(combineResultList);
342
- var combineResultListWithAllErrors = (resultList) => {
343
- let acc = ok([]);
344
- for (const result of resultList) {
345
- if (result.isErr() && acc.isErr()) {
346
- acc.error.push(result.error);
347
- } else if (result.isErr() && acc.isOk()) {
348
- acc = err([result.error]);
349
- } else if (result.isOk() && acc.isOk()) {
350
- acc.value.push(result.value);
351
- }
352
- }
353
- return acc;
354
- };
355
- var combineResultAsyncListWithAllErrors = (asyncResultList) => ResultAsync.fromSafePromise(Promise.all(asyncResultList)).andThen(combineResultListWithAllErrors);
356
- exports2.Result = undefined;
357
- (function(Result) {
358
- function fromThrowable2(fn, errorFn) {
359
- return (...args) => {
360
- try {
361
- const result = fn(...args);
362
- return ok(result);
363
- } catch (e) {
364
- return err(errorFn ? errorFn(e) : e);
365
- }
366
- };
367
- }
368
- Result.fromThrowable = fromThrowable2;
369
- function combine(resultList) {
370
- return combineResultList(resultList);
371
- }
372
- Result.combine = combine;
373
- function combineWithAllErrors(resultList) {
374
- return combineResultListWithAllErrors(resultList);
375
- }
376
- Result.combineWithAllErrors = combineWithAllErrors;
377
- })(exports2.Result || (exports2.Result = {}));
378
- function ok(value) {
379
- return new Ok(value);
380
- }
381
- function err(err2) {
382
- return new Err(err2);
383
- }
384
- function safeTry(body) {
385
- const n = body().next();
386
- if (n instanceof Promise) {
387
- return new ResultAsync(n.then((r) => r.value));
388
- }
389
- return n.value;
390
- }
391
-
392
- class Ok {
393
- constructor(value) {
394
- this.value = value;
395
- }
396
- isOk() {
397
- return true;
398
- }
399
- isErr() {
400
- return !this.isOk();
401
- }
402
- map(f) {
403
- return ok(f(this.value));
404
- }
405
- mapErr(_f) {
406
- return ok(this.value);
407
- }
408
- andThen(f) {
409
- return f(this.value);
410
- }
411
- andThrough(f) {
412
- return f(this.value).map((_value) => this.value);
413
- }
414
- andTee(f) {
415
- try {
416
- f(this.value);
417
- } catch (e) {}
418
- return ok(this.value);
419
- }
420
- orTee(_f) {
421
- return ok(this.value);
422
- }
423
- orElse(_f) {
424
- return ok(this.value);
425
- }
426
- asyncAndThen(f) {
427
- return f(this.value);
428
- }
429
- asyncAndThrough(f) {
430
- return f(this.value).map(() => this.value);
431
- }
432
- asyncMap(f) {
433
- return ResultAsync.fromSafePromise(f(this.value));
434
- }
435
- unwrapOr(_v) {
436
- return this.value;
437
- }
438
- match(ok2, _err) {
439
- return ok2(this.value);
440
- }
441
- safeUnwrap() {
442
- const value = this.value;
443
- return function* () {
444
- return value;
445
- }();
446
- }
447
- _unsafeUnwrap(_) {
448
- return this.value;
449
- }
450
- _unsafeUnwrapErr(config) {
451
- throw createNeverThrowError("Called `_unsafeUnwrapErr` on an Ok", this, config);
452
- }
453
- *[Symbol.iterator]() {
454
- return this.value;
455
- }
456
- }
457
-
458
- class Err {
459
- constructor(error) {
460
- this.error = error;
461
- }
462
- isOk() {
463
- return false;
464
- }
465
- isErr() {
466
- return !this.isOk();
467
- }
468
- map(_f) {
469
- return err(this.error);
470
- }
471
- mapErr(f) {
472
- return err(f(this.error));
473
- }
474
- andThrough(_f) {
475
- return err(this.error);
476
- }
477
- andTee(_f) {
478
- return err(this.error);
479
- }
480
- orTee(f) {
481
- try {
482
- f(this.error);
483
- } catch (e) {}
484
- return err(this.error);
485
- }
486
- andThen(_f) {
487
- return err(this.error);
488
- }
489
- orElse(f) {
490
- return f(this.error);
491
- }
492
- asyncAndThen(_f) {
493
- return errAsync(this.error);
494
- }
495
- asyncAndThrough(_f) {
496
- return errAsync(this.error);
497
- }
498
- asyncMap(_f) {
499
- return errAsync(this.error);
500
- }
501
- unwrapOr(v) {
502
- return v;
503
- }
504
- match(_ok, err2) {
505
- return err2(this.error);
506
- }
507
- safeUnwrap() {
508
- const error = this.error;
509
- return function* () {
510
- yield err(error);
511
- throw new Error("Do not use this generator out of `safeTry`");
512
- }();
513
- }
514
- _unsafeUnwrap(config) {
515
- throw createNeverThrowError("Called `_unsafeUnwrap` on an Err", this, config);
516
- }
517
- _unsafeUnwrapErr(_) {
518
- return this.error;
519
- }
520
- *[Symbol.iterator]() {
521
- const self = this;
522
- yield self;
523
- return self;
524
- }
525
- }
526
- var fromThrowable = exports2.Result.fromThrowable;
527
- exports2.Err = Err;
528
- exports2.Ok = Ok;
529
- exports2.ResultAsync = ResultAsync;
530
- exports2.err = err;
531
- exports2.errAsync = errAsync;
532
- exports2.fromAsyncThrowable = fromAsyncThrowable;
533
- exports2.fromPromise = fromPromise;
534
- exports2.fromSafePromise = fromSafePromise;
535
- exports2.fromThrowable = fromThrowable;
536
- exports2.ok = ok;
537
- exports2.okAsync = okAsync;
538
- exports2.safeTry = safeTry;
539
- });
540
-
541
39
  // src/index.ts
542
40
  var exports_src = {};
543
41
  __export(exports_src, {
@@ -755,6 +253,7 @@ var AckType;
755
253
  AckType2[AckType2["CDC2_NACK_FILE_SYS_FULL"] = 220] = "CDC2_NACK_FILE_SYS_FULL";
756
254
  AckType2[AckType2["TIMEOUT"] = 256] = "TIMEOUT";
757
255
  AckType2[AckType2["WRITE_ERROR"] = 257] = "WRITE_ERROR";
256
+ AckType2[AckType2["NOT_CONNECTED"] = 258] = "NOT_CONNECTED";
758
257
  })(AckType ||= {});
759
258
  var SmartDeviceType;
760
259
  ((SmartDeviceType2) => {
@@ -850,6 +349,7 @@ var SelectDashScreen;
850
349
  // src/VexError.ts
851
350
  class VexSerialError extends Error {
852
351
  kind;
352
+ ackType = undefined;
853
353
  constructor(kind, message) {
854
354
  super(message);
855
355
  this.name = "VexSerialError";
@@ -872,9 +372,11 @@ class VexInvalidArgumentError extends VexSerialError {
872
372
  }
873
373
 
874
374
  class VexProtocolError extends VexSerialError {
875
- constructor(message) {
375
+ ackType;
376
+ constructor(message, ackType) {
876
377
  super("protocol", message);
877
378
  this.name = "VexProtocolError";
379
+ this.ackType = ackType;
878
380
  }
879
381
  }
880
382
 
@@ -934,9 +436,26 @@ class VexEventEmitter {
934
436
  this.handlerMap.set(eventName, listeners);
935
437
  }
936
438
  emit(eventName, data) {
937
- (this.handlerMap.get(eventName) ?? []).forEach((callback) => {
938
- callback(data);
939
- });
439
+ const listeners = this.handlerMap.get(eventName);
440
+ if (listeners === undefined || listeners.length === 0)
441
+ return;
442
+ if (listeners.length === 1) {
443
+ listeners[0](data);
444
+ return;
445
+ }
446
+ const errors = [];
447
+ for (const callback of [...listeners]) {
448
+ try {
449
+ callback(data);
450
+ } catch (error) {
451
+ errors.push(error);
452
+ }
453
+ }
454
+ if (errors.length === 1)
455
+ throw errors[0];
456
+ if (errors.length > 1) {
457
+ throw new AggregateError(errors, `listeners for ${String(eventName)} failed`);
458
+ }
940
459
  }
941
460
  clearListeners() {
942
461
  this.handlerMap.clear();
@@ -949,13 +468,13 @@ class VexEventTarget {
949
468
  this.emitter = new VexEventEmitter;
950
469
  }
951
470
  emit(eventName, data) {
952
- this.emitter.emit(String(eventName), data);
471
+ this.emitter.emit(eventName, data);
953
472
  }
954
473
  on(eventName, listener) {
955
- this.emitter.on(String(eventName), listener);
474
+ this.emitter.on(eventName, listener);
956
475
  }
957
476
  remove(eventName, listener) {
958
- this.emitter.remove(String(eventName), listener);
477
+ this.emitter.remove(eventName, listener);
959
478
  }
960
479
  clearListeners() {
961
480
  this.emitter.clearListeners();
@@ -963,7 +482,7 @@ class VexEventTarget {
963
482
  }
964
483
 
965
484
  // src/VexConnection.ts
966
- var import_neverthrow = __toESM(require_index_cjs(), 1);
485
+ var import_neverthrow = require("neverthrow");
967
486
 
968
487
  // src/VexPacketBase.ts
969
488
  var MATCH_STATUS_ALT_ACK = 167;
@@ -977,42 +496,32 @@ class Packet {
977
496
  }
978
497
 
979
498
  class DeviceBoundPacket extends Packet {
499
+ static COMMAND_ID;
500
+ static COMMAND_EXTENDED_ID;
980
501
  get commandId() {
981
502
  return this.constructor.COMMAND_ID;
982
503
  }
983
504
  get commandExtendedId() {
984
505
  return this.constructor.COMMAND_EXTENDED_ID;
985
506
  }
986
- static COMMAND_ID;
987
- static COMMAND_EXTENDED_ID;
988
507
  constructor(payload) {
989
- super(new Uint8Array);
990
- const me = this.constructor;
991
- if (me.COMMAND_EXTENDED_ID === undefined) {
992
- if (payload === undefined) {
993
- this.data = Packet.ENCODER.cdcCommand(me.COMMAND_ID);
994
- } else {
995
- this.data = Packet.ENCODER.cdcCommandWithData(me.COMMAND_ID, payload);
996
- }
997
- } else {
998
- if (payload === undefined) {
999
- this.data = Packet.ENCODER.cdc2Command(me.COMMAND_ID, me.COMMAND_EXTENDED_ID);
1000
- } else {
1001
- this.data = Packet.ENCODER.cdc2CommandWithData(me.COMMAND_ID, me.COMMAND_EXTENDED_ID, payload);
1002
- }
1003
- }
508
+ const { COMMAND_ID: cmd, COMMAND_EXTENDED_ID: ext } = new.target;
509
+ const encoder = Packet.ENCODER;
510
+ super(ext === undefined ? payload === undefined ? encoder.cdcCommand(cmd) : encoder.cdcCommandWithData(cmd, payload) : payload === undefined ? encoder.cdc2Command(cmd, ext) : encoder.cdc2CommandWithData(cmd, ext, payload));
1004
511
  }
1005
512
  }
1006
513
 
1007
514
  class HostBoundPacket extends Packet {
1008
- ack = 255 /* CDC2_NACK */;
515
+ static COMMAND_ID;
516
+ static COMMAND_EXTENDED_ID;
517
+ ack;
1009
518
  payloadSize;
1010
519
  ackIndex;
1011
520
  constructor(data) {
1012
521
  super(data);
1013
522
  this.payloadSize = Packet.ENCODER.getPayloadSize(this.data);
1014
- const n = Packet.ENCODER.getHostHeaderLength(this.data);
1015
- this.ack = this.data[this.ackIndex = n + 1];
523
+ this.ackIndex = Packet.ENCODER.getHostHeaderLength(this.data) + 1;
524
+ this.ack = this.data[this.ackIndex];
1016
525
  }
1017
526
  static isValidPacket(data, n) {
1018
527
  const ack = data[n + 1];
@@ -1021,385 +530,162 @@ class HostBoundPacket extends Packet {
1021
530
  }
1022
531
 
1023
532
  // src/VexCRC.ts
1024
- var CRC16TABLE = [
1025
- 0,
1026
- 4129,
1027
- 8258,
1028
- 12387,
1029
- 16516,
1030
- 20645,
1031
- 24774,
1032
- 28903,
1033
- 33032,
1034
- 37161,
1035
- 41290,
1036
- 45419,
1037
- 49548,
1038
- 53677,
1039
- 57806,
1040
- 61935,
1041
- 4657,
1042
- 528,
1043
- 12915,
1044
- 8786,
1045
- 21173,
1046
- 17044,
1047
- 29431,
1048
- 25302,
1049
- 37689,
1050
- 33560,
1051
- 45947,
1052
- 41818,
1053
- 54205,
1054
- 50076,
1055
- 62463,
1056
- 58334,
1057
- 9314,
1058
- 13379,
1059
- 1056,
1060
- 5121,
1061
- 25830,
1062
- 29895,
1063
- 17572,
1064
- 21637,
1065
- 42346,
1066
- 46411,
1067
- 34088,
1068
- 38153,
1069
- 58862,
1070
- 62927,
1071
- 50604,
1072
- 54669,
1073
- 13907,
1074
- 9842,
1075
- 5649,
1076
- 1584,
1077
- 30423,
1078
- 26358,
1079
- 22165,
1080
- 18100,
1081
- 46939,
1082
- 42874,
1083
- 38681,
1084
- 34616,
1085
- 63455,
1086
- 59390,
1087
- 55197,
1088
- 51132,
1089
- 18628,
1090
- 22757,
1091
- 26758,
1092
- 30887,
1093
- 2112,
1094
- 6241,
1095
- 10242,
1096
- 14371,
1097
- 51660,
1098
- 55789,
1099
- 59790,
1100
- 63919,
1101
- 35144,
1102
- 39273,
1103
- 43274,
1104
- 47403,
1105
- 23285,
1106
- 19156,
1107
- 31415,
1108
- 27286,
1109
- 6769,
1110
- 2640,
1111
- 14899,
1112
- 10770,
1113
- 56317,
1114
- 52188,
1115
- 64447,
1116
- 60318,
1117
- 39801,
1118
- 35672,
1119
- 47931,
1120
- 43802,
1121
- 27814,
1122
- 31879,
1123
- 19684,
1124
- 23749,
1125
- 11298,
1126
- 15363,
1127
- 3168,
1128
- 7233,
1129
- 60846,
1130
- 64911,
1131
- 52716,
1132
- 56781,
1133
- 44330,
1134
- 48395,
1135
- 36200,
1136
- 40265,
1137
- 32407,
1138
- 28342,
1139
- 24277,
1140
- 20212,
1141
- 15891,
1142
- 11826,
1143
- 7761,
1144
- 3696,
1145
- 65439,
1146
- 61374,
1147
- 57309,
1148
- 53244,
1149
- 48923,
1150
- 44858,
1151
- 40793,
1152
- 36728,
1153
- 37256,
1154
- 33193,
1155
- 45514,
1156
- 41451,
1157
- 53516,
1158
- 49453,
1159
- 61774,
1160
- 57711,
1161
- 4224,
1162
- 161,
1163
- 12482,
1164
- 8419,
1165
- 20484,
1166
- 16421,
1167
- 28742,
1168
- 24679,
1169
- 33721,
1170
- 37784,
1171
- 41979,
1172
- 46042,
1173
- 49981,
1174
- 54044,
1175
- 58239,
1176
- 62302,
1177
- 689,
1178
- 4752,
1179
- 8947,
1180
- 13010,
1181
- 16949,
1182
- 21012,
1183
- 25207,
1184
- 29270,
1185
- 46570,
1186
- 42443,
1187
- 38312,
1188
- 34185,
1189
- 62830,
1190
- 58703,
1191
- 54572,
1192
- 50445,
1193
- 13538,
1194
- 9411,
1195
- 5280,
1196
- 1153,
1197
- 29798,
1198
- 25671,
1199
- 21540,
1200
- 17413,
1201
- 42971,
1202
- 47098,
1203
- 34713,
1204
- 38840,
1205
- 59231,
1206
- 63358,
1207
- 50973,
1208
- 55100,
1209
- 9939,
1210
- 14066,
1211
- 1681,
1212
- 5808,
1213
- 26199,
1214
- 30326,
1215
- 17941,
1216
- 22068,
1217
- 55628,
1218
- 51565,
1219
- 63758,
1220
- 59695,
1221
- 39368,
1222
- 35305,
1223
- 47498,
1224
- 43435,
1225
- 22596,
1226
- 18533,
1227
- 30726,
1228
- 26663,
1229
- 6336,
1230
- 2273,
1231
- 14466,
1232
- 10403,
1233
- 52093,
1234
- 56156,
1235
- 60223,
1236
- 64286,
1237
- 35833,
1238
- 39896,
1239
- 43963,
1240
- 48026,
1241
- 19061,
1242
- 23124,
1243
- 27191,
1244
- 31254,
1245
- 2801,
1246
- 6864,
1247
- 10931,
1248
- 14994,
1249
- 64814,
1250
- 60687,
1251
- 56684,
1252
- 52557,
1253
- 48554,
1254
- 44427,
1255
- 40424,
1256
- 36297,
1257
- 31782,
1258
- 27655,
1259
- 23652,
1260
- 19525,
1261
- 15522,
1262
- 11395,
1263
- 7392,
1264
- 3265,
1265
- 61215,
1266
- 65342,
1267
- 53085,
1268
- 57212,
1269
- 44955,
1270
- 49082,
1271
- 36825,
1272
- 40952,
1273
- 28183,
1274
- 32310,
1275
- 20053,
1276
- 24180,
1277
- 11923,
1278
- 16050,
1279
- 3793,
1280
- 7920
1281
- ];
533
+ var POLYNOMIAL_CRC32 = 79764919;
534
+ var POLYNOMIAL_CRC16 = 4129;
535
+ function genTable16() {
536
+ const table = new Uint16Array(256);
537
+ for (let i = 0;i < 256; i++) {
538
+ let acc = i << 8;
539
+ for (let j = 0;j < 8; j++) {
540
+ acc = (acc & 32768) !== 0 ? acc << 1 ^ POLYNOMIAL_CRC16 : acc << 1;
541
+ }
542
+ table[i] = acc & 65535;
543
+ }
544
+ return table;
545
+ }
546
+ function genTable32() {
547
+ const table = new Uint32Array(256);
548
+ for (let i = 0;i < 256; i++) {
549
+ let acc = i << 24;
550
+ for (let j = 0;j < 8; j++) {
551
+ acc = (acc & 2147483648) !== 0 ? acc << 1 ^ POLYNOMIAL_CRC32 : acc << 1;
552
+ }
553
+ table[i] = acc;
554
+ }
555
+ return table;
556
+ }
557
+ var CRC16_TABLE = genTable16();
558
+ var CRC32_TABLE = genTable32();
1282
559
 
1283
560
  class CrcGenerator {
1284
- crc32Table;
1285
- static POLYNOMIAL_CRC32 = 79764919;
1286
- static POLYNOMIAL_CRC16 = 4129;
1287
- constructor() {
1288
- this.crc32Table = new Uint32Array(256);
1289
- this.crc32GenTable();
1290
- }
561
+ crc32Table = CRC32_TABLE;
562
+ static POLYNOMIAL_CRC32 = POLYNOMIAL_CRC32;
563
+ static POLYNOMIAL_CRC16 = POLYNOMIAL_CRC16;
1291
564
  crc16(buf, initValue) {
1292
- const numberOfBytes = buf.byteLength;
1293
- let accumulator = initValue;
1294
- let i;
1295
- let j;
1296
- for (j = 0;j < numberOfBytes; j++) {
1297
- i = (accumulator >>> 8 ^ buf[j]) & 255;
1298
- accumulator = (accumulator << 8 ^ CRC16TABLE[i]) >>> 0;
1299
- }
1300
- return (accumulator & 65535) >>> 0;
1301
- }
1302
- crc32GenTable() {
1303
- let i;
1304
- let j;
1305
- let crcAccumulator;
1306
- for (i = 0;i < 256; i++) {
1307
- crcAccumulator = i << 24;
1308
- for (j = 0;j < 8; j++) {
1309
- if ((crcAccumulator & 2147483648) !== 0)
1310
- crcAccumulator = crcAccumulator << 1 ^ CrcGenerator.POLYNOMIAL_CRC32;
1311
- else
1312
- crcAccumulator = crcAccumulator << 1;
1313
- }
1314
- this.crc32Table[i] = crcAccumulator;
565
+ let acc = initValue;
566
+ for (let j = 0;j < buf.length; j++) {
567
+ acc = (acc << 8 ^ CRC16_TABLE[(acc >>> 8 ^ buf[j]) & 255]) >>> 0;
1315
568
  }
569
+ return acc & 65535;
1316
570
  }
1317
571
  crc32(buf, initValue) {
1318
- const numberOfBytes = buf.byteLength;
1319
- let crcAccumulator = initValue;
1320
- let i;
1321
- let j;
1322
- for (j = 0;j < numberOfBytes; j++) {
1323
- i = (crcAccumulator >>> 24 ^ buf[j]) & 255;
1324
- crcAccumulator = (crcAccumulator << 8 ^ this.crc32Table[i]) >>> 0;
572
+ let acc = initValue;
573
+ for (let j = 0;j < buf.length; j++) {
574
+ acc = (acc << 8 ^ CRC32_TABLE[(acc >>> 24 ^ buf[j]) & 255]) >>> 0;
1325
575
  }
1326
- return (crcAccumulator & 4294967295) >>> 0;
576
+ return acc >>> 0;
1327
577
  }
1328
578
  }
1329
579
 
1330
- // src/VexPacketModels.ts
1331
- var exports_VexPacketModels = {};
1332
- __export(exports_VexPacketModels, {
1333
- WriteKeyValueReplyD2HPacket: () => WriteKeyValueReplyD2HPacket,
1334
- WriteKeyValueH2DPacket: () => WriteKeyValueH2DPacket,
1335
- WriteFileReplyD2HPacket: () => WriteFileReplyD2HPacket,
1336
- WriteFileH2DPacket: () => WriteFileH2DPacket,
1337
- UpdateMatchModeH2DPacket: () => UpdateMatchModeH2DPacket,
1338
- SystemVersionReplyD2HPacket: () => SystemVersionReplyD2HPacket,
1339
- SystemVersionH2DPacket: () => SystemVersionH2DPacket,
1340
- SendDashTouchReplyD2HPacket: () => SendDashTouchReplyD2HPacket,
1341
- SendDashTouchH2DPacket: () => SendDashTouchH2DPacket,
1342
- SelectDashReplyD2HPacket: () => SelectDashReplyD2HPacket,
1343
- SelectDashH2DPacket: () => SelectDashH2DPacket,
1344
- ScreenCaptureReplyD2HPacket: () => ScreenCaptureReplyD2HPacket,
1345
- ScreenCaptureH2DPacket: () => ScreenCaptureH2DPacket,
1346
- ReadLogPageReplyD2HPacket: () => ReadLogPageReplyD2HPacket,
1347
- ReadLogPageH2DPacket: () => ReadLogPageH2DPacket,
1348
- ReadKeyValueReplyD2HPacket: () => ReadKeyValueReplyD2HPacket,
1349
- ReadKeyValueH2DPacket: () => ReadKeyValueH2DPacket,
1350
- ReadFileReplyD2HPacket: () => ReadFileReplyD2HPacket,
1351
- ReadFileH2DPacket: () => ReadFileH2DPacket,
1352
- Query1ReplyD2HPacket: () => Query1ReplyD2HPacket,
1353
- Query1H2DPacket: () => Query1H2DPacket,
1354
- MatchStatusReplyD2HPacket: () => MatchStatusReplyD2HPacket,
1355
- MatchModeReplyD2HPacket: () => MatchModeReplyD2HPacket,
1356
- LoadFileActionReplyD2HPacket: () => LoadFileActionReplyD2HPacket,
1357
- LoadFileActionH2DPacket: () => LoadFileActionH2DPacket,
1358
- LinkFileReplyD2HPacket: () => LinkFileReplyD2HPacket,
1359
- LinkFileH2DPacket: () => LinkFileH2DPacket,
1360
- InitFileTransferReplyD2HPacket: () => InitFileTransferReplyD2HPacket,
1361
- InitFileTransferH2DPacket: () => InitFileTransferH2DPacket,
1362
- GetSystemStatusReplyD2HPacket: () => GetSystemStatusReplyD2HPacket,
1363
- GetSystemStatusH2DPacket: () => GetSystemStatusH2DPacket,
1364
- GetSystemFlagsReplyD2HPacket: () => GetSystemFlagsReplyD2HPacket,
1365
- GetSystemFlagsH2DPacket: () => GetSystemFlagsH2DPacket,
1366
- GetSlot5to8InfoReplyD2HPacket: () => GetSlot5to8InfoReplyD2HPacket,
1367
- GetSlot5to8InfoH2DPacket: () => GetSlot5to8InfoH2DPacket,
1368
- GetSlot1to4InfoReplyD2HPacket: () => GetSlot1to4InfoReplyD2HPacket,
1369
- GetSlot1to4InfoH2DPacket: () => GetSlot1to4InfoH2DPacket,
1370
- GetRadioStatusReplyD2HPacket: () => GetRadioStatusReplyD2HPacket,
1371
- GetRadioStatusH2DPacket: () => GetRadioStatusH2DPacket,
1372
- GetRadioModeH2DPacket: () => GetRadioModeH2DPacket,
1373
- GetProgramSlotInfoReplyD2HPacket: () => GetProgramSlotInfoReplyD2HPacket,
1374
- GetProgramSlotInfoH2DPacket: () => GetProgramSlotInfoH2DPacket,
1375
- GetMatchStatusH2DPacket: () => GetMatchStatusH2DPacket,
1376
- GetLogCountReplyD2HPacket: () => GetLogCountReplyD2HPacket,
1377
- GetLogCountH2DPacket: () => GetLogCountH2DPacket,
1378
- GetFileMetadataReplyD2HPacket: () => GetFileMetadataReplyD2HPacket,
1379
- GetFileMetadataH2DPacket: () => GetFileMetadataH2DPacket,
1380
- GetFdtStatusReplyD2HPacket: () => GetFdtStatusReplyD2HPacket,
1381
- GetFdtStatusH2DPacket: () => GetFdtStatusH2DPacket,
1382
- GetDirectoryFileCountReplyD2HPacket: () => GetDirectoryFileCountReplyD2HPacket,
1383
- GetDirectoryFileCountH2DPacket: () => GetDirectoryFileCountH2DPacket,
1384
- GetDirectoryEntryReplyD2HPacket: () => GetDirectoryEntryReplyD2HPacket,
1385
- GetDirectoryEntryH2DPacket: () => GetDirectoryEntryH2DPacket,
1386
- GetDeviceStatusReplyD2HPacket: () => GetDeviceStatusReplyD2HPacket,
1387
- GetDeviceStatusH2DPacket: () => GetDeviceStatusH2DPacket,
1388
- FileFormatReplyD2HPacket: () => FileFormatReplyD2HPacket,
1389
- FileFormatH2DPacket: () => FileFormatH2DPacket,
1390
- FileControlReplyD2HPacket: () => FileControlReplyD2HPacket,
1391
- FileControlH2DPacket: () => FileControlH2DPacket,
1392
- FileClearUpReplyD2HPacket: () => FileClearUpReplyD2HPacket,
1393
- FileClearUpH2DPacket: () => FileClearUpH2DPacket,
1394
- FactoryStatusReplyD2HPacket: () => FactoryStatusReplyD2HPacket,
1395
- FactoryStatusH2DPacket: () => FactoryStatusH2DPacket,
1396
- FactoryEnableReplyD2HPacket: () => FactoryEnableReplyD2HPacket,
1397
- FactoryEnableH2DPacket: () => FactoryEnableH2DPacket,
1398
- ExitFileTransferReplyD2HPacket: () => ExitFileTransferReplyD2HPacket,
1399
- ExitFileTransferH2DPacket: () => ExitFileTransferH2DPacket,
1400
- EraseFileReplyD2HPacket: () => EraseFileReplyD2HPacket,
1401
- EraseFileH2DPacket: () => EraseFileH2DPacket
1402
- });
580
+ // src/VexPacketEncoder.ts
581
+ var textEncoder = new TextEncoder;
582
+ var HEADER_TO_DEVICE = Uint8Array.of(201, 54, 184, 71);
583
+ function encodeFixedText(value, field, maxBytes) {
584
+ const encoded = textEncoder.encode(value);
585
+ if (encoded.byteLength > maxBytes) {
586
+ throw new RangeError(`${field} must be at most ${maxBytes} UTF-8 bytes`);
587
+ }
588
+ return encoded;
589
+ }
590
+
591
+ class PacketEncoder {
592
+ static HEADERS_LENGTH = 4;
593
+ static HEADER_TO_DEVICE = [201, 54, 184, 71];
594
+ static HEADER_TO_HOST = [170, 85];
595
+ static J2000_EPOCH = 946684800;
596
+ vexVersion = 0;
597
+ crcgen = new CrcGenerator;
598
+ allPacketsTable = new Map;
599
+ static getInstance() {
600
+ Packet.ENCODER ??= new PacketEncoder;
601
+ return Packet.ENCODER;
602
+ }
603
+ constructor() {}
604
+ registerPacketTypes(types) {
605
+ for (const type of types) {
606
+ let byExtendedId = this.allPacketsTable.get(type.COMMAND_ID);
607
+ if (byExtendedId === undefined) {
608
+ byExtendedId = new Map;
609
+ this.allPacketsTable.set(type.COMMAND_ID, byExtendedId);
610
+ }
611
+ byExtendedId.set(type.COMMAND_EXTENDED_ID, type);
612
+ }
613
+ }
614
+ getPacketType(commandId, commandExtendedId) {
615
+ if (commandId === undefined)
616
+ return;
617
+ return this.allPacketsTable.get(commandId)?.get(commandExtendedId);
618
+ }
619
+ createHeader(buf) {
620
+ if (buf === undefined || buf.byteLength < PacketEncoder.HEADERS_LENGTH) {
621
+ buf = new ArrayBuffer(PacketEncoder.HEADERS_LENGTH);
622
+ }
623
+ const h = new Uint8Array(buf);
624
+ h.set(HEADER_TO_DEVICE);
625
+ return h;
626
+ }
627
+ cdcCommand(cmd) {
628
+ const h = new Uint8Array(5);
629
+ h.set(HEADER_TO_DEVICE);
630
+ h[4] = cmd;
631
+ return h;
632
+ }
633
+ cdcCommandWithData(cmd, data) {
634
+ const h = new Uint8Array(6 + data.length);
635
+ h.set(HEADER_TO_DEVICE);
636
+ h[4] = cmd;
637
+ h[5] = data.length;
638
+ h.set(data, 6);
639
+ return h;
640
+ }
641
+ cdc2Command(cmd, ext) {
642
+ const h = new Uint8Array(9);
643
+ h.set(HEADER_TO_DEVICE);
644
+ h[4] = cmd;
645
+ h[5] = ext;
646
+ h[6] = 0;
647
+ this.appendCrc16(h);
648
+ return h;
649
+ }
650
+ cdc2CommandBufferLength(data) {
651
+ return PacketEncoder.HEADERS_LENGTH + data.length + 5 + (data.length > 127 ? 1 : 0);
652
+ }
653
+ cdc2CommandWithData(cmd, ext, data) {
654
+ const h = new Uint8Array(this.cdc2CommandBufferLength(data));
655
+ h.set(HEADER_TO_DEVICE);
656
+ h[4] = cmd;
657
+ h[5] = ext;
658
+ if (data.length < 128) {
659
+ h[6] = data.length;
660
+ h.set(data, 7);
661
+ } else {
662
+ h[6] = data.length >>> 8 | 128;
663
+ h[7] = data.length & 255;
664
+ h.set(data, 8);
665
+ }
666
+ this.appendCrc16(h);
667
+ return h;
668
+ }
669
+ appendCrc16(h) {
670
+ const crc = this.crcgen.crc16(h.subarray(0, h.length - 2), 0);
671
+ h[h.length - 2] = crc >>> 8;
672
+ h[h.length - 1] = crc & 255;
673
+ }
674
+ validateHeader(data) {
675
+ return data[0] === PacketEncoder.HEADER_TO_HOST[0] && data[1] === PacketEncoder.HEADER_TO_HOST[1];
676
+ }
677
+ validateMessageCdc(data) {
678
+ const crc = (data[data.byteLength - 2] << 8) + data[data.byteLength - 1];
679
+ return this.crcgen.crc16(data.subarray(0, data.byteLength - 2), 0) === crc;
680
+ }
681
+ getPayloadSize(data) {
682
+ const a = data[3];
683
+ return (a & 128) === 0 ? a : ((a & 127) << 8) + data[4];
684
+ }
685
+ getHostHeaderLength(data) {
686
+ return (data[3] & 128) === 0 ? 4 : 5;
687
+ }
688
+ }
1403
689
 
1404
690
  // src/VexFirmwareVersion.ts
1405
691
  class VexFirmwareVersion {
@@ -1415,9 +701,8 @@ class VexFirmwareVersion {
1415
701
  }
1416
702
  static fromString(version) {
1417
703
  const parts = version.toLowerCase().replace(/b/g, "").split(".").map((x) => parseInt(x, 10));
1418
- while (parts.length < 4) {
704
+ while (parts.length < 4)
1419
705
  parts.push(0);
1420
- }
1421
706
  return new VexFirmwareVersion(parts[0], parts[1], parts[2], parts[3]);
1422
707
  }
1423
708
  static fromUint8Array(data, offset = 0, reverse = false) {
@@ -1447,24 +732,23 @@ class VexFirmwareVersion {
1447
732
  return `${this.toUserString()}.b${this.beta}`;
1448
733
  }
1449
734
  compare(that) {
1450
- const majorComp = this.major - that.major;
1451
- const minorComp = this.minor - that.minor;
1452
- const buildComp = this.build - that.build;
1453
- const betaComp = this.beta - that.beta;
1454
- if (majorComp !== 0) {
1455
- return majorComp;
1456
- } else if (minorComp !== 0) {
1457
- return minorComp;
1458
- } else if (buildComp !== 0) {
1459
- return buildComp;
1460
- } else if (betaComp !== 0) {
1461
- return betaComp;
735
+ for (const [a, b] of [
736
+ [this.major, that.major],
737
+ [this.minor, that.minor],
738
+ [this.build, that.build],
739
+ [this.beta, that.beta]
740
+ ]) {
741
+ const delta = a - b;
742
+ if (delta !== 0)
743
+ return delta;
1462
744
  }
1463
745
  return 0;
1464
746
  }
1465
747
  }
1466
748
 
1467
749
  // src/VexPacketView.ts
750
+ var textDecoder = new TextDecoder("UTF-8");
751
+
1468
752
  class PacketView extends DataView {
1469
753
  position = 0;
1470
754
  littleEndianDefault = true;
@@ -1472,19 +756,15 @@ class PacketView extends DataView {
1472
756
  super(buffer, offset, length);
1473
757
  }
1474
758
  static fromPacket(packet) {
1475
- const view = new PacketView(packet.data.buffer, packet.data.byteOffset);
759
+ const view = new PacketView(packet.data.buffer, packet.data.byteOffset, packet.data.byteLength);
1476
760
  view.position = packet.ackIndex + 1;
1477
761
  return view;
1478
762
  }
1479
763
  nextInt8() {
1480
- const result = this.getInt8(this.position);
1481
- this.position += 1;
1482
- return result;
764
+ return this.getInt8(this.position++);
1483
765
  }
1484
766
  nextUint8() {
1485
- const result = this.getUint8(this.position);
1486
- this.position += 1;
1487
- return result;
767
+ return this.getUint8(this.position++);
1488
768
  }
1489
769
  nextInt16(littleEndian = this.littleEndianDefault) {
1490
770
  const result = this.getInt16(this.position, littleEndian);
@@ -1507,37 +787,28 @@ class PacketView extends DataView {
1507
787
  return result;
1508
788
  }
1509
789
  nextString(length) {
1510
- let result = "";
1511
- for (let i = 0;i < length; i++) {
1512
- result += String.fromCharCode(this.nextUint8());
1513
- }
790
+ const result = textDecoder.decode(new Uint8Array(this.buffer, this.byteOffset + this.position, length));
791
+ this.position += length;
1514
792
  return result;
1515
793
  }
1516
794
  nextNTBS(length) {
1517
- let result = "";
1518
- const lastPosition = this.position;
1519
- for (let i = 0;i < length; i++) {
1520
- if (this.byteLength <= this.position)
1521
- break;
1522
- const g = this.nextUint8();
1523
- if (g === 0)
1524
- break;
1525
- result += String.fromCharCode(g);
1526
- }
1527
- this.position = lastPosition + length;
795
+ const start = this.position;
796
+ const result = this.nextVarNTBS(length);
797
+ this.position = start + length;
1528
798
  return result;
1529
799
  }
1530
800
  nextVarNTBS(length) {
1531
- let result = "";
801
+ const start = this.position;
802
+ let byteLength = 0;
1532
803
  for (let i = 0;i < length; i++) {
1533
804
  if (this.byteLength <= this.position)
1534
805
  break;
1535
806
  const g = this.nextUint8();
1536
807
  if (g === 0)
1537
808
  break;
1538
- result += String.fromCharCode(g);
809
+ byteLength++;
1539
810
  }
1540
- return result;
811
+ return textDecoder.decode(new Uint8Array(this.buffer, this.byteOffset + start, byteLength));
1541
812
  }
1542
813
  nextVersion(reverse = false) {
1543
814
  const result = VexFirmwareVersion.fromUint8Array(new Uint8Array(this.buffer, this.byteOffset, this.byteLength), this.position, reverse);
@@ -1547,43 +818,34 @@ class PacketView extends DataView {
1547
818
  }
1548
819
 
1549
820
  // src/VexPacketModels.ts
1550
- var textEncoder = new TextEncoder;
821
+ var textEncoder2 = new TextEncoder;
822
+ function filePayload(a, b, fileName) {
823
+ const payload = new Uint8Array(26);
824
+ payload[0] = a;
825
+ payload[1] = b;
826
+ payload.set(encodeFixedText(fileName, "Filename", 24), 2);
827
+ return payload;
828
+ }
829
+ var clamp100 = (value) => value !== undefined && value > 100 ? 100 : value;
1551
830
 
1552
831
  class Query1H2DPacket extends DeviceBoundPacket {
1553
832
  static COMMAND_ID = 33;
1554
833
  static COMMAND_EXTENDED_ID = undefined;
1555
- constructor() {
1556
- super(undefined);
1557
- }
1558
834
  }
1559
835
 
1560
836
  class SystemVersionH2DPacket extends DeviceBoundPacket {
1561
837
  static COMMAND_ID = 164;
1562
838
  static COMMAND_EXTENDED_ID = undefined;
1563
- constructor() {
1564
- super(undefined);
1565
- }
1566
839
  }
1567
840
 
1568
841
  class UpdateMatchModeH2DPacket extends DeviceBoundPacket {
1569
842
  static COMMAND_ID = 88;
1570
843
  static COMMAND_EXTENDED_ID = 193;
1571
844
  constructor(mode, matchClock) {
1572
- let bit1;
1573
- switch (mode) {
1574
- case "autonomous":
1575
- bit1 = 10;
1576
- break;
1577
- case "driver":
1578
- bit1 = 8;
1579
- break;
1580
- case "disabled":
1581
- bit1 = 11;
1582
- }
845
+ const bit1 = mode === "autonomous" ? 10 : mode === "driver" ? 8 : 11;
1583
846
  const payload = new Uint8Array(5);
1584
- const view = new DataView(payload.buffer);
1585
- payload[0] = (15 & bit1) >>> 0;
1586
- view.setUint32(1, matchClock, true);
847
+ payload[0] = bit1 & 15;
848
+ new DataView(payload.buffer).setUint32(1, matchClock, true);
1587
849
  super(payload);
1588
850
  }
1589
851
  }
@@ -1591,18 +853,13 @@ class UpdateMatchModeH2DPacket extends DeviceBoundPacket {
1591
853
  class GetMatchStatusH2DPacket extends DeviceBoundPacket {
1592
854
  static COMMAND_ID = 88;
1593
855
  static COMMAND_EXTENDED_ID = 194;
1594
- constructor() {
1595
- super(undefined);
1596
- }
1597
856
  }
1598
857
 
1599
858
  class GetRadioModeH2DPacket extends DeviceBoundPacket {
1600
859
  static COMMAND_ID = 88;
1601
860
  static COMMAND_EXTENDED_ID = 65;
1602
861
  constructor(mode) {
1603
- const payload = new Uint8Array(1);
1604
- payload[0] = mode;
1605
- super(payload);
862
+ super(Uint8Array.of(mode));
1606
863
  }
1607
864
  }
1608
865
 
@@ -1610,9 +867,7 @@ class FileControlH2DPacket extends DeviceBoundPacket {
1610
867
  static COMMAND_ID = 86;
1611
868
  static COMMAND_EXTENDED_ID = 16;
1612
869
  constructor(a, b) {
1613
- const payload = new Uint8Array(2);
1614
- payload.set([a, b], 0);
1615
- super(payload);
870
+ super(Uint8Array.of(a, b));
1616
871
  }
1617
872
  }
1618
873
 
@@ -1622,25 +877,21 @@ class InitFileTransferH2DPacket extends DeviceBoundPacket {
1622
877
  constructor(operation, target, vendor, options, binary, addr, name, type, version = new VexFirmwareVersion(1, 0, 0, 0)) {
1623
878
  const payload = new Uint8Array(52);
1624
879
  const view = new DataView(payload.buffer);
1625
- view.setUint8(0, operation);
1626
- view.setUint8(1, target);
1627
- view.setUint8(2, vendor);
1628
- view.setUint8(3, options);
880
+ payload[0] = operation;
881
+ payload[1] = target;
882
+ payload[2] = vendor;
883
+ payload[3] = options;
1629
884
  view.setUint32(4, binary.length, true);
1630
885
  view.setUint32(8, addr, true);
1631
886
  view.setUint32(12, operation === 1 /* WRITE */ ? Packet.ENCODER.crcgen.crc32(binary, 0) : 0, true);
1632
- const re = /(?:\.([^.]+))?$/;
1633
- const reResult = re.exec(name);
1634
- let ext = reResult != null ? reResult[1] : undefined;
1635
- ext ??= "";
1636
- ext = ext === "gz" ? "bin" : ext;
887
+ let ext = /(?:\.([^.]+))?$/.exec(name)?.[1] ?? "";
888
+ if (ext === "gz")
889
+ ext = "bin";
1637
890
  payload.set(encodeFixedText(type ?? ext, "File type", 4), 16);
1638
891
  const timestamp = (Date.now() / 1000 >>> 0) - PacketEncoder.J2000_EPOCH;
1639
892
  view.setUint32(20, timestamp, true);
1640
893
  payload.set(version.toUint8Array(), 24);
1641
- const nameEncoded = encodeFixedText(name, "Filename", 23);
1642
- payload.set(nameEncoded, 28);
1643
- view.setUint8(51, 0);
894
+ payload.set(encodeFixedText(name, "Filename", 24), 28);
1644
895
  super(payload);
1645
896
  }
1646
897
  }
@@ -1649,9 +900,7 @@ class ExitFileTransferH2DPacket extends DeviceBoundPacket {
1649
900
  static COMMAND_ID = 86;
1650
901
  static COMMAND_EXTENDED_ID = 18;
1651
902
  constructor(action) {
1652
- const payload = new Uint8Array(1);
1653
- payload[0] = action;
1654
- super(payload);
903
+ super(Uint8Array.of(action));
1655
904
  }
1656
905
  }
1657
906
 
@@ -1660,8 +909,7 @@ class WriteFileH2DPacket extends DeviceBoundPacket {
1660
909
  static COMMAND_EXTENDED_ID = 19;
1661
910
  constructor(addr, buf) {
1662
911
  const payload = new Uint8Array(4 + buf.length);
1663
- const view = new DataView(payload.buffer);
1664
- view.setUint32(0, addr, true);
912
+ new DataView(payload.buffer).setUint32(0, addr, true);
1665
913
  payload.set(buf, 4);
1666
914
  super(payload);
1667
915
  }
@@ -1683,11 +931,7 @@ class LinkFileH2DPacket extends DeviceBoundPacket {
1683
931
  static COMMAND_ID = 86;
1684
932
  static COMMAND_EXTENDED_ID = 21;
1685
933
  constructor(vendor, fileName, options) {
1686
- const str = encodeFixedText(fileName, "Filename", 23);
1687
- const payload = new Uint8Array(26);
1688
- payload.set([vendor, options], 0);
1689
- payload.set(str, 2);
1690
- super(payload);
934
+ super(filePayload(vendor, options, fileName));
1691
935
  }
1692
936
  }
1693
937
 
@@ -1695,9 +939,7 @@ class GetDirectoryFileCountH2DPacket extends DeviceBoundPacket {
1695
939
  static COMMAND_ID = 86;
1696
940
  static COMMAND_EXTENDED_ID = 22;
1697
941
  constructor(vendor) {
1698
- const payload = new Uint8Array(2);
1699
- payload.set([vendor, 0], 0);
1700
- super(payload);
942
+ super(Uint8Array.of(vendor, 0));
1701
943
  }
1702
944
  }
1703
945
 
@@ -1705,9 +947,7 @@ class GetDirectoryEntryH2DPacket extends DeviceBoundPacket {
1705
947
  static COMMAND_ID = 86;
1706
948
  static COMMAND_EXTENDED_ID = 23;
1707
949
  constructor(index) {
1708
- const payload = new Uint8Array(2);
1709
- payload.set([index, 0], 0);
1710
- super(payload);
950
+ super(Uint8Array.of(index, 0));
1711
951
  }
1712
952
  }
1713
953
 
@@ -1715,17 +955,8 @@ class LoadFileActionH2DPacket extends DeviceBoundPacket {
1715
955
  static COMMAND_ID = 86;
1716
956
  static COMMAND_EXTENDED_ID = 24;
1717
957
  constructor(vendor, actionId, fileNameOrSlotNumber) {
1718
- let fileName;
1719
- if (typeof fileNameOrSlotNumber === "string") {
1720
- fileName = fileNameOrSlotNumber;
1721
- } else {
1722
- fileName = "___s_" + (fileNameOrSlotNumber - 1) + ".bin";
1723
- }
1724
- const encodedName = encodeFixedText(fileName, "Filename", 23);
1725
- const payload = new Uint8Array(26);
1726
- payload.set([vendor, actionId], 0);
1727
- payload.set(encodedName, 2);
1728
- super(payload);
958
+ const fileName = typeof fileNameOrSlotNumber === "string" ? fileNameOrSlotNumber : `___s_${fileNameOrSlotNumber - 1}.bin`;
959
+ super(filePayload(vendor, actionId, fileName));
1729
960
  }
1730
961
  }
1731
962
 
@@ -1733,11 +964,7 @@ class GetFileMetadataH2DPacket extends DeviceBoundPacket {
1733
964
  static COMMAND_ID = 86;
1734
965
  static COMMAND_EXTENDED_ID = 25;
1735
966
  constructor(vendor, fileName, options) {
1736
- const encodedName = encodeFixedText(fileName, "Filename", 23);
1737
- const payload = new Uint8Array(26);
1738
- payload.set([vendor, options], 0);
1739
- payload.set(encodedName, 2);
1740
- super(payload);
967
+ super(filePayload(vendor, options, fileName));
1741
968
  }
1742
969
  }
1743
970
 
@@ -1745,11 +972,7 @@ class EraseFileH2DPacket extends DeviceBoundPacket {
1745
972
  static COMMAND_ID = 86;
1746
973
  static COMMAND_EXTENDED_ID = 27;
1747
974
  constructor(vendor, fileName) {
1748
- const encodedName = encodeFixedText(fileName, "Filename", 23);
1749
- const payload = new Uint8Array(26);
1750
- payload.set([vendor, 128], 0);
1751
- payload.set(encodedName, 2);
1752
- super(payload);
975
+ super(filePayload(vendor, 128, fileName));
1753
976
  }
1754
977
  }
1755
978
 
@@ -1757,11 +980,7 @@ class GetProgramSlotInfoH2DPacket extends DeviceBoundPacket {
1757
980
  static COMMAND_ID = 86;
1758
981
  static COMMAND_EXTENDED_ID = 28;
1759
982
  constructor(vendor, fileName) {
1760
- const encodedName = encodeFixedText(fileName, "Filename", 23);
1761
- const payload = new Uint8Array(26);
1762
- payload.set([vendor, 0], 0);
1763
- payload.set(encodedName, 2);
1764
- super(payload);
983
+ super(filePayload(vendor, 0, fileName));
1765
984
  }
1766
985
  }
1767
986
 
@@ -1769,9 +988,7 @@ class FileClearUpH2DPacket extends DeviceBoundPacket {
1769
988
  static COMMAND_ID = 86;
1770
989
  static COMMAND_EXTENDED_ID = 30;
1771
990
  constructor(vendor) {
1772
- const payload = new Uint8Array(2);
1773
- payload.set([vendor, 0], 0);
1774
- super(payload);
991
+ super(Uint8Array.of(vendor, 0));
1775
992
  }
1776
993
  }
1777
994
 
@@ -1779,9 +996,7 @@ class FileFormatH2DPacket extends DeviceBoundPacket {
1779
996
  static COMMAND_ID = 86;
1780
997
  static COMMAND_EXTENDED_ID = 31;
1781
998
  constructor() {
1782
- const payload = new Uint8Array(4);
1783
- payload.set([68, 67, 66, 65], 0);
1784
- super(payload);
999
+ super(Uint8Array.of(68, 67, 66, 65));
1785
1000
  }
1786
1001
  }
1787
1002
 
@@ -1793,33 +1008,21 @@ class GetSystemFlagsH2DPacket extends DeviceBoundPacket {
1793
1008
  class GetDeviceStatusH2DPacket extends DeviceBoundPacket {
1794
1009
  static COMMAND_ID = 86;
1795
1010
  static COMMAND_EXTENDED_ID = 33;
1796
- constructor() {
1797
- super(undefined);
1798
- }
1799
1011
  }
1800
1012
 
1801
1013
  class GetSystemStatusH2DPacket extends DeviceBoundPacket {
1802
1014
  static COMMAND_ID = 86;
1803
1015
  static COMMAND_EXTENDED_ID = 34;
1804
- constructor() {
1805
- super(undefined);
1806
- }
1807
1016
  }
1808
1017
 
1809
1018
  class GetFdtStatusH2DPacket extends DeviceBoundPacket {
1810
1019
  static COMMAND_ID = 86;
1811
1020
  static COMMAND_EXTENDED_ID = 35;
1812
- constructor() {
1813
- super(undefined);
1814
- }
1815
1021
  }
1816
1022
 
1817
1023
  class GetLogCountH2DPacket extends DeviceBoundPacket {
1818
1024
  static COMMAND_ID = 86;
1819
1025
  static COMMAND_EXTENDED_ID = 36;
1820
- constructor() {
1821
- super(undefined);
1822
- }
1823
1026
  }
1824
1027
 
1825
1028
  class ReadLogPageH2DPacket extends DeviceBoundPacket {
@@ -1837,18 +1040,13 @@ class ReadLogPageH2DPacket extends DeviceBoundPacket {
1837
1040
  class GetRadioStatusH2DPacket extends DeviceBoundPacket {
1838
1041
  static COMMAND_ID = 86;
1839
1042
  static COMMAND_EXTENDED_ID = 38;
1840
- constructor() {
1841
- super(undefined);
1842
- }
1843
1043
  }
1844
1044
 
1845
1045
  class ScreenCaptureH2DPacket extends DeviceBoundPacket {
1846
1046
  static COMMAND_ID = 86;
1847
1047
  static COMMAND_EXTENDED_ID = 40;
1848
1048
  constructor(e) {
1849
- const payload = new Uint8Array(1);
1850
- payload[0] = e;
1851
- super(payload);
1049
+ super(Uint8Array.of(e));
1852
1050
  }
1853
1051
  }
1854
1052
 
@@ -1869,10 +1067,7 @@ class SelectDashH2DPacket extends DeviceBoundPacket {
1869
1067
  static COMMAND_ID = 86;
1870
1068
  static COMMAND_EXTENDED_ID = 43;
1871
1069
  constructor(screen, port) {
1872
- const payload = new Uint8Array(2);
1873
- payload[0] = screen;
1874
- payload[1] = port;
1875
- super(payload);
1070
+ super(Uint8Array.of(screen, port));
1876
1071
  }
1877
1072
  }
1878
1073
 
@@ -1880,9 +1075,8 @@ class ReadKeyValueH2DPacket extends DeviceBoundPacket {
1880
1075
  static COMMAND_ID = 86;
1881
1076
  static COMMAND_EXTENDED_ID = 46;
1882
1077
  constructor(key) {
1883
- const encodedKey = encodeFixedText(key, "Key", 31);
1884
1078
  const payload = new Uint8Array(32);
1885
- payload.set(encodedKey, 0);
1079
+ payload.set(encodeFixedText(key, "Key", 31), 0);
1886
1080
  super(payload);
1887
1081
  }
1888
1082
  }
@@ -1892,7 +1086,7 @@ class WriteKeyValueH2DPacket extends DeviceBoundPacket {
1892
1086
  static COMMAND_EXTENDED_ID = 47;
1893
1087
  constructor(key, value) {
1894
1088
  const strk = encodeFixedText(key, "Key", 31);
1895
- const strv = textEncoder.encode(value);
1089
+ const strv = textEncoder2.encode(value);
1896
1090
  if (strk.byteLength + strv.byteLength + 20 > 32767) {
1897
1091
  throw new RangeError("Key and value are too large for a protocol packet");
1898
1092
  }
@@ -1906,17 +1100,11 @@ class WriteKeyValueH2DPacket extends DeviceBoundPacket {
1906
1100
  class GetSlot1to4InfoH2DPacket extends DeviceBoundPacket {
1907
1101
  static COMMAND_ID = 86;
1908
1102
  static COMMAND_EXTENDED_ID = 49;
1909
- constructor() {
1910
- super(undefined);
1911
- }
1912
1103
  }
1913
1104
 
1914
1105
  class GetSlot5to8InfoH2DPacket extends DeviceBoundPacket {
1915
1106
  static COMMAND_ID = 86;
1916
1107
  static COMMAND_EXTENDED_ID = 50;
1917
- constructor() {
1918
- super(undefined);
1919
- }
1920
1108
  }
1921
1109
 
1922
1110
  class FactoryStatusH2DPacket extends DeviceBoundPacket {
@@ -1928,9 +1116,7 @@ class FactoryEnableH2DPacket extends DeviceBoundPacket {
1928
1116
  static COMMAND_ID = 86;
1929
1117
  static COMMAND_EXTENDED_ID = 255;
1930
1118
  constructor() {
1931
- const payload = new Uint8Array(4);
1932
- payload.set([77, 76, 75, 74], 0);
1933
- super(payload);
1119
+ super(Uint8Array.of(77, 76, 75, 74));
1934
1120
  }
1935
1121
  }
1936
1122
 
@@ -1972,8 +1158,7 @@ class MatchModeReplyD2HPacket extends HostBoundPacket {
1972
1158
  modebit;
1973
1159
  constructor(data) {
1974
1160
  super(data);
1975
- const dataView = PacketView.fromPacket(this);
1976
- this.modebit = dataView.nextUint8();
1161
+ this.modebit = PacketView.fromPacket(this).nextUint8();
1977
1162
  }
1978
1163
  }
1979
1164
 
@@ -1999,31 +1184,27 @@ class MatchStatusReplyD2HPacket extends HostBoundPacket {
1999
1184
  rxSignalQuality;
2000
1185
  constructor(data) {
2001
1186
  super(data);
2002
- const dataView = PacketView.fromPacket(this);
1187
+ const view = PacketView.fromPacket(this);
2003
1188
  const n = this.ackIndex;
2004
- this.rssi = dataView.nextInt8();
2005
- this.systemStatusBits = dataView.nextUint16();
2006
- this.radioStatusBits = dataView.nextUint16();
2007
- this.fieldStatusBits = dataView.nextUint8();
2008
- this.matchClock = dataView.nextUint8();
2009
- this.brainBatteryPercent = dataView.nextUint8();
2010
- this.controllerBatteryPercent = dataView.nextUint8();
2011
- this.partnerControllerBatteryPercent = dataView.nextUint8();
2012
- this.pad = dataView.nextUint8();
2013
- this.buttons = dataView.nextUint16();
2014
- this.activeProgram = dataView.nextUint8();
2015
- this.radioType = dataView.nextUint8();
2016
- this.radioChannel = dataView.nextUint8();
2017
- this.radioSlot = dataView.nextUint8();
2018
- this.robotName = dataView.nextNTBS(10);
2019
- this.controllerFlags = dataView.getUint8(n + 28);
2020
- this.rxSignalQuality = dataView.getUint8(n + 29);
2021
- let rawStr = new TextDecoder("UTF-8").decode(data.slice(n + 18, n + this.payloadSize + 28));
2022
- const endIdx = rawStr.indexOf("\x00");
2023
- if (endIdx > -1) {
2024
- rawStr = rawStr.substr(0, endIdx);
2025
- }
2026
- this.robotName = rawStr;
1189
+ this.rssi = view.nextInt8();
1190
+ this.systemStatusBits = view.nextUint16();
1191
+ this.radioStatusBits = view.nextUint16();
1192
+ this.fieldStatusBits = view.nextUint8();
1193
+ this.matchClock = view.nextUint8();
1194
+ this.brainBatteryPercent = view.nextUint8();
1195
+ this.controllerBatteryPercent = view.nextUint8();
1196
+ this.partnerControllerBatteryPercent = view.nextUint8();
1197
+ this.pad = view.nextUint8();
1198
+ this.buttons = view.nextUint16();
1199
+ this.activeProgram = view.nextUint8();
1200
+ this.radioType = view.nextUint8();
1201
+ this.radioChannel = view.nextUint8();
1202
+ this.radioSlot = view.nextUint8();
1203
+ this.controllerFlags = view.getUint8(n + 28);
1204
+ this.rxSignalQuality = view.getUint8(n + 29);
1205
+ const raw = new TextDecoder("UTF-8").decode(this.data.slice(n + 18, n + this.payloadSize + 28));
1206
+ const end = raw.indexOf("\x00");
1207
+ this.robotName = end > -1 ? raw.slice(0, end) : raw;
2027
1208
  }
2028
1209
  }
2029
1210
 
@@ -2040,10 +1221,10 @@ class InitFileTransferReplyD2HPacket extends HostBoundPacket {
2040
1221
  crc32;
2041
1222
  constructor(data) {
2042
1223
  super(data);
2043
- const dataView = PacketView.fromPacket(this);
2044
- this.windowSize = dataView.nextUint16();
2045
- this.fileSize = dataView.nextUint32();
2046
- this.crc32 = dataView.nextUint32();
1224
+ const view = PacketView.fromPacket(this);
1225
+ this.windowSize = view.nextUint16();
1226
+ this.fileSize = view.nextUint32();
1227
+ this.crc32 = view.nextUint32();
2047
1228
  }
2048
1229
  }
2049
1230
 
@@ -2065,13 +1246,10 @@ class ReadFileReplyD2HPacket extends HostBoundPacket {
2065
1246
  buf;
2066
1247
  constructor(data) {
2067
1248
  super(data);
2068
- const dataView = PacketView.fromPacket(this);
2069
- this.addr = dataView.nextUint32();
1249
+ const view = PacketView.fromPacket(this);
1250
+ this.addr = view.nextUint32();
2070
1251
  this.length = this.payloadSize - 8;
2071
- this.buf = this.data.slice(dataView.position, dataView.position + this.length).buffer;
2072
- }
2073
- static isValidPacket(data, n) {
2074
- return super.isValidPacket(data, n);
1252
+ this.buf = this.data.subarray(view.position, view.position + this.length);
2075
1253
  }
2076
1254
  }
2077
1255
 
@@ -2086,8 +1264,7 @@ class GetDirectoryFileCountReplyD2HPacket extends HostBoundPacket {
2086
1264
  count;
2087
1265
  constructor(data) {
2088
1266
  super(data);
2089
- const dataView = PacketView.fromPacket(this);
2090
- this.count = dataView.nextUint16();
1267
+ this.count = PacketView.fromPacket(this).nextUint16();
2091
1268
  }
2092
1269
  }
2093
1270
 
@@ -2097,19 +1274,19 @@ class GetDirectoryEntryReplyD2HPacket extends HostBoundPacket {
2097
1274
  file;
2098
1275
  constructor(data) {
2099
1276
  super(data);
2100
- const dataView = PacketView.fromPacket(this);
2101
- if (this.payloadSize > 4) {
2102
- this.file = {
2103
- index: dataView.nextUint8(),
2104
- size: dataView.nextUint32(),
2105
- loadAddress: dataView.nextUint32(),
2106
- crc32: dataView.nextUint32(),
2107
- type: dataView.nextString(4),
2108
- timestamp: dataView.nextUint32() + PacketEncoder.J2000_EPOCH,
2109
- version: dataView.nextVersion(),
2110
- filename: dataView.nextNTBS(32)
2111
- };
2112
- }
1277
+ if (this.payloadSize <= 4)
1278
+ return;
1279
+ const view = PacketView.fromPacket(this);
1280
+ this.file = {
1281
+ index: view.nextUint8(),
1282
+ size: view.nextUint32(),
1283
+ loadAddress: view.nextUint32(),
1284
+ crc32: view.nextUint32(),
1285
+ type: view.nextString(4),
1286
+ timestamp: view.nextUint32() + PacketEncoder.J2000_EPOCH,
1287
+ version: view.nextVersion(),
1288
+ filename: view.nextNTBS(32)
1289
+ };
2113
1290
  }
2114
1291
  }
2115
1292
 
@@ -2124,18 +1301,18 @@ class GetFileMetadataReplyD2HPacket extends HostBoundPacket {
2124
1301
  file;
2125
1302
  constructor(data) {
2126
1303
  super(data);
2127
- const dataView = PacketView.fromPacket(this);
2128
- dataView.nextUint8();
2129
- if (this.payloadSize > 4) {
2130
- this.file = {
2131
- size: dataView.nextUint32(),
2132
- loadAddress: dataView.nextUint32(),
2133
- crc32: dataView.nextUint32(),
2134
- type: dataView.nextString(4),
2135
- timestamp: dataView.nextUint32() + PacketEncoder.J2000_EPOCH,
2136
- version: dataView.nextVersion()
2137
- };
2138
- }
1304
+ if (this.payloadSize <= 4)
1305
+ return;
1306
+ const view = PacketView.fromPacket(this);
1307
+ view.nextUint8();
1308
+ this.file = {
1309
+ size: view.nextUint32(),
1310
+ loadAddress: view.nextUint32(),
1311
+ crc32: view.nextUint32(),
1312
+ type: view.nextString(4),
1313
+ timestamp: view.nextUint32() + PacketEncoder.J2000_EPOCH,
1314
+ version: view.nextVersion()
1315
+ };
2139
1316
  }
2140
1317
  }
2141
1318
 
@@ -2151,9 +1328,9 @@ class GetProgramSlotInfoReplyD2HPacket extends HostBoundPacket {
2151
1328
  slot;
2152
1329
  constructor(data) {
2153
1330
  super(data);
2154
- const dataView = PacketView.fromPacket(this);
2155
- this.slot = dataView.nextUint8();
2156
- this.requestedSlot = dataView.nextUint8();
1331
+ const view = PacketView.fromPacket(this);
1332
+ this.slot = view.nextUint8();
1333
+ this.requestedSlot = view.nextUint8();
2157
1334
  }
2158
1335
  }
2159
1336
 
@@ -2171,40 +1348,32 @@ class GetSystemFlagsReplyD2HPacket extends HostBoundPacket {
2171
1348
  static COMMAND_ID = 86;
2172
1349
  static COMMAND_EXTENDED_ID = 32;
2173
1350
  flags;
2174
- radioSearching;
1351
+ radioSearching = false;
2175
1352
  radioQuality;
2176
1353
  controllerBatteryPercent;
2177
1354
  partnerControllerBatteryPercent;
2178
1355
  battery;
2179
- currentProgram;
1356
+ currentProgram = 0;
2180
1357
  constructor(data) {
2181
1358
  super(data);
2182
- const dataView = PacketView.fromPacket(this);
2183
- this.radioSearching = false;
2184
- this.currentProgram = 0;
2185
- this.flags = dataView.nextUint32();
1359
+ const view = PacketView.fromPacket(this);
1360
+ this.flags = view.nextUint32();
2186
1361
  const hasPartner = (8192 & this.flags) !== 0;
2187
1362
  const hasRadio = (1536 & this.flags) === 1536;
2188
- const byte1 = dataView.nextUint8();
2189
- const byte2 = dataView.nextUint8();
1363
+ const byte1 = view.nextUint8();
1364
+ const byte2 = view.nextUint8();
2190
1365
  if (this.payloadSize === 11) {
2191
- this.battery = 8 * (byte1 & 15);
2192
- if ((this.flags & 256) !== 0 || hasRadio)
2193
- this.controllerBatteryPercent = 8 * (byte1 >> 4 & 15);
1366
+ this.battery = clamp100(8 * (byte1 & 15));
1367
+ if ((this.flags & 256) !== 0 || hasRadio) {
1368
+ this.controllerBatteryPercent = clamp100(8 * (byte1 >> 4 & 15));
1369
+ }
2194
1370
  if (hasRadio)
2195
- this.radioQuality = 8 * (byte2 & 15);
1371
+ this.radioQuality = clamp100(8 * (byte2 & 15));
2196
1372
  this.radioSearching = (this.flags & 1536) === 512;
2197
- if (hasPartner)
2198
- this.partnerControllerBatteryPercent = 8 * (byte2 >> 4 & 15);
2199
- this.currentProgram = dataView.nextUint8();
2200
- if (this.battery != null && this.battery > 100)
2201
- this.battery = 100;
2202
- if (this.controllerBatteryPercent != null && this.controllerBatteryPercent > 100)
2203
- this.controllerBatteryPercent = 100;
2204
- if (this.radioQuality != null && this.radioQuality > 100)
2205
- this.radioQuality = 100;
2206
- if (this.partnerControllerBatteryPercent != null && this.partnerControllerBatteryPercent > 100)
2207
- this.partnerControllerBatteryPercent = 100;
1373
+ if (hasPartner) {
1374
+ this.partnerControllerBatteryPercent = clamp100(8 * (byte2 >> 4 & 15));
1375
+ }
1376
+ this.currentProgram = view.nextUint8();
2208
1377
  }
2209
1378
  }
2210
1379
  }
@@ -2216,17 +1385,17 @@ class GetDeviceStatusReplyD2HPacket extends HostBoundPacket {
2216
1385
  devices;
2217
1386
  constructor(data) {
2218
1387
  super(data);
2219
- const dataView = PacketView.fromPacket(this);
2220
- this.count = dataView.nextUint8();
1388
+ const view = PacketView.fromPacket(this);
1389
+ this.count = view.nextUint8();
2221
1390
  this.devices = [];
2222
1391
  for (let i = 0;i < this.count; i++) {
2223
1392
  this.devices.push({
2224
- port: dataView.nextUint8(),
2225
- type: dataView.nextUint8(),
2226
- status: dataView.nextUint8(),
2227
- betaversion: dataView.nextUint8(),
2228
- version: dataView.nextUint16(),
2229
- bootversion: dataView.nextUint16()
1393
+ port: view.nextUint8(),
1394
+ type: view.nextUint8(),
1395
+ status: view.nextUint8(),
1396
+ betaversion: view.nextUint8(),
1397
+ version: view.nextUint16(),
1398
+ bootversion: view.nextUint16()
2230
1399
  });
2231
1400
  }
2232
1401
  }
@@ -2238,48 +1407,42 @@ class GetSystemStatusReplyD2HPacket extends HostBoundPacket {
2238
1407
  systemVersion;
2239
1408
  cpu0Version;
2240
1409
  cpu1Version;
2241
- nxpVersion;
1410
+ nxpVersion = VexFirmwareVersion.allZero();
2242
1411
  touchVersion;
2243
- uniqueId;
2244
- sysflags;
2245
- eventBrain;
2246
- romBootloaderActive;
2247
- ramBootloaderActive;
2248
- goldenVersion;
1412
+ uniqueId = 1234;
1413
+ sysflags = [0, 0, 0, 0, 0, 0, 0];
1414
+ eventBrain = false;
1415
+ romBootloaderActive = false;
1416
+ ramBootloaderActive = false;
1417
+ goldenVersion = VexFirmwareVersion.allZero();
2249
1418
  constructor(data) {
2250
1419
  super(data);
2251
- const dataView = PacketView.fromPacket(this);
2252
- dataView.nextUint8();
2253
- this.systemVersion = dataView.nextVersion();
2254
- this.cpu0Version = dataView.nextVersion();
2255
- this.cpu1Version = dataView.nextVersion();
2256
- this.touchVersion = dataView.nextVersion(true);
2257
- this.uniqueId = 1234;
2258
- this.sysflags = [0, 0, 0, 0, 0, 0, 0];
2259
- this.goldenVersion = VexFirmwareVersion.allZero();
2260
- this.nxpVersion = VexFirmwareVersion.allZero();
2261
- this.eventBrain = false;
2262
- this.romBootloaderActive = false;
2263
- this.ramBootloaderActive = false;
1420
+ const view = PacketView.fromPacket(this);
1421
+ view.nextUint8();
1422
+ this.systemVersion = view.nextVersion();
1423
+ this.cpu0Version = view.nextVersion();
1424
+ this.cpu1Version = view.nextVersion();
1425
+ this.touchVersion = view.nextVersion(true);
2264
1426
  if (this.payloadSize > 25) {
2265
- this.uniqueId = dataView.nextUint32();
1427
+ this.uniqueId = view.nextUint32();
2266
1428
  this.sysflags = [
2267
- dataView.nextUint8(),
2268
- dataView.nextUint8(),
2269
- dataView.nextUint8(),
2270
- dataView.nextUint8(),
2271
- dataView.nextUint8(),
1429
+ view.nextUint8(),
1430
+ view.nextUint8(),
1431
+ view.nextUint8(),
1432
+ view.nextUint8(),
1433
+ view.nextUint8(),
2272
1434
  0,
2273
- dataView.nextUint8()
1435
+ view.nextUint8()
2274
1436
  ];
2275
- this.eventBrain = (1 & this.sysflags[6]) !== 0;
2276
- this.romBootloaderActive = (2 & this.sysflags[6]) !== 0;
2277
- this.ramBootloaderActive = (4 & this.sysflags[6]) !== 0;
2278
- dataView.nextUint16();
2279
- this.goldenVersion = dataView.nextVersion();
1437
+ const flags6 = this.sysflags[6];
1438
+ this.eventBrain = (1 & flags6) !== 0;
1439
+ this.romBootloaderActive = (2 & flags6) !== 0;
1440
+ this.ramBootloaderActive = (4 & flags6) !== 0;
1441
+ view.nextUint16();
1442
+ this.goldenVersion = view.nextVersion();
2280
1443
  }
2281
1444
  if (this.payloadSize > 37) {
2282
- this.nxpVersion = dataView.nextVersion();
1445
+ this.nxpVersion = view.nextVersion();
2283
1446
  }
2284
1447
  }
2285
1448
  }
@@ -2291,17 +1454,17 @@ class GetFdtStatusReplyD2HPacket extends HostBoundPacket {
2291
1454
  status;
2292
1455
  constructor(data) {
2293
1456
  super(data);
2294
- const dataView = PacketView.fromPacket(this);
2295
- this.count = dataView.nextUint8();
1457
+ const view = PacketView.fromPacket(this);
1458
+ this.count = view.nextUint8();
2296
1459
  this.status = [];
2297
1460
  for (let i = 0;i < this.count; i++) {
2298
1461
  this.status.push({
2299
- index: dataView.nextUint8(),
2300
- type: dataView.nextUint8(),
2301
- status: dataView.nextUint8(),
2302
- betaversion: dataView.nextUint8(),
2303
- version: dataView.nextUint16(),
2304
- bootversion: dataView.nextUint16()
1462
+ index: view.nextUint8(),
1463
+ type: view.nextUint8(),
1464
+ status: view.nextUint8(),
1465
+ betaversion: view.nextUint8(),
1466
+ version: view.nextUint16(),
1467
+ bootversion: view.nextUint16()
2305
1468
  });
2306
1469
  }
2307
1470
  }
@@ -2313,9 +1476,9 @@ class GetLogCountReplyD2HPacket extends HostBoundPacket {
2313
1476
  count;
2314
1477
  constructor(data) {
2315
1478
  super(data);
2316
- const dataView = PacketView.fromPacket(this);
2317
- dataView.nextUint8();
2318
- this.count = dataView.nextUint32();
1479
+ const view = PacketView.fromPacket(this);
1480
+ view.nextUint8();
1481
+ this.count = view.nextUint32();
2319
1482
  }
2320
1483
  }
2321
1484
 
@@ -2327,20 +1490,19 @@ class ReadLogPageReplyD2HPacket extends HostBoundPacket {
2327
1490
  entries;
2328
1491
  constructor(data) {
2329
1492
  super(data);
2330
- const dataView = PacketView.fromPacket(this);
2331
- const n = this.ackIndex;
2332
- const size = dataView.nextUint8();
2333
- this.offset = dataView.nextUint32();
2334
- this.count = dataView.nextUint16();
1493
+ const view = PacketView.fromPacket(this);
1494
+ const size = view.nextUint8();
1495
+ this.offset = view.nextUint32();
1496
+ this.count = view.nextUint16();
2335
1497
  this.entries = [];
2336
- let j = n + 8;
1498
+ let j = this.ackIndex + 8;
2337
1499
  for (let i = 0;i < this.count; i++) {
2338
1500
  this.entries.push({
2339
- code: dataView.getUint8(j),
2340
- type: dataView.getUint8(j + 1),
2341
- desc: dataView.getUint8(j + 2),
2342
- spare: dataView.getUint8(j + 3),
2343
- time: dataView.getUint32(j + 4, true)
1501
+ code: view.getUint8(j),
1502
+ type: view.getUint8(j + 1),
1503
+ desc: view.getUint8(j + 2),
1504
+ spare: view.getUint8(j + 3),
1505
+ time: view.getUint32(j + 4, true)
2344
1506
  });
2345
1507
  j += size;
2346
1508
  }
@@ -2357,13 +1519,12 @@ class GetRadioStatusReplyD2HPacket extends HostBoundPacket {
2357
1519
  timeslot;
2358
1520
  constructor(data) {
2359
1521
  super(data);
2360
- const dataView = PacketView.fromPacket(this);
2361
- const n = this.ackIndex;
2362
- this.device = dataView.nextUint8();
2363
- this.quality = dataView.nextUint16();
2364
- this.strength = dataView.nextInt16();
2365
- this.channel = this.data[n + 6];
2366
- this.timeslot = this.data[n + 7];
1522
+ const view = PacketView.fromPacket(this);
1523
+ this.device = view.nextUint8();
1524
+ this.quality = view.nextUint16();
1525
+ this.strength = view.nextInt16();
1526
+ this.channel = this.data[this.ackIndex + 6];
1527
+ this.timeslot = this.data[this.ackIndex + 7];
2367
1528
  }
2368
1529
  }
2369
1530
 
@@ -2388,8 +1549,7 @@ class ReadKeyValueReplyD2HPacket extends HostBoundPacket {
2388
1549
  value;
2389
1550
  constructor(data) {
2390
1551
  super(data);
2391
- const dataView = PacketView.fromPacket(this);
2392
- this.value = dataView.nextVarNTBS(255);
1552
+ this.value = PacketView.fromPacket(this).nextVarNTBS(255);
2393
1553
  }
2394
1554
  }
2395
1555
 
@@ -2405,20 +1565,18 @@ class GetSlot1to4InfoReplyD2HPacket extends HostBoundPacket {
2405
1565
  slots;
2406
1566
  constructor(data, start = 1) {
2407
1567
  super(data);
2408
- const dataView = PacketView.fromPacket(this);
2409
- this.slotFlags = dataView.nextUint8();
1568
+ const view = PacketView.fromPacket(this);
1569
+ this.slotFlags = view.nextUint8();
2410
1570
  this.slots = [];
2411
1571
  for (let i = 0;i < 4; i++) {
2412
- const hasData = (this.slotFlags & 2 ** (start - 1 + i)) !== 0;
2413
- if (!hasData)
1572
+ if ((this.slotFlags & 1 << start - 1 + i) === 0)
2414
1573
  continue;
2415
- const iconNum = dataView.nextUint16();
2416
- const nameLen = dataView.nextUint8();
2417
- const name = dataView.nextString(nameLen);
1574
+ const icon = view.nextUint16();
1575
+ const nameLen = view.nextUint8();
2418
1576
  this.slots.push({
2419
1577
  slot: start + i,
2420
- icon: iconNum,
2421
- name
1578
+ icon,
1579
+ name: view.nextString(nameLen)
2422
1580
  });
2423
1581
  }
2424
1582
  }
@@ -2440,9 +1598,9 @@ class FactoryStatusReplyD2HPacket extends HostBoundPacket {
2440
1598
  percent;
2441
1599
  constructor(data) {
2442
1600
  super(data);
2443
- const dataView = PacketView.fromPacket(this);
2444
- this.status = dataView.nextUint8();
2445
- this.percent = dataView.nextUint8();
1601
+ const view = PacketView.fromPacket(this);
1602
+ this.status = view.nextUint8();
1603
+ this.percent = view.nextUint8();
2446
1604
  }
2447
1605
  }
2448
1606
 
@@ -2451,120 +1609,35 @@ class FactoryEnableReplyD2HPacket extends HostBoundPacket {
2451
1609
  static COMMAND_EXTENDED_ID = 255;
2452
1610
  }
2453
1611
 
2454
- // src/VexPacketEncoder.ts
2455
- var textEncoder2 = new TextEncoder;
2456
- function encodeFixedText(value, field, maxBytes) {
2457
- const encoded = textEncoder2.encode(value);
2458
- if (encoded.byteLength > maxBytes) {
2459
- throw new RangeError(`${field} must be at most ${maxBytes} UTF-8 bytes`);
2460
- }
2461
- return encoded;
2462
- }
2463
-
2464
- class PacketEncoder {
2465
- static HEADERS_LENGTH = 4;
2466
- static HEADER_TO_DEVICE = [201, 54, 184, 71];
2467
- static HEADER_TO_HOST = [170, 85];
2468
- static J2000_EPOCH = 946684800;
2469
- vexVersion;
2470
- crcgen;
2471
- allPacketsTable = {};
2472
- static getInstance() {
2473
- if (Packet.ENCODER === undefined) {
2474
- Packet.ENCODER = new PacketEncoder;
2475
- }
2476
- return Packet.ENCODER;
2477
- }
2478
- constructor() {
2479
- this.vexVersion = 0;
2480
- this.crcgen = new CrcGenerator;
2481
- Object.values(exports_VexPacketModels).forEach((packet) => {
2482
- if (typeof packet === "function" && packet.prototype instanceof HostBoundPacket) {
2483
- const packetType = packet;
2484
- this.allPacketsTable[packetType.COMMAND_ID + " " + packetType.COMMAND_EXTENDED_ID] = packetType;
2485
- }
2486
- });
2487
- }
2488
- createHeader(buf) {
2489
- if (buf === undefined || buf.byteLength < PacketEncoder.HEADERS_LENGTH) {
2490
- buf = new ArrayBuffer(PacketEncoder.HEADERS_LENGTH);
2491
- }
2492
- const h = new Uint8Array(buf);
2493
- h.set(PacketEncoder.HEADER_TO_DEVICE);
2494
- return h;
2495
- }
2496
- cdcCommand(cmd) {
2497
- const buf = new ArrayBuffer(PacketEncoder.HEADERS_LENGTH + 1);
2498
- const h = this.createHeader(buf);
2499
- h.set([cmd], PacketEncoder.HEADERS_LENGTH);
2500
- return h;
2501
- }
2502
- cdcCommandWithData(cmd, data) {
2503
- const buf = new ArrayBuffer(PacketEncoder.HEADERS_LENGTH + 2 + data.length);
2504
- const h = this.createHeader(buf);
2505
- h.set([cmd, data.length], PacketEncoder.HEADERS_LENGTH);
2506
- h.set(data, PacketEncoder.HEADERS_LENGTH + 2);
2507
- return h;
2508
- }
2509
- cdc2Command(cmd, ext) {
2510
- const buf = new ArrayBuffer(PacketEncoder.HEADERS_LENGTH + 5);
2511
- const h = this.createHeader(buf);
2512
- h.set([cmd, ext, 0], PacketEncoder.HEADERS_LENGTH);
2513
- const crc = this.crcgen.crc16(h.subarray(0, buf.byteLength - 2), 0) >>> 0;
2514
- h.set([crc >>> 8, crc & 255], buf.byteLength - 2);
2515
- return h;
2516
- }
2517
- cdc2CommandBufferLength(data) {
2518
- let length = PacketEncoder.HEADERS_LENGTH + data.length + 3 + 2;
2519
- if (data.length > 127)
2520
- length += 1;
2521
- return length;
2522
- }
2523
- cdc2CommandWithData(cmd, ext, data) {
2524
- const buf = new ArrayBuffer(this.cdc2CommandBufferLength(data));
2525
- const h = this.createHeader(buf);
2526
- if (data.length < 128) {
2527
- h.set([cmd, ext, data.length], PacketEncoder.HEADERS_LENGTH);
2528
- h.set(data, PacketEncoder.HEADERS_LENGTH + 3);
2529
- } else {
2530
- const lengthMsb = (data.length >>> 8 | 128) >>> 0;
2531
- const lengthLsb = (data.length & 255) >>> 0;
2532
- h.set([cmd, ext, lengthMsb, lengthLsb], PacketEncoder.HEADERS_LENGTH);
2533
- h.set(data, PacketEncoder.HEADERS_LENGTH + 4);
2534
- }
2535
- const crc = this.crcgen.crc16(h.subarray(0, buf.byteLength - 2), 0);
2536
- h.set([crc >>> 8, crc & 255], buf.byteLength - 2);
2537
- return h;
2538
- }
2539
- validateHeader(data) {
2540
- return !(data[0] !== PacketEncoder.HEADER_TO_HOST[0] || data[1] !== PacketEncoder.HEADER_TO_HOST[1]);
2541
- }
2542
- validateMessageCdc(data) {
2543
- const message = data.subarray(0, data.byteLength - 2);
2544
- const lastTwoBytes = (data[data.byteLength - 2] << 8) + data[data.byteLength - 1];
2545
- return this.crcgen.crc16(message, 0) === lastTwoBytes;
1612
+ // src/VexScreenCapture.ts
1613
+ var SCREEN_CAPTURE_HEIGHT = 272;
1614
+ var SCREEN_CAPTURE_WIDTH = 480;
1615
+ var SCREEN_CAPTURE_CHANNELS = 3;
1616
+ var SCREEN_CAPTURE_MESSAGE_WIDTH = 512;
1617
+ var SCREEN_CAPTURE_MESSAGE_CHANNELS = 4;
1618
+ var SCREEN_CAPTURE_FRAMEBUFFER_SIZE = SCREEN_CAPTURE_MESSAGE_WIDTH * SCREEN_CAPTURE_HEIGHT * SCREEN_CAPTURE_MESSAGE_CHANNELS;
1619
+ function convertScreenCapture(framebuffer) {
1620
+ if (framebuffer.length !== SCREEN_CAPTURE_FRAMEBUFFER_SIZE) {
1621
+ throw new Error(`bad screen capture framebuffer size: ${framebuffer.length}; expected ${SCREEN_CAPTURE_FRAMEBUFFER_SIZE}`);
2546
1622
  }
2547
- getPayloadSize(data) {
2548
- let t = 0;
2549
- let a = data[3];
2550
- if ((128 & a) !== 0) {
2551
- t = 127 & a;
2552
- a = data[4];
1623
+ const pixels = new Uint8Array(SCREEN_CAPTURE_WIDTH * SCREEN_CAPTURE_HEIGHT * SCREEN_CAPTURE_CHANNELS);
1624
+ let source = 0;
1625
+ let target = 0;
1626
+ for (let row = 0;row < SCREEN_CAPTURE_HEIGHT; row++) {
1627
+ for (let column = 0;column < SCREEN_CAPTURE_WIDTH; column++) {
1628
+ pixels[target] = framebuffer[source + 2];
1629
+ pixels[target + 1] = framebuffer[source + 1];
1630
+ pixels[target + 2] = framebuffer[source];
1631
+ source += SCREEN_CAPTURE_MESSAGE_CHANNELS;
1632
+ target += SCREEN_CAPTURE_CHANNELS;
2553
1633
  }
2554
- return (t << 8) + a;
2555
- }
2556
- getHostHeaderLength(data) {
2557
- return (data[3] & 128) === 0 ? 4 : 5;
1634
+ source += (SCREEN_CAPTURE_MESSAGE_WIDTH - SCREEN_CAPTURE_WIDTH) * SCREEN_CAPTURE_MESSAGE_CHANNELS;
2558
1635
  }
1636
+ return pixels;
2559
1637
  }
2560
1638
 
2561
1639
  // src/VexConnection.ts
2562
1640
  var thePacketEncoder = PacketEncoder.getInstance();
2563
- var SCREEN_CAPTURE_HEIGHT = 272;
2564
- var SCREEN_CAPTURE_WIDTH = 480;
2565
- var SCREEN_CAPTURE_CHANNELS = 3;
2566
- var SCREEN_CAPTURE_MESSAGE_WIDTH = 512;
2567
- var SCREEN_CAPTURE_MESSAGE_CHANNELS = 4;
2568
1641
 
2569
1642
  class VexSerialConnection extends VexEventTarget {
2570
1643
  filters = [{ usbVendorId: 10376 }];
@@ -2572,12 +1645,31 @@ class VexSerialConnection extends VexEventTarget {
2572
1645
  reader;
2573
1646
  port;
2574
1647
  serial;
2575
- callbacksQueue = [];
1648
+ pendingCallbacks = new Map;
1649
+ rawCallbacks = {
1650
+ head: undefined,
1651
+ tail: undefined
1652
+ };
1653
+ pendingCommandTails = new Map;
2576
1654
  _onPortDisconnect = null;
2577
1655
  _closePromise = null;
1656
+ _openPromise = null;
1657
+ _isClosing = false;
2578
1658
  _wasConnected = false;
2579
1659
  fileTransferTail = Promise.resolve();
2580
1660
  fileTransferDepth = 0;
1661
+ get callbacksQueue() {
1662
+ const callbacks = [];
1663
+ for (const queue of this.pendingCallbacks.values()) {
1664
+ for (let callback = queue.head;callback; callback = callback.next) {
1665
+ callbacks.push(callback);
1666
+ }
1667
+ }
1668
+ for (let callback = this.rawCallbacks.head;callback; callback = callback.next) {
1669
+ callbacks.push(callback);
1670
+ }
1671
+ return callbacks;
1672
+ }
2581
1673
  get isConnected() {
2582
1674
  return this.port !== undefined && this.reader !== undefined && this.writer !== undefined;
2583
1675
  }
@@ -2588,25 +1680,46 @@ class VexSerialConnection extends VexEventTarget {
2588
1680
  super();
2589
1681
  this.serial = serial;
2590
1682
  }
1683
+ reportWarning(message, details) {
1684
+ this.emitSafely("warning", {
1685
+ message,
1686
+ details
1687
+ });
1688
+ }
1689
+ emitSafely(eventName, data) {
1690
+ try {
1691
+ this.emit(eventName, data);
1692
+ } catch {}
1693
+ }
2591
1694
  async close() {
2592
1695
  if (this._closePromise)
2593
1696
  return this._closePromise;
2594
- if (!this._hasOpenResources())
2595
- return;
2596
- this._closePromise = this._doClose();
1697
+ this._isClosing = true;
1698
+ const closing = this._closeAfterOpen();
1699
+ this._closePromise = closing;
2597
1700
  try {
2598
- await this._closePromise;
1701
+ await closing;
2599
1702
  } finally {
2600
- this._closePromise = null;
1703
+ if (this._closePromise === closing)
1704
+ this._closePromise = null;
1705
+ this._isClosing = false;
2601
1706
  }
2602
1707
  }
1708
+ async _closeAfterOpen() {
1709
+ const opening = this._openPromise;
1710
+ if (opening !== null)
1711
+ await opening;
1712
+ if (!this._hasOpenResources())
1713
+ return;
1714
+ await this._doClose();
1715
+ }
2603
1716
  _hasOpenResources() {
2604
- return this.port !== undefined || this.reader !== undefined || this.writer !== undefined || this._onPortDisconnect !== null || this.callbacksQueue.length > 0;
1717
+ return this.port !== undefined || this.reader !== undefined || this.writer !== undefined || this._onPortDisconnect !== null || this.hasPendingCallbacks();
2605
1718
  }
2606
1719
  async _doClose() {
2607
- for (const callback of this.callbacksQueue.splice(0)) {
1720
+ for (const callback of this.drainPendingCallbacks()) {
2608
1721
  clearTimeout(callback.timeout);
2609
- callback.callback(255 /* CDC2_NACK */);
1722
+ callback.callback(258 /* NOT_CONNECTED */);
2610
1723
  }
2611
1724
  const onDisconnect = this._onPortDisconnect;
2612
1725
  this._onPortDisconnect = null;
@@ -2650,38 +1763,48 @@ class VexSerialConnection extends VexEventTarget {
2650
1763
  try {
2651
1764
  await port.close();
2652
1765
  } catch (e) {
2653
- console.warn("Close port error.", e);
1766
+ this.reportWarning("failed to close the serial port", e);
2654
1767
  }
2655
1768
  }
2656
1769
  if (this._wasConnected) {
2657
1770
  this._wasConnected = false;
2658
- this.emit("disconnected", undefined);
1771
+ this.emitSafely("disconnected", undefined);
2659
1772
  }
2660
1773
  }
2661
1774
  open(use = 0, askUser = true) {
2662
- if (this.port !== undefined) {
2663
- return errAsyncVex(new VexIoError("Already connected."));
2664
- }
2665
- return new import_neverthrow.ResultAsync(this._open(use, askUser));
1775
+ if (this._openPromise !== null)
1776
+ return new import_neverthrow.ResultAsync(this._openPromise);
1777
+ const opening = this._open(use, askUser);
1778
+ this._openPromise = opening;
1779
+ opening.then(() => {
1780
+ if (this._openPromise === opening)
1781
+ this._openPromise = null;
1782
+ }, () => {
1783
+ if (this._openPromise === opening)
1784
+ this._openPromise = null;
1785
+ });
1786
+ return new import_neverthrow.ResultAsync(opening);
2666
1787
  }
2667
1788
  async _open(use, askUser) {
2668
- let port;
2669
- if (use !== undefined) {
2670
- const ports = (await this.serial.getPorts()).filter((p) => {
2671
- const info = p.getInfo();
2672
- return this.filters.find((f) => (f.usbVendorId === undefined || f.usbVendorId === info.usbVendorId) && (f.usbProductId === undefined || f.usbProductId === info.usbProductId));
2673
- }).filter((candidate) => candidate.readable === null);
2674
- port = ports[use];
1789
+ if (this._closePromise !== null)
1790
+ await this._closePromise;
1791
+ if (this.port !== undefined) {
1792
+ return import_neverthrow.err(new VexIoError("Already connected."));
2675
1793
  }
1794
+ const ports = (await this.serial.getPorts()).filter((p) => {
1795
+ const info = p.getInfo();
1796
+ return this.filters.find((f) => (f.usbVendorId === undefined || f.usbVendorId === info.usbVendorId) && (f.usbProductId === undefined || f.usbProductId === info.usbProductId));
1797
+ }).filter((candidate) => candidate.readable === null);
1798
+ let port = ports[use];
2676
1799
  if (port == null && askUser) {
2677
1800
  try {
2678
1801
  port = await this.serial.requestPort({ filters: this.filters });
2679
1802
  } catch {}
2680
1803
  }
2681
1804
  if (port == null)
2682
- return import_neverthrow.ok(undefined);
1805
+ return import_neverthrow.ok("no-port");
2683
1806
  if (port.readable != null)
2684
- return import_neverthrow.ok(false);
1807
+ return import_neverthrow.ok("busy");
2685
1808
  try {
2686
1809
  this.port = port;
2687
1810
  await port.open({ baudRate: 115200 });
@@ -2693,46 +1816,79 @@ class VexSerialConnection extends VexEventTarget {
2693
1816
  this.reader = this.port.readable.getReader();
2694
1817
  this.startReader();
2695
1818
  this._wasConnected = true;
2696
- this.emit("connected", undefined);
2697
- return import_neverthrow.ok(true);
2698
- } catch {
2699
- await this.close();
2700
- return import_neverthrow.ok(false);
1819
+ this.emitSafely("connected", undefined);
1820
+ return import_neverthrow.ok("opened");
1821
+ } catch (e) {
1822
+ await this._doClose();
1823
+ return import_neverthrow.err(toVexSerialError(e, "io"));
1824
+ }
1825
+ }
1826
+ async writeDataAsync(rawData, timeout = 1000) {
1827
+ if (rawData instanceof DeviceBoundPacket) {
1828
+ return this.serializeCommand(rawData.commandId, rawData.commandExtendedId, () => this.writeDataAsyncUnserialized(rawData, timeout));
1829
+ }
1830
+ return this.writeDataAsyncUnserialized(rawData, timeout);
1831
+ }
1832
+ async serializeCommand(commandId, commandExtendedId, operation) {
1833
+ const key = `${commandId}:${commandExtendedId ?? ""}`;
1834
+ const previous = this.pendingCommandTails.get(key) ?? Promise.resolve();
1835
+ let release = () => {};
1836
+ const current = new Promise((resolve) => {
1837
+ release = resolve;
1838
+ });
1839
+ this.pendingCommandTails.set(key, current);
1840
+ await previous;
1841
+ try {
1842
+ return await operation();
1843
+ } finally {
1844
+ release();
1845
+ if (this.pendingCommandTails.get(key) === current) {
1846
+ this.pendingCommandTails.delete(key);
1847
+ }
2701
1848
  }
2702
1849
  }
2703
- writeData(rawData, resolve, timeout = 1000) {
2704
- this.writeDataAsync(rawData, timeout).then(resolve);
2705
- }
2706
- async writeDataAsync(rawData, timeout = 1000) {
1850
+ async writeDataAsyncUnserialized(rawData, timeout) {
2707
1851
  return new Promise((resolve) => {
2708
- if (this.writer === undefined) {
2709
- resolve(255 /* CDC2_NACK */);
1852
+ if (this.writer === undefined || this._isClosing) {
1853
+ resolve(258 /* NOT_CONNECTED */);
2710
1854
  return;
2711
1855
  }
2712
1856
  const data = rawData instanceof DeviceBoundPacket ? rawData.data : rawData;
1857
+ const queue = rawData instanceof DeviceBoundPacket ? this.getPendingQueue(rawData.commandId, rawData.commandExtendedId, true) : this.rawCallbacks;
2713
1858
  const cb = {
1859
+ active: true,
2714
1860
  callback: resolve,
2715
1861
  timeout: setTimeout(() => {
2716
- const index = this.callbacksQueue.indexOf(cb);
2717
- if (index === -1)
1862
+ if (!this.removePendingCallback(cb))
2718
1863
  return;
2719
- this.callbacksQueue.splice(index, 1);
2720
1864
  cb.callback(256 /* TIMEOUT */);
2721
1865
  }, timeout),
1866
+ next: undefined,
1867
+ previous: undefined,
1868
+ queue,
2722
1869
  wantedCommandId: rawData instanceof DeviceBoundPacket ? rawData.commandId : undefined,
2723
1870
  wantedCommandExId: rawData instanceof DeviceBoundPacket ? rawData.commandExtendedId : undefined
2724
1871
  };
2725
- this.callbacksQueue.push(cb);
1872
+ this.enqueuePendingCallback(cb);
2726
1873
  this.writer.write(data).catch(() => {
2727
- const index = this.callbacksQueue.indexOf(cb);
2728
- if (index === -1)
1874
+ if (!this.removePendingCallback(cb))
2729
1875
  return;
2730
- this.callbacksQueue.splice(index, 1);
2731
1876
  clearTimeout(cb.timeout);
2732
1877
  resolve(257 /* WRITE_ERROR */);
2733
1878
  });
2734
1879
  });
2735
1880
  }
1881
+ request(packet, ReplyType, timeout = 1000) {
1882
+ return new import_neverthrow.ResultAsync((async () => {
1883
+ const result = await this.writeDataAsync(packet, timeout);
1884
+ if (result instanceof ReplyType)
1885
+ return import_neverthrow.ok(result);
1886
+ if (result === 258 /* NOT_CONNECTED */) {
1887
+ return import_neverthrow.err(new VexNotConnectedError);
1888
+ }
1889
+ return import_neverthrow.err(new VexProtocolError(expectedReplyMessage(packet, ReplyType, result), typeof result === "number" ? result : undefined));
1890
+ })());
1891
+ }
2736
1892
  async readData(cache, expectedSize) {
2737
1893
  if (this.reader == null)
2738
1894
  throw new Error("No reader");
@@ -2740,93 +1896,172 @@ class VexSerialConnection extends VexEventTarget {
2740
1896
  const { value: readData, done: isDone } = await this.reader.read();
2741
1897
  if (isDone)
2742
1898
  throw new Error("No data");
2743
- cache = binaryArrayJoin(cache, readData);
1899
+ cache.append(readData);
2744
1900
  }
2745
- return cache;
2746
1901
  }
2747
1902
  async startReader() {
2748
- let cache = new Uint8Array([]);
1903
+ const cache = new ReceiveBuffer;
2749
1904
  let sliceIdx = 0;
2750
1905
  for (;; )
2751
1906
  try {
2752
- cache = await this.readData(cache, 5);
1907
+ await this.readData(cache, 5);
2753
1908
  sliceIdx = 0;
2754
- while (!thePacketEncoder.validateHeader(cache)) {
2755
- const nextHeader = cache.findIndex((byte, index) => index > 0 && byte === PacketEncoder.HEADER_TO_HOST[0] && cache[index + 1] === PacketEncoder.HEADER_TO_HOST[1]);
1909
+ while (!thePacketEncoder.validateHeader(cache.bytes)) {
1910
+ const bytes = cache.bytes;
1911
+ const nextHeader = bytes.findIndex((byte, index) => index > 0 && byte === PacketEncoder.HEADER_TO_HOST[0] && bytes[index + 1] === PacketEncoder.HEADER_TO_HOST[1]);
2756
1912
  if (nextHeader >= 0) {
2757
- cache = cache.slice(nextHeader);
1913
+ cache.discard(nextHeader);
2758
1914
  } else {
2759
- cache = cache.slice(cache.at(-1) === PacketEncoder.HEADER_TO_HOST[0] ? -1 : cache.length);
1915
+ cache.discard(bytes.at(-1) === PacketEncoder.HEADER_TO_HOST[0] ? -1 : bytes.length);
2760
1916
  }
2761
- cache = await this.readData(cache, 5);
1917
+ await this.readData(cache, 5);
2762
1918
  }
2763
- const payloadExpectedSize = thePacketEncoder.getPayloadSize(cache);
2764
- const n = thePacketEncoder.getHostHeaderLength(cache);
1919
+ const payloadExpectedSize = thePacketEncoder.getPayloadSize(cache.bytes);
1920
+ const n = thePacketEncoder.getHostHeaderLength(cache.bytes);
2765
1921
  const totalSize = n + payloadExpectedSize;
2766
- cache = await this.readData(cache, totalSize);
1922
+ await this.readData(cache, totalSize);
2767
1923
  sliceIdx = totalSize;
2768
- const cmdId = cache[2];
1924
+ const packet = cache.copy(totalSize);
1925
+ const cmdId = packet[2];
2769
1926
  const hasExtId = cmdId === 88 || cmdId === 86;
2770
- const cmdExId = hasExtId ? cache[n] : undefined;
2771
- const ack = cache[n + 1];
1927
+ const cmdExId = hasExtId ? packet[n] : undefined;
1928
+ const ack = packet[n + 1];
2772
1929
  if (hasExtId) {
2773
- if (!thePacketEncoder.validateMessageCdc(cache))
2774
- throw new Error("Invalid message CDC");
2775
- }
2776
- let callbackInfo;
2777
- let wantedCmdId;
2778
- let wantedCmdExId;
2779
- let tryIdx = 0;
2780
- while ((callbackInfo = this.callbacksQueue[tryIdx++]) !== undefined) {
2781
- wantedCmdId = callbackInfo?.wantedCommandId;
2782
- wantedCmdExId = callbackInfo?.wantedCommandExId;
2783
- if (wantedCmdId !== undefined && wantedCmdId !== cmdId || wantedCmdExId !== undefined && wantedCmdExId !== cmdExId) {
1930
+ if (!thePacketEncoder.validateMessageCdc(packet)) {
1931
+ this.reportWarning("discarding a reply with an invalid CDC CRC", {
1932
+ commandId: cmdId,
1933
+ commandExtendedId: cmdExId,
1934
+ ack
1935
+ });
2784
1936
  continue;
2785
1937
  }
2786
- break;
2787
1938
  }
1939
+ const callbackInfo = this.shiftPendingCallback(cmdId, cmdExId) ?? this.shiftRawCallback();
2788
1940
  if (callbackInfo === undefined) {
2789
- console.warn("Unexpected command", cmdId, cmdExId, ack);
1941
+ this.reportWarning("received a reply with no matching request", {
1942
+ commandId: cmdId,
1943
+ commandExtendedId: cmdExId,
1944
+ ack
1945
+ });
2790
1946
  continue;
2791
1947
  }
2792
- const data = cache.slice(0, sliceIdx);
2793
- const PackageType = thePacketEncoder.allPacketsTable[wantedCmdId + " " + wantedCmdExId];
2794
- if (wantedCmdId === undefined && wantedCmdExId === undefined || PackageType === undefined) {
2795
- callbackInfo.callback(data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength));
1948
+ const wantedCmdId = callbackInfo.wantedCommandId;
1949
+ const wantedCmdExId = callbackInfo.wantedCommandExId;
1950
+ const PackageType = thePacketEncoder.getPacketType(wantedCmdId, wantedCmdExId);
1951
+ if (wantedCmdId === undefined || PackageType === undefined) {
1952
+ if (wantedCmdId !== undefined) {
1953
+ this.reportWarning("no packet class is registered for the wanted command", { commandId: wantedCmdId, commandExtendedId: wantedCmdExId });
1954
+ }
1955
+ callbackInfo.callback(packet.buffer);
2796
1956
  } else {
2797
- if (!hasExtId || PackageType.isValidPacket(data, n)) {
2798
- callbackInfo.callback(new PackageType(data));
1957
+ if (PackageType.isValidPacket(packet, n)) {
1958
+ callbackInfo.callback(new PackageType(packet));
2799
1959
  } else {
2800
- console.warn("ack", ack);
1960
+ this.reportWarning("reply failed packet validation; delivering its ack instead", { commandId: cmdId, commandExtendedId: cmdExId, ack });
2801
1961
  callbackInfo.callback(ack);
2802
1962
  }
2803
1963
  }
2804
1964
  clearTimeout(callbackInfo.timeout);
2805
- this.callbacksQueue.splice(tryIdx - 1, 1);
2806
1965
  } catch (e) {
2807
1966
  if (!(e instanceof Error && e.message === "No data")) {
2808
- console.warn("Read error.", e, cache);
1967
+ this.reportWarning("reader loop stopped by a read error", {
1968
+ error: e,
1969
+ pendingBytes: cache.bytes
1970
+ });
2809
1971
  }
2810
1972
  await this.close();
2811
1973
  break;
2812
1974
  } finally {
2813
- cache = cache.slice(sliceIdx);
1975
+ cache.discard(sliceIdx);
1976
+ }
1977
+ }
1978
+ pendingKey(commandId, commandExtendedId) {
1979
+ return `${commandId}:${commandExtendedId ?? ""}`;
1980
+ }
1981
+ getPendingQueue(commandId, commandExtendedId, create) {
1982
+ const key = this.pendingKey(commandId, commandExtendedId);
1983
+ let queue = this.pendingCallbacks.get(key);
1984
+ if (queue === undefined && create) {
1985
+ const created = { head: undefined, tail: undefined };
1986
+ this.pendingCallbacks.set(key, created);
1987
+ queue = created;
1988
+ }
1989
+ return queue ?? { head: undefined, tail: undefined };
1990
+ }
1991
+ enqueuePendingCallback(callback) {
1992
+ const tail = callback.queue.tail;
1993
+ callback.previous = tail;
1994
+ if (tail === undefined)
1995
+ callback.queue.head = callback;
1996
+ else
1997
+ tail.next = callback;
1998
+ callback.queue.tail = callback;
1999
+ }
2000
+ removePendingCallback(callback) {
2001
+ if (!callback.active)
2002
+ return false;
2003
+ callback.active = false;
2004
+ const { queue, previous, next } = callback;
2005
+ if (previous === undefined)
2006
+ queue.head = next;
2007
+ else
2008
+ previous.next = next;
2009
+ if (next === undefined)
2010
+ queue.tail = previous;
2011
+ else
2012
+ next.previous = previous;
2013
+ callback.previous = undefined;
2014
+ callback.next = undefined;
2015
+ if (queue !== this.rawCallbacks && queue.head === undefined && callback.wantedCommandId !== undefined) {
2016
+ this.pendingCallbacks.delete(this.pendingKey(callback.wantedCommandId, callback.wantedCommandExId));
2017
+ }
2018
+ return true;
2019
+ }
2020
+ shiftPendingCallback(commandId, commandExtendedId) {
2021
+ const queue = this.getPendingQueue(commandId, commandExtendedId, false);
2022
+ const callback = queue.head;
2023
+ if (callback !== undefined)
2024
+ this.removePendingCallback(callback);
2025
+ return callback;
2026
+ }
2027
+ shiftRawCallback() {
2028
+ const callback = this.rawCallbacks.head;
2029
+ if (callback !== undefined)
2030
+ this.removePendingCallback(callback);
2031
+ return callback;
2032
+ }
2033
+ hasPendingCallbacks() {
2034
+ return this.pendingCallbacks.size > 0 || this.rawCallbacks.head !== undefined;
2035
+ }
2036
+ drainPendingCallbacks() {
2037
+ const callbacks = [];
2038
+ for (const queue of this.pendingCallbacks.values()) {
2039
+ for (let callback = queue.head;callback; ) {
2040
+ const next = callback.next;
2041
+ callback.active = false;
2042
+ callback.previous = undefined;
2043
+ callback.next = undefined;
2044
+ callbacks.push(callback);
2045
+ callback = next;
2814
2046
  }
2047
+ }
2048
+ for (let callback = this.rawCallbacks.head;callback; ) {
2049
+ const next = callback.next;
2050
+ callback.active = false;
2051
+ callback.previous = undefined;
2052
+ callback.next = undefined;
2053
+ callbacks.push(callback);
2054
+ callback = next;
2055
+ }
2056
+ this.pendingCallbacks.clear();
2057
+ this.rawCallbacks = { head: undefined, tail: undefined };
2058
+ return callbacks;
2815
2059
  }
2816
2060
  query1() {
2817
- return new import_neverthrow.ResultAsync((async () => {
2818
- const result = await this.writeDataAsync(new Query1H2DPacket, 100);
2819
- return result instanceof Query1ReplyD2HPacket ? import_neverthrow.ok(result) : import_neverthrow.err(new VexProtocolError("query1 was not acknowledged"));
2820
- })());
2061
+ return this.request(new Query1H2DPacket, Query1ReplyD2HPacket, 100);
2821
2062
  }
2822
2063
  getSystemVersion() {
2823
- return new import_neverthrow.ResultAsync((async () => {
2824
- const result = await this.writeDataAsync(new SystemVersionH2DPacket);
2825
- if (result instanceof SystemVersionReplyD2HPacket) {
2826
- return import_neverthrow.ok(result.version);
2827
- }
2828
- return import_neverthrow.err(new VexProtocolError("system version was not acknowledged"));
2829
- })());
2064
+ return this.request(new SystemVersionH2DPacket, SystemVersionReplyD2HPacket).map((result) => result.version);
2830
2065
  }
2831
2066
  }
2832
2067
 
@@ -2853,34 +2088,19 @@ class V5SerialConnection extends VexSerialConnection {
2853
2088
  }
2854
2089
  }
2855
2090
  getDeviceStatus() {
2856
- return new import_neverthrow.ResultAsync((async () => {
2857
- const result = await this.writeDataAsync(new GetDeviceStatusH2DPacket);
2858
- return result instanceof GetDeviceStatusReplyD2HPacket ? import_neverthrow.ok(result) : import_neverthrow.err(new VexProtocolError("device status was not acknowledged"));
2859
- })());
2091
+ return this.request(new GetDeviceStatusH2DPacket, GetDeviceStatusReplyD2HPacket);
2860
2092
  }
2861
2093
  getRadioStatus() {
2862
- return new import_neverthrow.ResultAsync((async () => {
2863
- const result = await this.writeDataAsync(new GetRadioStatusH2DPacket);
2864
- return result instanceof GetRadioStatusReplyD2HPacket ? import_neverthrow.ok(result) : import_neverthrow.err(new VexProtocolError("radio status was not acknowledged"));
2865
- })());
2094
+ return this.request(new GetRadioStatusH2DPacket, GetRadioStatusReplyD2HPacket);
2866
2095
  }
2867
2096
  getSystemFlags() {
2868
- return new import_neverthrow.ResultAsync((async () => {
2869
- const result = await this.writeDataAsync(new GetSystemFlagsH2DPacket);
2870
- return result instanceof GetSystemFlagsReplyD2HPacket ? import_neverthrow.ok(result) : import_neverthrow.err(new VexProtocolError("system flags were not acknowledged"));
2871
- })());
2097
+ return this.request(new GetSystemFlagsH2DPacket, GetSystemFlagsReplyD2HPacket);
2872
2098
  }
2873
2099
  getSystemStatus(timeout = 1000) {
2874
- return new import_neverthrow.ResultAsync((async () => {
2875
- const result = await this.writeDataAsync(new GetSystemStatusH2DPacket, timeout);
2876
- return result instanceof GetSystemStatusReplyD2HPacket ? import_neverthrow.ok(result) : import_neverthrow.err(new VexProtocolError("system status was not acknowledged"));
2877
- })());
2100
+ return this.request(new GetSystemStatusH2DPacket, GetSystemStatusReplyD2HPacket, timeout);
2878
2101
  }
2879
2102
  getMatchStatus() {
2880
- return new import_neverthrow.ResultAsync((async () => {
2881
- const result = await this.writeDataAsync(new GetMatchStatusH2DPacket);
2882
- return result instanceof MatchStatusReplyD2HPacket ? import_neverthrow.ok(result) : import_neverthrow.err(new VexProtocolError("match status was not acknowledged"));
2883
- })());
2103
+ return this.request(new GetMatchStatusH2DPacket, MatchStatusReplyD2HPacket);
2884
2104
  }
2885
2105
  uploadProgramToDevice(iniConfig, binFileBuf, coldFileBuf, progressCallback) {
2886
2106
  return wrapTransfer(this, () => this._uploadProgramToDevice(iniConfig, binFileBuf, coldFileBuf, progressCallback));
@@ -2941,32 +2161,35 @@ class V5SerialConnection extends VexSerialConnection {
2941
2161
  async _downloadFileToHostUnlocked(request, downloadTarget, progressCallback) {
2942
2162
  const { filename, vendor, loadAddress, size } = request;
2943
2163
  let nextAddress = loadAddress ?? USER_FLASH_USR_CODE_START;
2944
- const p1 = await this.writeDataAsync(new InitFileTransferH2DPacket(2 /* READ */, downloadTarget, vendor, 0 /* NONE */, new Uint8Array, nextAddress, filename, ""));
2945
- if (!(p1 instanceof InitFileTransferReplyD2HPacket)) {
2946
- return import_neverthrow.err(new VexTransferError("InitFileTransferH2DPacket failed"));
2947
- }
2164
+ const p1Result = await this.request(new InitFileTransferH2DPacket(2 /* READ */, downloadTarget, vendor, 0 /* NONE */, new Uint8Array, nextAddress, filename, ""), InitFileTransferReplyD2HPacket);
2165
+ if (p1Result.isErr())
2166
+ return import_neverthrow.err(p1Result.error);
2948
2167
  let transferFailed = true;
2949
2168
  let result = import_neverthrow.ok(new Uint8Array);
2950
2169
  try {
2170
+ const p1 = p1Result.value;
2951
2171
  const fileSize = size ?? p1.fileSize;
2952
2172
  const bufferChunkSize = getTransferChunkSize(p1.windowSize);
2953
2173
  let bufferOffset = 0;
2954
2174
  const fileBuf = new Uint8Array(fileSize);
2955
2175
  while (bufferOffset < fileSize) {
2956
- const requestedSize = Math.min(bufferChunkSize, fileSize - bufferOffset);
2957
- const p2 = await this.writeDataAsync(new ReadFileH2DPacket(nextAddress, requestedSize), 3000);
2958
- if (!(p2 instanceof ReadFileReplyD2HPacket)) {
2959
- throw new VexTransferError("ReadFileReplyD2HPacket failed");
2960
- }
2176
+ const remainingSize = fileSize - bufferOffset;
2177
+ const chunkSize = Math.min(bufferChunkSize, remainingSize);
2178
+ const requestedSize = chunkSize + 3 & ~3;
2179
+ const p2Result = await this.request(new ReadFileH2DPacket(nextAddress, requestedSize), ReadFileReplyD2HPacket, 3000);
2180
+ if (p2Result.isErr())
2181
+ throw p2Result.error;
2182
+ const p2 = p2Result.value;
2961
2183
  if (p2.addr !== nextAddress) {
2962
2184
  throw new VexTransferError(`ReadFileReplyD2HPacket returned address ${p2.addr}, expected ${nextAddress}`);
2963
2185
  }
2964
2186
  if (p2.length <= 0 || p2.length > requestedSize || p2.buf.byteLength !== p2.length) {
2965
2187
  throw new VexTransferError(`ReadFileReplyD2HPacket returned invalid length ${p2.length}`);
2966
2188
  }
2967
- fileBuf.set(new Uint8Array(p2.buf), bufferOffset);
2968
- bufferOffset += p2.length;
2969
- nextAddress += p2.length;
2189
+ const receivedSize = Math.min(p2.length, remainingSize);
2190
+ fileBuf.set(p2.buf.subarray(0, receivedSize), bufferOffset);
2191
+ bufferOffset += receivedSize;
2192
+ nextAddress += receivedSize;
2970
2193
  progressCallback?.(bufferOffset, fileSize);
2971
2194
  }
2972
2195
  transferFailed = false;
@@ -2975,11 +2198,12 @@ class V5SerialConnection extends VexSerialConnection {
2975
2198
  result = import_neverthrow.err(e instanceof VexSerialError ? e : toVexSerialError(e, "transfer"));
2976
2199
  } finally {
2977
2200
  try {
2978
- await this.writeDataAsync(new ExitFileTransferH2DPacket(3 /* EXIT_HALT */), 30000);
2979
- } catch (cleanupError) {
2980
- if (!transferFailed) {
2981
- result = import_neverthrow.err(toVexSerialError(cleanupError, "io"));
2982
- }
2201
+ const exitResult = await this.request(new ExitFileTransferH2DPacket(3 /* EXIT_HALT */), ExitFileTransferReplyD2HPacket, 30000);
2202
+ if (!transferFailed && exitResult.isErr())
2203
+ result = import_neverthrow.err(exitResult.error);
2204
+ } catch (e) {
2205
+ if (!transferFailed)
2206
+ result = import_neverthrow.err(toVexSerialError(e, "io"));
2983
2207
  }
2984
2208
  }
2985
2209
  return result;
@@ -3004,22 +2228,20 @@ class V5SerialConnection extends VexSerialConnection {
3004
2228
  downloadTarget = downloadTarget ?? 1 /* FILE_TARGET_QSPI */;
3005
2229
  vendor = vendor ?? 1 /* USER */;
3006
2230
  let nextAddress = loadAddress ?? USER_FLASH_USR_CODE_START;
3007
- const p1 = await this.writeDataAsync(new InitFileTransferH2DPacket(1 /* WRITE */, downloadTarget, vendor, 1 /* OVERWRITE */, buf, nextAddress, filename, exttype));
3008
- if (!(p1 instanceof InitFileTransferReplyD2HPacket)) {
3009
- return import_neverthrow.err(new VexTransferError("InitFileTransferH2DPacket failed"));
3010
- }
2231
+ const p1Result = await this.request(new InitFileTransferH2DPacket(1 /* WRITE */, downloadTarget, vendor, 1 /* OVERWRITE */, buf, nextAddress, filename, exttype), InitFileTransferReplyD2HPacket);
2232
+ if (p1Result.isErr())
2233
+ return import_neverthrow.err(p1Result.error);
2234
+ const p1 = p1Result.value;
3011
2235
  const bufferChunkSize = getTransferChunkSize(p1.windowSize);
3012
2236
  let bufferOffset = 0;
3013
2237
  let lastBlock = false;
3014
2238
  let transferFailed = true;
3015
- let exitReply;
3016
2239
  let result = import_neverthrow.ok(false);
3017
2240
  try {
3018
2241
  if (linkedFile !== undefined) {
3019
- const p3 = await this.writeDataAsync(new LinkFileH2DPacket(linkedFile.vendor ?? 1 /* USER */, linkedFile.filename, 0), 1e4);
3020
- if (!(p3 instanceof LinkFileReplyD2HPacket)) {
3021
- throw new VexTransferError("LinkFileH2DPacket failed");
3022
- }
2242
+ const p3Result = await this.request(new LinkFileH2DPacket(linkedFile.vendor ?? 1 /* USER */, linkedFile.filename, 0), LinkFileReplyD2HPacket, 1e4);
2243
+ if (p3Result.isErr())
2244
+ throw p3Result.error;
3023
2245
  }
3024
2246
  while (!lastBlock) {
3025
2247
  let tmpbuf;
@@ -3031,23 +2253,21 @@ class V5SerialConnection extends VexSerialConnection {
3031
2253
  tmpbuf.set(buf.subarray(bufferOffset, buf.byteLength));
3032
2254
  lastBlock = true;
3033
2255
  }
3034
- const p2 = await this.writeDataAsync(new WriteFileH2DPacket(nextAddress, tmpbuf), 3000);
3035
- if (!(p2 instanceof WriteFileReplyD2HPacket))
3036
- throw new VexTransferError("WriteFileReplyD2DPacket failed");
3037
- if (progressCallback != null)
3038
- progressCallback(bufferOffset, buf.byteLength);
2256
+ const p2Result = await this.request(new WriteFileH2DPacket(nextAddress, tmpbuf), WriteFileReplyD2HPacket, 3000);
2257
+ if (p2Result.isErr())
2258
+ throw p2Result.error;
3039
2259
  bufferOffset += bufferChunkSize;
3040
2260
  nextAddress += bufferChunkSize;
2261
+ progressCallback?.(Math.min(bufferOffset, buf.byteLength), buf.byteLength);
3041
2262
  }
3042
- progressCallback?.(buf.byteLength, buf.byteLength);
3043
2263
  transferFailed = false;
3044
2264
  } catch (e) {
3045
2265
  result = import_neverthrow.err(e instanceof VexSerialError ? e : toVexSerialError(e, "transfer"));
3046
2266
  } finally {
3047
2267
  try {
3048
- exitReply = await this.writeDataAsync(new ExitFileTransferH2DPacket(transferFailed ? 3 /* EXIT_HALT */ : autoRun ? 1 /* EXIT_RUN */ : 3 /* EXIT_HALT */), 30000);
2268
+ const exitResult = await this.request(new ExitFileTransferH2DPacket(transferFailed ? 3 /* EXIT_HALT */ : autoRun ? 1 /* EXIT_RUN */ : 3 /* EXIT_HALT */), ExitFileTransferReplyD2HPacket, 30000);
3049
2269
  if (!transferFailed) {
3050
- result = import_neverthrow.ok(exitReply !== undefined && exitReply instanceof ExitFileTransferReplyD2HPacket);
2270
+ result = exitResult.map(() => true);
3051
2271
  }
3052
2272
  } catch (cleanupError) {
3053
2273
  if (!transferFailed) {
@@ -3069,14 +2289,20 @@ class V5SerialConnection extends VexSerialConnection {
3069
2289
  }
3070
2290
  let result;
3071
2291
  try {
3072
- const ack = await this.writeDataAsync(new EraseFileH2DPacket(vendor, filename));
3073
- result = ack instanceof EraseFileReplyD2HPacket ? import_neverthrow.ok(undefined) : import_neverthrow.err(new VexProtocolError("removeFile was not acknowledged"));
2292
+ const eraseResult = await this.request(new EraseFileH2DPacket(vendor, filename), EraseFileReplyD2HPacket);
2293
+ result = eraseResult.map(() => {
2294
+ return;
2295
+ });
3074
2296
  } catch (e) {
3075
2297
  result = import_neverthrow.err(toVexSerialError(e, "io"));
3076
- } finally {
3077
- try {
3078
- await this.writeDataAsync(new ExitFileTransferH2DPacket(3 /* EXIT_HALT */));
3079
- } catch {}
2298
+ }
2299
+ try {
2300
+ const exitResult = await this.request(new ExitFileTransferH2DPacket(3 /* EXIT_HALT */), ExitFileTransferReplyD2HPacket, 30000);
2301
+ if (result.isOk() && exitResult.isErr())
2302
+ result = import_neverthrow.err(exitResult.error);
2303
+ } catch (e) {
2304
+ if (result.isOk())
2305
+ result = import_neverthrow.err(toVexSerialError(e, "io"));
3080
2306
  }
3081
2307
  return result;
3082
2308
  }));
@@ -3085,23 +2311,26 @@ class V5SerialConnection extends VexSerialConnection {
3085
2311
  return new import_neverthrow.ResultAsync(this.withFileTransfer(async () => {
3086
2312
  let result;
3087
2313
  try {
3088
- const ack = await this.writeDataAsync(new FileClearUpH2DPacket(1 /* USER */), 30000);
3089
- result = ack instanceof FileClearUpReplyD2HPacket ? import_neverthrow.ok(undefined) : import_neverthrow.err(new VexProtocolError("removeAllFiles was not acknowledged"));
2314
+ const clearResult = await this.request(new FileClearUpH2DPacket(1 /* USER */), FileClearUpReplyD2HPacket, 30000);
2315
+ result = clearResult.map(() => {
2316
+ return;
2317
+ });
3090
2318
  } catch (e) {
3091
2319
  result = import_neverthrow.err(toVexSerialError(e, "io"));
3092
- } finally {
3093
- try {
3094
- await this.writeDataAsync(new ExitFileTransferH2DPacket(3 /* EXIT_HALT */));
3095
- } catch {}
2320
+ }
2321
+ try {
2322
+ const exitResult = await this.request(new ExitFileTransferH2DPacket(3 /* EXIT_HALT */), ExitFileTransferReplyD2HPacket, 30000);
2323
+ if (result.isOk() && exitResult.isErr())
2324
+ result = import_neverthrow.err(exitResult.error);
2325
+ } catch (e) {
2326
+ if (result.isOk())
2327
+ result = import_neverthrow.err(toVexSerialError(e, "io"));
3096
2328
  }
3097
2329
  return result;
3098
2330
  }));
3099
2331
  }
3100
2332
  captureScreenSetup() {
3101
- return new import_neverthrow.ResultAsync((async () => {
3102
- const result = await this.writeDataAsync(new ScreenCaptureH2DPacket(0));
3103
- return result instanceof ScreenCaptureReplyD2HPacket ? import_neverthrow.ok(result) : import_neverthrow.err(new VexProtocolError("screen capture was rejected"));
3104
- })());
2333
+ return this.request(new ScreenCaptureH2DPacket(0), ScreenCaptureReplyD2HPacket);
3105
2334
  }
3106
2335
  captureScreen(progressCallback) {
3107
2336
  return wrapTransfer(this, () => this._captureScreen(progressCallback));
@@ -3115,74 +2344,85 @@ class V5SerialConnection extends VexSerialConnection {
3115
2344
  filename: "screen",
3116
2345
  vendor: 15 /* SYS */,
3117
2346
  loadAddress: 0,
3118
- size: SCREEN_CAPTURE_MESSAGE_WIDTH * SCREEN_CAPTURE_HEIGHT * SCREEN_CAPTURE_MESSAGE_CHANNELS
2347
+ size: SCREEN_CAPTURE_FRAMEBUFFER_SIZE
3119
2348
  }, 2 /* FILE_TARGET_CBUF */, progressCallback);
3120
2349
  if (framebuffer.isErr())
3121
2350
  return import_neverthrow.err(framebuffer.error);
3122
2351
  return import_neverthrow.ok(convertScreenCapture(framebuffer.value));
3123
2352
  }
3124
2353
  setMatchMode(mode) {
3125
- return new import_neverthrow.ResultAsync((async () => {
3126
- const result = await this.writeDataAsync(new UpdateMatchModeH2DPacket(mode, 0));
3127
- return result instanceof MatchModeReplyD2HPacket ? import_neverthrow.ok(result) : import_neverthrow.err(new VexProtocolError("setMatchMode was not acknowledged"));
3128
- })());
2354
+ return this.request(new UpdateMatchModeH2DPacket(mode, 0), MatchModeReplyD2HPacket);
3129
2355
  }
3130
2356
  runProgram(value) {
3131
2357
  return this.loadProgram(value);
3132
2358
  }
3133
2359
  loadProgram(value) {
3134
- return new import_neverthrow.ResultAsync((async () => {
3135
- const result = await this.writeDataAsync(new LoadFileActionH2DPacket(1 /* USER */, 0 /* RUN */, value));
3136
- return result instanceof LoadFileActionReplyD2HPacket ? import_neverthrow.ok(result) : import_neverthrow.err(new VexProtocolError("loadProgram was not acknowledged"));
3137
- })());
2360
+ return this.request(new LoadFileActionH2DPacket(1 /* USER */, 0 /* RUN */, value), LoadFileActionReplyD2HPacket);
3138
2361
  }
3139
2362
  stopProgram() {
3140
- return new import_neverthrow.ResultAsync((async () => {
3141
- const result = await this.writeDataAsync(new LoadFileActionH2DPacket(1 /* USER */, 128 /* STOP */, ""));
3142
- return result instanceof LoadFileActionReplyD2HPacket ? import_neverthrow.ok(result) : import_neverthrow.err(new VexProtocolError("stopProgram was not acknowledged"));
3143
- })());
2363
+ return this.request(new LoadFileActionH2DPacket(1 /* USER */, 128 /* STOP */, ""), LoadFileActionReplyD2HPacket);
3144
2364
  }
3145
2365
  mockTouch(x, y, press) {
3146
- return new import_neverthrow.ResultAsync((async () => {
3147
- const result = await this.writeDataAsync(new SendDashTouchH2DPacket(x, y, press));
3148
- return result instanceof SendDashTouchReplyD2HPacket ? import_neverthrow.ok(result) : import_neverthrow.err(new VexProtocolError("mockTouch was not acknowledged"));
3149
- })());
2366
+ return this.request(new SendDashTouchH2DPacket(x, y, press), SendDashTouchReplyD2HPacket);
3150
2367
  }
3151
2368
  openScreen(screen, port) {
3152
- return new import_neverthrow.ResultAsync((async () => {
3153
- const result = await this.writeDataAsync(new SelectDashH2DPacket(screen, port));
3154
- return result instanceof SelectDashReplyD2HPacket ? import_neverthrow.ok(result) : import_neverthrow.err(new VexProtocolError("openScreen was not acknowledged"));
3155
- })());
2369
+ return this.request(new SelectDashH2DPacket(screen, port), SelectDashReplyD2HPacket);
2370
+ }
2371
+ }
2372
+
2373
+ class ReceiveBuffer {
2374
+ storage = new Uint8Array(0);
2375
+ start = 0;
2376
+ end = 0;
2377
+ get byteLength() {
2378
+ return this.end - this.start;
2379
+ }
2380
+ get bytes() {
2381
+ return this.storage.subarray(this.start, this.end);
2382
+ }
2383
+ append(chunk) {
2384
+ this.reserve(chunk.byteLength);
2385
+ this.storage.set(chunk, this.end);
2386
+ this.end += chunk.byteLength;
2387
+ }
2388
+ copy(length) {
2389
+ return this.bytes.slice(0, length);
2390
+ }
2391
+ discard(length) {
2392
+ this.start += length < 0 ? this.byteLength + length : length;
2393
+ if (this.start < 0)
2394
+ this.start = 0;
2395
+ if (this.start > this.end)
2396
+ this.start = this.end;
2397
+ if (this.start === this.end) {
2398
+ this.start = 0;
2399
+ this.end = 0;
2400
+ } else if (this.start >= 4096 && this.start * 2 >= this.storage.length) {
2401
+ this.storage.copyWithin(0, this.start, this.end);
2402
+ this.end -= this.start;
2403
+ this.start = 0;
2404
+ }
2405
+ }
2406
+ reserve(additional) {
2407
+ const required = this.byteLength + additional;
2408
+ if (this.storage.length - this.end >= additional)
2409
+ return;
2410
+ if (this.storage.length >= required) {
2411
+ this.storage.copyWithin(0, this.start, this.end);
2412
+ this.end = this.byteLength;
2413
+ this.start = 0;
2414
+ return;
2415
+ }
2416
+ const storage = new Uint8Array(Math.max(required, Math.max(64, this.storage.length * 2)));
2417
+ storage.set(this.bytes);
2418
+ this.end = this.byteLength;
2419
+ this.start = 0;
2420
+ this.storage = storage;
3156
2421
  }
3157
2422
  }
3158
- function binaryArrayJoin(left, right) {
3159
- const leftSize = left != null ? left.byteLength : 0;
3160
- const rightSize = right != null ? right.byteLength : 0;
3161
- const all = new Uint8Array(leftSize + rightSize);
3162
- if (all.length === 0)
3163
- return new Uint8Array;
3164
- if (left != null)
3165
- all.set(new Uint8Array(left), 0);
3166
- if (right != null)
3167
- all.set(new Uint8Array(right), leftSize);
3168
- return all;
3169
- }
3170
2423
  function getTransferChunkSize(windowSize) {
3171
2424
  return windowSize > 0 && windowSize <= USER_PROG_CHUNK_SIZE ? windowSize : USER_PROG_CHUNK_SIZE;
3172
2425
  }
3173
- function convertScreenCapture(framebuffer) {
3174
- const pixels = new Uint8Array(SCREEN_CAPTURE_WIDTH * SCREEN_CAPTURE_HEIGHT * SCREEN_CAPTURE_CHANNELS);
3175
- for (let row = 0;row < SCREEN_CAPTURE_HEIGHT; row++) {
3176
- for (let column = 0;column < SCREEN_CAPTURE_WIDTH; column++) {
3177
- const source = (row * SCREEN_CAPTURE_MESSAGE_WIDTH + column) * SCREEN_CAPTURE_MESSAGE_CHANNELS;
3178
- const target = (row * SCREEN_CAPTURE_WIDTH + column) * SCREEN_CAPTURE_CHANNELS;
3179
- pixels[target] = framebuffer[source + 2] ?? 0;
3180
- pixels[target + 1] = framebuffer[source + 1] ?? 0;
3181
- pixels[target + 2] = framebuffer[source] ?? 0;
3182
- }
3183
- }
3184
- return pixels;
3185
- }
3186
2426
  function wrapTransfer(conn, operation) {
3187
2427
  return new import_neverthrow.ResultAsync(conn.withFileTransfer(async () => {
3188
2428
  try {
@@ -3194,21 +2434,26 @@ function wrapTransfer(conn, operation) {
3194
2434
  }
3195
2435
  }));
3196
2436
  }
3197
- function errAsyncVex(error) {
3198
- return new import_neverthrow.ResultAsync(Promise.resolve(import_neverthrow.err(error)));
2437
+ function expectedReplyMessage(packet, ReplyType, reply) {
2438
+ const expected = `expected ${ReplyType.name} for ${packet.constructor.name}`;
2439
+ if (typeof reply === "number")
2440
+ return `${expected}; received ${ackTypeName(reply)}`;
2441
+ if (reply instanceof ArrayBuffer)
2442
+ return `${expected}; received raw ArrayBuffer`;
2443
+ return `${expected}; received ${reply.constructor.name}`;
2444
+ }
2445
+ function ackTypeName(ackType) {
2446
+ return `AckType.${AckType[ackType] ?? "UNKNOWN"} (${ackType})`;
3199
2447
  }
3200
2448
  // src/VexDeviceState.ts
3201
- var import_neverthrow4 = __toESM(require_index_cjs(), 1);
2449
+ var import_neverthrow4 = require("neverthrow");
3202
2450
 
3203
2451
  // src/VexFirmware.ts
3204
- var import_neverthrow2 = __toESM(require_index_cjs(), 1);
2452
+ var import_neverthrow2 = require("neverthrow");
3205
2453
  var MAX_CATALOG_BYTES = 4 * 1024;
3206
2454
  var MAX_VEXOS_BYTES = 64 * 1024 * 1024;
3207
2455
  var MAX_FIRMWARE_IMAGE_BYTES = 32 * 1024 * 1024;
3208
2456
  var MAX_AGGREGATE_IMAGE_BYTES = 48 * 1024 * 1024;
3209
- function fromAsyncFn(fn) {
3210
- return new import_neverthrow2.ResultAsync(fn());
3211
- }
3212
2457
  function downloadFileFromInternet(link, options = {}) {
3213
2458
  const { maxBytes = Number.POSITIVE_INFINITY, timeout = 30000 } = options;
3214
2459
  if (maxBytes <= 0) {
@@ -3217,7 +2462,7 @@ function downloadFileFromInternet(link, options = {}) {
3217
2462
  if (timeout < 0) {
3218
2463
  return import_neverthrow2.errAsync(new VexInvalidArgumentError("timeout must be non-negative"));
3219
2464
  }
3220
- return fromAsyncFn(() => runDownload(link, maxBytes, timeout));
2465
+ return new import_neverthrow2.ResultAsync(runDownload(link, maxBytes, timeout));
3221
2466
  }
3222
2467
  async function runDownload(link, maxBytes, timeout) {
3223
2468
  const controller = new AbortController;
@@ -3284,7 +2529,7 @@ function sleepUntilAsync(f, timeout, interval = 20) {
3284
2529
  if (interval <= 0) {
3285
2530
  return import_neverthrow2.errAsync(new VexInvalidArgumentError("interval must be positive"));
3286
2531
  }
3287
- return fromAsyncFn(() => runSleepUntilAsync(f, timeout, interval));
2532
+ return new import_neverthrow2.ResultAsync(runSleepUntilAsync(f, timeout, interval));
3288
2533
  }
3289
2534
  async function runSleepUntilAsync(f, timeout, interval) {
3290
2535
  const deadline = Date.now() + timeout;
@@ -3309,7 +2554,7 @@ function sleepUntil(f, timeout, interval = 20) {
3309
2554
  if (interval <= 0) {
3310
2555
  return import_neverthrow2.errAsync(new VexInvalidArgumentError("interval must be positive"));
3311
2556
  }
3312
- return fromAsyncFn(() => runSleepUntil(f, timeout, interval));
2557
+ return new import_neverthrow2.ResultAsync(runSleepUntil(f, timeout, interval));
3313
2558
  }
3314
2559
  async function runSleepUntil(f, timeout, interval) {
3315
2560
  const deadline = Date.now() + timeout;
@@ -3336,6 +2581,59 @@ function sleep(ms) {
3336
2581
  async function sleepInner(ms) {
3337
2582
  return new Promise((resolve) => setTimeout(resolve, ms));
3338
2583
  }
2584
+ async function flashFactoryImage(conn, options, pcb) {
2585
+ const { image, label, downloadTarget } = options;
2586
+ pcb(`FACTORY ENB ${label}`, 0, 0);
2587
+ const enableReply = await conn.request(new FactoryEnableH2DPacket, FactoryEnableReplyD2HPacket);
2588
+ if (enableReply.isErr())
2589
+ return import_neverthrow2.err(enableReply.error);
2590
+ const writeRequest = {
2591
+ filename: "null.bin",
2592
+ vendor: 1 /* USER */,
2593
+ loadAddress: USER_FLASH_USR_CODE_START,
2594
+ buf: image.buf,
2595
+ downloadTarget,
2596
+ exttype: "bin",
2597
+ autoRun: true,
2598
+ linkedFile: undefined
2599
+ };
2600
+ const upload = await conn.uploadFileToDeviceUnlocked(writeRequest, (c, t) => {
2601
+ pcb(`UPLOAD ${label}`, c, t);
2602
+ });
2603
+ if (upload.isErr())
2604
+ return import_neverthrow2.err(upload.error);
2605
+ if (!upload.value) {
2606
+ return import_neverthrow2.err(new VexFirmwareError(`${label} upload was rejected by device`));
2607
+ }
2608
+ const deadline = Date.now() + 120000;
2609
+ while (Date.now() < deadline) {
2610
+ const statusReply = await conn.request(new FactoryStatusH2DPacket, FactoryStatusReplyD2HPacket, 1e4);
2611
+ if (statusReply.isErr())
2612
+ return import_neverthrow2.err(statusReply.error);
2613
+ reportFactoryStatus(label, statusReply.value, pcb);
2614
+ if (statusReply.value.status === 0 && statusReply.value.percent === 100) {
2615
+ return import_neverthrow2.ok(undefined);
2616
+ }
2617
+ await sleepInner(500);
2618
+ }
2619
+ return import_neverthrow2.err(new VexFirmwareError(`${label} factory status timed out`));
2620
+ }
2621
+ function reportFactoryStatus(label, reply, pcb) {
2622
+ switch (reply.status) {
2623
+ case 2:
2624
+ pcb(`ERASE ${label}`, reply.percent, 100);
2625
+ break;
2626
+ case 3:
2627
+ pcb(`WRITE ${label}`, reply.percent, 100);
2628
+ break;
2629
+ case 4:
2630
+ pcb(`VERIFY ${label}`, reply.percent, 100);
2631
+ break;
2632
+ case 8:
2633
+ pcb(`FINISHING ${label}`, reply.percent, 100);
2634
+ break;
2635
+ }
2636
+ }
3339
2637
  async function extractFirmwareImages(usingVersion, vexos) {
3340
2638
  const { unzip } = await import("unzipit");
3341
2639
  const { entries } = await unzip(vexos);
@@ -3383,7 +2681,7 @@ async function extractFirmwareImages(usingVersion, vexos) {
3383
2681
  return ordered;
3384
2682
  }
3385
2683
  function uploadFirmware(state, publicUrl = "https://content.vexrobotics.com/vexos/public/V5/", usingVersion, progressCallback) {
3386
- return fromAsyncFn(() => runUploadFirmware(state, publicUrl, usingVersion, progressCallback));
2684
+ return new import_neverthrow2.ResultAsync(runUploadFirmware(state, publicUrl, usingVersion, progressCallback));
3387
2685
  }
3388
2686
  async function runUploadFirmware(state, publicUrl, usingVersion, progressCallback) {
3389
2687
  const device = state._instance;
@@ -3425,133 +2723,48 @@ async function runUploadFirmware(state, publicUrl, usingVersion, progressCallbac
3425
2723
  return import_neverthrow2.err(toVexSerialError(e, "firmware"));
3426
2724
  }
3427
2725
  pcb("UNZIP VEXOS", 1, 1);
3428
- return state.withFileTransfer(async () => {
3429
- pcb("FACTORY ENB BOOT", 0, 0);
3430
- const result = await conn.writeDataAsync(new FactoryEnableH2DPacket);
3431
- if (!(result instanceof FactoryEnableReplyD2HPacket)) {
3432
- return import_neverthrow2.err(new VexProtocolError("FactoryEnableH2DPacket failed"));
3433
- }
3434
- const boot = images.find((image) => image.name.endsWith("BOOT.bin"));
3435
- if (boot === undefined) {
3436
- return import_neverthrow2.err(new VexFirmwareError("VEXos archive is missing BOOT.bin"));
3437
- }
3438
- const assertImage = images.find((image) => image.name.endsWith("assets.bin"));
3439
- if (assertImage === undefined) {
3440
- return import_neverthrow2.err(new VexFirmwareError("VEXos archive is missing assets.bin"));
3441
- }
3442
- const bootWriteRequest = {
3443
- filename: "null.bin",
3444
- vendor: 1 /* USER */,
3445
- loadAddress: USER_FLASH_USR_CODE_START,
3446
- buf: boot.buf,
3447
- downloadTarget: 14 /* FILE_TARGET_B1 */,
3448
- exttype: "bin",
3449
- autoRun: true,
3450
- linkedFile: undefined
3451
- };
3452
- const bootUpload = await conn.uploadFileToDevice(bootWriteRequest, (c, t) => {
3453
- pcb("UPLOAD BOOT", c, t);
3454
- });
3455
- if (bootUpload.isErr())
3456
- return import_neverthrow2.err(bootUpload.error);
3457
- if (!bootUpload.value)
3458
- return import_neverthrow2.ok(false);
3459
- const bootDeadline = Date.now() + 120000;
3460
- let bootComplete = false;
3461
- while (Date.now() < bootDeadline) {
3462
- const result3 = await conn.writeDataAsync(new FactoryStatusH2DPacket, 1e4);
3463
- if (result3 instanceof FactoryStatusReplyD2HPacket) {
3464
- switch (result3.status) {
3465
- case 2:
3466
- pcb("ERASE BOOT", result3.percent, 100);
3467
- break;
3468
- case 3:
3469
- pcb("WRITE BOOT", result3.percent, 100);
3470
- break;
3471
- case 4:
3472
- pcb("VERIFY BOOT", result3.percent, 100);
3473
- break;
3474
- case 8:
3475
- pcb("FINISHING BOOT", result3.percent, 100);
3476
- break;
3477
- }
3478
- if (result3.status === 0 && result3.percent === 100) {
3479
- bootComplete = true;
3480
- break;
3481
- }
3482
- } else {
3483
- return import_neverthrow2.ok(false);
3484
- }
3485
- await sleepInner(500);
3486
- }
3487
- if (!bootComplete)
3488
- return import_neverthrow2.ok(false);
3489
- pcb("FACTORY ENB ASSERT", 0, 0);
3490
- const result5 = await conn.writeDataAsync(new FactoryEnableH2DPacket);
3491
- if (!(result5 instanceof FactoryEnableReplyD2HPacket)) {
3492
- return import_neverthrow2.err(new VexProtocolError("FactoryEnableH2DPacket failed"));
3493
- }
3494
- const assertWriteRequest = {
3495
- filename: "null.bin",
3496
- vendor: 1 /* USER */,
3497
- loadAddress: USER_FLASH_USR_CODE_START,
3498
- buf: assertImage.buf,
3499
- downloadTarget: 13 /* FILE_TARGET_A1 */,
3500
- exttype: "bin",
3501
- autoRun: true,
3502
- linkedFile: undefined
3503
- };
3504
- const assertUpload = await conn.uploadFileToDevice(assertWriteRequest, (c, t) => {
3505
- pcb("UPLOAD ASSERT", c, t);
3506
- });
3507
- if (assertUpload.isErr())
3508
- return import_neverthrow2.err(assertUpload.error);
3509
- if (!assertUpload.value)
3510
- return import_neverthrow2.ok(false);
3511
- const assertDeadline = Date.now() + 120000;
3512
- let assertComplete = false;
3513
- while (Date.now() < assertDeadline) {
3514
- const result7 = await conn.writeDataAsync(new FactoryStatusH2DPacket, 1e4);
3515
- if (result7 instanceof FactoryStatusReplyD2HPacket) {
3516
- switch (result7.status) {
3517
- case 2:
3518
- pcb("ERASE ASSERT", result7.percent, 100);
3519
- break;
3520
- case 3:
3521
- pcb("WRITE ASSERT", result7.percent, 100);
3522
- break;
3523
- case 4:
3524
- pcb("VERIFY ASSERT", result7.percent, 100);
3525
- break;
3526
- case 8:
3527
- pcb("FINISHING ASSERT", result7.percent, 100);
3528
- break;
2726
+ return state.withRefreshPaused(async () => {
2727
+ try {
2728
+ return await conn.withFileTransfer(async () => {
2729
+ const boot = images.find((image) => image.name.endsWith("BOOT.bin"));
2730
+ if (boot === undefined) {
2731
+ return import_neverthrow2.err(new VexFirmwareError("VEXos archive is missing BOOT.bin"));
3529
2732
  }
3530
- if (result7.status === 0 && result7.percent === 100) {
3531
- assertComplete = true;
3532
- break;
2733
+ const assertImage = images.find((image) => image.name.endsWith("assets.bin"));
2734
+ if (assertImage === undefined) {
2735
+ return import_neverthrow2.err(new VexFirmwareError("VEXos archive is missing assets.bin"));
3533
2736
  }
3534
- } else {
3535
- return import_neverthrow2.ok(false);
3536
- }
3537
- await sleepInner(500);
2737
+ const bootFlash = await flashFactoryImage(conn, {
2738
+ image: boot,
2739
+ label: "BOOT",
2740
+ downloadTarget: 14 /* FILE_TARGET_B1 */
2741
+ }, pcb);
2742
+ if (bootFlash.isErr())
2743
+ return import_neverthrow2.err(bootFlash.error);
2744
+ const assetsFlash = await flashFactoryImage(conn, {
2745
+ image: assertImage,
2746
+ label: "ASSETS",
2747
+ downloadTarget: 13 /* FILE_TARGET_A1 */
2748
+ }, pcb);
2749
+ if (assetsFlash.isErr())
2750
+ return import_neverthrow2.err(assetsFlash.error);
2751
+ return import_neverthrow2.ok(true);
2752
+ });
2753
+ } catch (e) {
2754
+ return import_neverthrow2.err(toVexSerialError(e, "firmware"));
3538
2755
  }
3539
- if (!assertComplete)
3540
- return import_neverthrow2.ok(false);
3541
- return import_neverthrow2.ok(true);
3542
2756
  });
3543
2757
  }
3544
2758
 
3545
2759
  // src/VexTransfers.ts
3546
- var import_neverthrow3 = __toESM(require_index_cjs(), 1);
2760
+ var import_neverthrow3 = require("neverthrow");
3547
2761
  function getValue(state, key) {
3548
2762
  return new import_neverthrow3.ResultAsync((async () => {
3549
2763
  const conn = state._instance.connection;
3550
2764
  if (conn == null || !conn.isConnected) {
3551
2765
  return import_neverthrow3.err(new VexNotConnectedError);
3552
2766
  }
3553
- const result = await conn.writeDataAsync(new ReadKeyValueH2DPacket(key));
3554
- return result instanceof ReadKeyValueReplyD2HPacket ? import_neverthrow3.ok(result.value) : import_neverthrow3.err(new VexProtocolError("getValue was not acknowledged"));
2767
+ return conn.request(new ReadKeyValueH2DPacket(key), ReadKeyValueReplyD2HPacket).map((result) => result.value);
3555
2768
  })());
3556
2769
  }
3557
2770
  function setValue(state, key, value) {
@@ -3560,8 +2773,9 @@ function setValue(state, key, value) {
3560
2773
  if (conn == null || !conn.isConnected) {
3561
2774
  return import_neverthrow3.err(new VexNotConnectedError);
3562
2775
  }
3563
- const result = await conn.writeDataAsync(new WriteKeyValueH2DPacket(key, value));
3564
- return result instanceof WriteKeyValueReplyD2HPacket ? import_neverthrow3.ok(undefined) : import_neverthrow3.err(new VexProtocolError("setValue was not acknowledged"));
2776
+ return conn.request(new WriteKeyValueH2DPacket(key, value), WriteKeyValueReplyD2HPacket).map(() => {
2777
+ return;
2778
+ });
3565
2779
  })());
3566
2780
  }
3567
2781
  function listFiles(state, vendor = 1 /* USER */) {
@@ -3570,26 +2784,24 @@ function listFiles(state, vendor = 1 /* USER */) {
3570
2784
  if (conn == null || !conn.isConnected) {
3571
2785
  return import_neverthrow3.err(new VexNotConnectedError);
3572
2786
  }
3573
- const result = await conn.writeDataAsync(new GetDirectoryFileCountH2DPacket(vendor));
3574
- if (!(result instanceof GetDirectoryFileCountReplyD2HPacket)) {
3575
- return import_neverthrow3.err(new VexProtocolError("directory file count was not acknowledged"));
3576
- }
2787
+ const countResult = await conn.request(new GetDirectoryFileCountH2DPacket(vendor), GetDirectoryFileCountReplyD2HPacket);
2788
+ if (countResult.isErr())
2789
+ return import_neverthrow3.err(countResult.error);
3577
2790
  const files = [];
3578
- for (let i = 0;i < result.count; i++) {
3579
- const result2 = await conn.writeDataAsync(new GetDirectoryEntryH2DPacket(i));
3580
- if (!(result2 instanceof GetDirectoryEntryReplyD2HPacket)) {
3581
- return import_neverthrow3.err(new VexProtocolError("directory entry was not acknowledged"));
3582
- }
3583
- if (result2.file != null) {
2791
+ for (let i = 0;i < countResult.value.count; i++) {
2792
+ const entryResult = await conn.request(new GetDirectoryEntryH2DPacket(i), GetDirectoryEntryReplyD2HPacket);
2793
+ if (entryResult.isErr())
2794
+ return import_neverthrow3.err(entryResult.error);
2795
+ if (entryResult.value.file != null) {
3584
2796
  files.push({
3585
- filename: result2.file.filename,
2797
+ filename: entryResult.value.file.filename,
3586
2798
  vendor,
3587
- loadAddress: result2.file.loadAddress,
3588
- size: result2.file.size,
3589
- crc32: result2.file.crc32,
3590
- type: result2.file.type,
3591
- timestamp: result2.file.timestamp,
3592
- version: result2.file.version
2799
+ loadAddress: entryResult.value.file.loadAddress,
2800
+ size: entryResult.value.file.size,
2801
+ crc32: entryResult.value.file.crc32,
2802
+ type: entryResult.value.file.type,
2803
+ timestamp: entryResult.value.file.timestamp,
2804
+ version: entryResult.value.file.version
3593
2805
  });
3594
2806
  }
3595
2807
  }
@@ -3625,10 +2837,10 @@ function listProgram(state) {
3625
2837
  time: n,
3626
2838
  requestedSlot: -1
3627
2839
  };
3628
- const result2 = await conn.writeDataAsync(new GetProgramSlotInfoH2DPacket(1 /* USER */, program.binfile));
3629
- if (result2 instanceof GetProgramSlotInfoReplyD2HPacket) {
3630
- program.slot = result2.slot;
3631
- program.requestedSlot = result2.requestedSlot;
2840
+ const slotInfo = await conn.request(new GetProgramSlotInfoH2DPacket(1 /* USER */, program.binfile), GetProgramSlotInfoReplyD2HPacket);
2841
+ if (slotInfo.isOk()) {
2842
+ program.slot = slotInfo.value.slot;
2843
+ program.requestedSlot = slotInfo.value.requestedSlot;
3632
2844
  }
3633
2845
  programList.push(program);
3634
2846
  }
@@ -3647,7 +2859,7 @@ function readFile(state, request, downloadTarget = 1 /* FILE_TARGET_QSPI */, pro
3647
2859
  } else {
3648
2860
  handle = request;
3649
2861
  }
3650
- return state.withFileTransfer(() => conn.downloadFileToHost(handle, downloadTarget, progressCallback));
2862
+ return state.withRefreshPaused(() => conn.downloadFileToHost(handle, downloadTarget, progressCallback));
3651
2863
  })());
3652
2864
  }
3653
2865
  function removeFile(state, request) {
@@ -3656,7 +2868,7 @@ function removeFile(state, request) {
3656
2868
  if (conn == null || !conn.isConnected) {
3657
2869
  return import_neverthrow3.err(new VexNotConnectedError);
3658
2870
  }
3659
- return state.withFileTransfer(() => conn.removeFile(request));
2871
+ return state.withRefreshPaused(() => conn.removeFile(request));
3660
2872
  })());
3661
2873
  }
3662
2874
  function removeAllFiles(state) {
@@ -3665,7 +2877,7 @@ function removeAllFiles(state) {
3665
2877
  if (conn == null || !conn.isConnected) {
3666
2878
  return import_neverthrow3.err(new VexNotConnectedError);
3667
2879
  }
3668
- return state.withFileTransfer(() => conn.removeAllFiles());
2880
+ return state.withRefreshPaused(() => conn.removeAllFiles());
3669
2881
  })());
3670
2882
  }
3671
2883
  function uploadProgram(state, iniConfig, binFileBuf, coldFileBuf, progressCallback) {
@@ -3678,7 +2890,7 @@ async function runUploadProgram(state, iniConfig, binFileBuf, coldFileBuf, progr
3678
2890
  return import_neverthrow3.err(new VexNotConnectedError);
3679
2891
  }
3680
2892
  let switchedToDownload = false;
3681
- return state.withFileTransfer(async () => {
2893
+ return state.withRefreshPaused(async () => {
3682
2894
  try {
3683
2895
  if (device.isV5Controller) {
3684
2896
  await sleep(250);
@@ -3737,7 +2949,7 @@ function writeFile(state, request, progressCallback) {
3737
2949
  if (conn == null || !conn.isConnected) {
3738
2950
  return import_neverthrow3.err(new VexNotConnectedError);
3739
2951
  }
3740
- return state.withFileTransfer(() => conn.uploadFileToDevice(request, progressCallback));
2952
+ return state.withRefreshPaused(() => conn.uploadFileToDevice(request, progressCallback));
3741
2953
  })());
3742
2954
  }
3743
2955
  function captureScreen(state, progressCallback) {
@@ -3746,7 +2958,7 @@ function captureScreen(state, progressCallback) {
3746
2958
  if (conn == null || !conn.isConnected) {
3747
2959
  return import_neverthrow3.err(new VexNotConnectedError);
3748
2960
  }
3749
- return state.withFileTransfer(() => conn.captureScreen(progressCallback));
2961
+ return state.withRefreshPaused(() => conn.captureScreen(progressCallback));
3750
2962
  })());
3751
2963
  }
3752
2964
 
@@ -3769,10 +2981,13 @@ class VexSerialDevice extends VexEventTarget {
3769
2981
  class V5SerialDeviceState {
3770
2982
  _instance;
3771
2983
  refreshPauseDepth = 0;
3772
- get isFileTransferring() {
2984
+ get isRefreshPaused() {
3773
2985
  return this.refreshPauseDepth > 0;
3774
2986
  }
3775
- async withFileTransfer(operation) {
2987
+ get isFileTransferring() {
2988
+ return this.isRefreshPaused;
2989
+ }
2990
+ async withRefreshPaused(operation) {
3776
2991
  this.refreshPauseDepth++;
3777
2992
  try {
3778
2993
  return await operation();
@@ -3780,6 +2995,9 @@ class V5SerialDeviceState {
3780
2995
  this.refreshPauseDepth--;
3781
2996
  }
3782
2997
  }
2998
+ async withFileTransfer(operation) {
2999
+ return this.withRefreshPaused(operation);
3000
+ }
3783
3001
  brain = {
3784
3002
  activeProgram: 0,
3785
3003
  battery: {
@@ -3809,7 +3027,8 @@ class V5SerialDeviceState {
3809
3027
  },
3810
3028
  {
3811
3029
  battery: 0,
3812
- isAvailable: false
3030
+ isAvailable: false,
3031
+ isCharging: false
3813
3032
  }
3814
3033
  ];
3815
3034
  devices = [];
@@ -3832,8 +3051,14 @@ class V5SerialDeviceState {
3832
3051
 
3833
3052
  class V5Brain {
3834
3053
  state;
3054
+ batteryFacade;
3055
+ buttonFacade;
3056
+ settingsFacade;
3835
3057
  constructor(state) {
3836
3058
  this.state = state;
3059
+ this.batteryFacade = new V5Battery(state);
3060
+ this.buttonFacade = new V5BrainButton(state);
3061
+ this.settingsFacade = new V5BrainSettings(state);
3837
3062
  }
3838
3063
  get isRunningProgram() {
3839
3064
  return this.activeProgram !== 0;
@@ -3866,9 +3091,8 @@ class V5Brain {
3866
3091
  const reply = await conn.runProgram(slot);
3867
3092
  if (reply.isErr())
3868
3093
  return import_neverthrow4.err(reply.error);
3869
- const slotNumber = typeof slot === "string" ? Number.parseInt(slot, 10) : slot;
3870
- if (Number.isFinite(slotNumber))
3871
- this.state.brain.activeProgram = slotNumber;
3094
+ if (typeof slot === "number")
3095
+ this.state.brain.activeProgram = slot;
3872
3096
  return import_neverthrow4.ok(undefined);
3873
3097
  })());
3874
3098
  }
@@ -3885,10 +3109,10 @@ class V5Brain {
3885
3109
  })());
3886
3110
  }
3887
3111
  get battery() {
3888
- return new V5Battery(this.state);
3112
+ return this.batteryFacade;
3889
3113
  }
3890
3114
  get button() {
3891
- return new V5BrainButton(this.state);
3115
+ return this.buttonFacade;
3892
3116
  }
3893
3117
  get cpu0Version() {
3894
3118
  return this.state.brain.cpu0Version;
@@ -3900,7 +3124,7 @@ class V5Brain {
3900
3124
  return this.state.brain.isAvailable;
3901
3125
  }
3902
3126
  get settings() {
3903
- return new V5BrainSettings(this.state);
3127
+ return this.settingsFacade;
3904
3128
  }
3905
3129
  get systemVersion() {
3906
3130
  return this.state.brain.systemVersion;
@@ -4055,67 +3279,144 @@ class V5Radio {
4055
3279
  }
4056
3280
  changeChannel(channel) {
4057
3281
  return new import_neverthrow4.ResultAsync((async () => {
4058
- const result = await this.state._instance.connection?.writeDataAsync(new FileControlH2DPacket(1, channel));
4059
- return result instanceof FileControlReplyD2HPacket ? import_neverthrow4.ok(undefined) : import_neverthrow4.err(new VexProtocolError("changeChannel was not acknowledged"));
3282
+ const conn = this.state._instance.connection;
3283
+ if (conn == null || !conn.isConnected) {
3284
+ return import_neverthrow4.err(new VexNotConnectedError);
3285
+ }
3286
+ return conn.request(new FileControlH2DPacket(1, channel), FileControlReplyD2HPacket).map(() => {
3287
+ return;
3288
+ });
4060
3289
  })());
4061
3290
  }
4062
3291
  }
4063
3292
 
4064
3293
  // src/VexDevice.ts
4065
- var import_neverthrow5 = __toESM(require_index_cjs(), 1);
3294
+ var import_neverthrow5 = require("neverthrow");
3295
+ function unrefTimerIfPossible(timer) {
3296
+ if (typeof timer !== "object" || timer === null || !("unref" in timer))
3297
+ return;
3298
+ const unref = timer.unref;
3299
+ if (typeof unref === "function")
3300
+ unref.call(timer);
3301
+ }
3302
+ function describePort(port) {
3303
+ const info = port.getInfo();
3304
+ const identifier = info.path ?? info.id ?? info.serialNumber;
3305
+ return typeof identifier === "string" ? identifier : undefined;
3306
+ }
4066
3307
 
4067
3308
  class V5SerialDevice extends VexSerialDevice {
4068
3309
  autoReconnect = true;
4069
- autoRefresh = true;
4070
3310
  pauseRefreshOnFileTransfer = true;
4071
3311
  _isReconnecting = false;
4072
3312
  _isDisconnecting = false;
4073
3313
  _refreshInterval;
4074
3314
  state = new V5SerialDeviceState(this);
4075
3315
  _disposed = false;
3316
+ _lifecycleGeneration = 0;
3317
+ _disconnectListener;
4076
3318
  _refreshGeneration = 0;
4077
- constructor(defaultSerial) {
3319
+ _autoRefresh = false;
3320
+ _refreshIntervalMs = 200;
3321
+ _isLastRefreshComplete = true;
3322
+ _brain = new V5Brain(this.state);
3323
+ _controllers = [
3324
+ new V5Controller(this.state, 0),
3325
+ new V5Controller(this.state, 1)
3326
+ ];
3327
+ _radio = new V5Radio(this.state);
3328
+ _deviceFacades = [];
3329
+ _emitSafely(eventName, data) {
3330
+ try {
3331
+ this.emit(eventName, data);
3332
+ } catch {}
3333
+ }
3334
+ constructor(defaultSerial, options = false) {
4078
3335
  super(defaultSerial);
4079
- let isLastRefreshComplete = true;
3336
+ const autoRefresh = typeof options === "boolean" ? options : options.autoRefresh ?? false;
3337
+ this.refreshIntervalMs = typeof options === "boolean" ? 200 : options.refreshIntervalMs ?? 200;
3338
+ this.autoRefresh = autoRefresh;
3339
+ }
3340
+ get autoRefresh() {
3341
+ return this._autoRefresh;
3342
+ }
3343
+ set autoRefresh(value) {
3344
+ if (this._autoRefresh === value)
3345
+ return;
3346
+ this._autoRefresh = value;
3347
+ if (value) {
3348
+ this._startRefreshInterval();
3349
+ } else {
3350
+ this._stopRefreshInterval();
3351
+ }
3352
+ }
3353
+ get refreshIntervalMs() {
3354
+ return this._refreshIntervalMs;
3355
+ }
3356
+ set refreshIntervalMs(value) {
3357
+ if (!Number.isFinite(value) || value <= 0) {
3358
+ throw new VexInvalidArgumentError("refreshIntervalMs must be a positive finite number");
3359
+ }
3360
+ if (this._refreshIntervalMs === value)
3361
+ return;
3362
+ this._refreshIntervalMs = value;
3363
+ if (this._refreshInterval !== undefined) {
3364
+ this._stopRefreshInterval();
3365
+ this._startRefreshInterval();
3366
+ }
3367
+ }
3368
+ _startRefreshInterval() {
3369
+ if (this._refreshInterval !== undefined || this._disposed)
3370
+ return;
4080
3371
  this._refreshInterval = setInterval(() => {
4081
3372
  if (this._disposed)
4082
3373
  return;
4083
- if (this.autoRefresh && isLastRefreshComplete) {
3374
+ if (this._autoRefresh && this._isLastRefreshComplete) {
4084
3375
  if (!this.isConnected) {
4085
3376
  this.state.brain.isAvailable = false;
4086
3377
  return;
4087
3378
  }
4088
- if (!this.pauseRefreshOnFileTransfer || !this.state.isFileTransferring) {
4089
- isLastRefreshComplete = false;
3379
+ if (!this.pauseRefreshOnFileTransfer || !this.state.isRefreshPaused) {
3380
+ this._isLastRefreshComplete = false;
4090
3381
  (async () => {
4091
3382
  try {
4092
3383
  const r = await this.refresh();
4093
3384
  if (r.isErr())
4094
- this.emit("error", r.error);
3385
+ this._emitSafely("error", r.error);
4095
3386
  } catch (error) {
4096
- this.emit("error", error);
3387
+ this._emitSafely("error", error);
4097
3388
  } finally {
4098
- isLastRefreshComplete = true;
3389
+ this._isLastRefreshComplete = true;
4099
3390
  }
4100
3391
  })();
4101
3392
  }
4102
3393
  }
4103
- }, 200);
3394
+ }, this._refreshIntervalMs);
3395
+ unrefTimerIfPossible(this._refreshInterval);
3396
+ }
3397
+ _stopRefreshInterval() {
3398
+ if (this._refreshInterval === undefined)
3399
+ return;
3400
+ clearInterval(this._refreshInterval);
3401
+ this._refreshInterval = undefined;
4104
3402
  }
4105
3403
  get isV5Controller() {
4106
3404
  return this.deviceType === 1283 /* V5_CONTROLLER */;
4107
3405
  }
4108
3406
  get brain() {
4109
- return new V5Brain(this.state);
3407
+ return this._brain;
4110
3408
  }
4111
3409
  get controllers() {
4112
- return [new V5Controller(this.state, 0), new V5Controller(this.state, 1)];
3410
+ return this._controllers;
4113
3411
  }
4114
3412
  get devices() {
4115
3413
  const rtn = [];
4116
- for (let i = 1;i <= this.state.devices.length; i++) {
4117
- if (this.state.devices[i] != null)
4118
- rtn.push(new V5SmartDevice(this.state, i));
3414
+ for (let i = 1;i < this.state.devices.length; i++) {
3415
+ if (this.state.devices[i] != null) {
3416
+ const facade = this._deviceFacades[i] ?? new V5SmartDevice(this.state, i);
3417
+ this._deviceFacades[i] = facade;
3418
+ rtn.push(facade);
3419
+ }
4119
3420
  }
4120
3421
  return rtn;
4121
3422
  }
@@ -4140,7 +3441,7 @@ class V5SerialDevice extends VexSerialDevice {
4140
3441
  })());
4141
3442
  }
4142
3443
  get radio() {
4143
- return new V5Radio(this.state);
3444
+ return this._radio;
4144
3445
  }
4145
3446
  mockTouch(x, y, press) {
4146
3447
  return new import_neverthrow5.ResultAsync((async () => {
@@ -4153,58 +3454,125 @@ class V5SerialDevice extends VexSerialDevice {
4153
3454
  })());
4154
3455
  }
4155
3456
  connect(conn) {
4156
- return new import_neverthrow5.ResultAsync(this._connect(conn));
3457
+ if (this._disposed) {
3458
+ return new import_neverthrow5.ResultAsync(Promise.resolve(this._staleLifecycleResult()));
3459
+ }
3460
+ if (this.isConnected)
3461
+ return new import_neverthrow5.ResultAsync(Promise.resolve(import_neverthrow5.ok(undefined)));
3462
+ const generation = ++this._lifecycleGeneration;
3463
+ return new import_neverthrow5.ResultAsync(this._connect(conn, generation));
4157
3464
  }
4158
- async _connect(conn) {
3465
+ async _connect(conn, generation = this._lifecycleGeneration) {
3466
+ if (!this._isLifecycleCurrent(generation)) {
3467
+ return this._staleLifecycleResult();
3468
+ }
4159
3469
  if (this.isConnected)
4160
3470
  return import_neverthrow5.ok(undefined);
4161
3471
  if (conn != null) {
4162
3472
  if (!conn.isConnected) {
4163
3473
  const opened = await conn.open();
4164
- if (opened.isErr() || opened.value !== true) {
3474
+ if (!this._isLifecycleCurrent(generation)) {
3475
+ await conn.close();
3476
+ return this._staleLifecycleResult();
3477
+ }
3478
+ if (opened.isErr() || opened.value !== "opened") {
4165
3479
  return import_neverthrow5.err(new VexIoError("failed to open the supplied connection"));
4166
3480
  }
4167
3481
  }
4168
3482
  const q = await conn.query1();
3483
+ if (!this._isLifecycleCurrent(generation)) {
3484
+ await conn.close();
3485
+ return this._staleLifecycleResult();
3486
+ }
4169
3487
  if (q.isErr()) {
4170
3488
  await conn.close();
4171
3489
  return import_neverthrow5.err(q.error);
4172
3490
  }
4173
- this.connection = conn;
3491
+ if (!this._commitConnection(conn, generation)) {
3492
+ await conn.close();
3493
+ return this._staleLifecycleResult();
3494
+ }
4174
3495
  } else {
4175
3496
  let tryIdx = 0;
3497
+ let canRequestPort = true;
3498
+ const attemptedPorts = new Set;
3499
+ const attemptedPortNames = [];
4176
3500
  while (true) {
4177
- const c = new V5SerialConnection(this.defaultSerial);
4178
- const result = await c.open(tryIdx++, true);
3501
+ if (!this._isLifecycleCurrent(generation)) {
3502
+ return this._staleLifecycleResult();
3503
+ }
3504
+ const c = this.createConnection();
3505
+ let result = await c.open(tryIdx++, false);
3506
+ if (!this._isLifecycleCurrent(generation)) {
3507
+ await c.close();
3508
+ return this._staleLifecycleResult();
3509
+ }
3510
+ if (result.isOk() && result.value === "no-port" && canRequestPort) {
3511
+ canRequestPort = false;
3512
+ result = await c.open(tryIdx, true);
3513
+ if (!this._isLifecycleCurrent(generation)) {
3514
+ await c.close();
3515
+ return this._staleLifecycleResult();
3516
+ }
3517
+ }
4179
3518
  if (result.isErr()) {
4180
3519
  await c.close();
4181
3520
  return import_neverthrow5.err(result.error);
4182
3521
  }
4183
- if (result.value === undefined) {
4184
- return import_neverthrow5.err(new VexNotConnectedError("no V5 device was found"));
3522
+ if (result.value === "no-port") {
3523
+ const attempted = attemptedPortNames.length ? `; attempted ${attemptedPortNames.join(", ")}` : "";
3524
+ return import_neverthrow5.err(new VexNotConnectedError(`no responsive V5 device was found${attempted}`));
4185
3525
  }
4186
- if (!result.value) {
3526
+ if (result.value === "busy") {
4187
3527
  await c.close();
4188
- continue;
3528
+ return import_neverthrow5.err(new VexNotConnectedError("the selected V5 serial port is busy"));
3529
+ }
3530
+ const port = c.port;
3531
+ if (port !== undefined && attemptedPorts.has(port)) {
3532
+ await c.close();
3533
+ const portName = describePort(port);
3534
+ return import_neverthrow5.err(new VexNotConnectedError(portName === undefined ? "the selected serial port did not respond as a V5 device" : `serial port ${portName} did not respond as a V5 device`));
3535
+ }
3536
+ if (port !== undefined) {
3537
+ attemptedPorts.add(port);
3538
+ const portName = describePort(port);
3539
+ if (portName !== undefined)
3540
+ attemptedPortNames.push(portName);
4189
3541
  }
4190
3542
  const q = await c.query1();
3543
+ if (!this._isLifecycleCurrent(generation)) {
3544
+ await c.close();
3545
+ return this._staleLifecycleResult();
3546
+ }
4191
3547
  if (q.isErr()) {
4192
3548
  await c.close();
4193
3549
  continue;
4194
3550
  }
4195
- this.connection = c;
3551
+ if (!this._commitConnection(c, generation)) {
3552
+ await c.close();
3553
+ return this._staleLifecycleResult();
3554
+ }
4196
3555
  break;
4197
3556
  }
4198
3557
  }
4199
- if (!this.isConnected)
3558
+ const connection = this.connection;
3559
+ if (!this._isLifecycleCurrent(generation) || connection == null) {
3560
+ return this._staleLifecycleResult();
3561
+ }
3562
+ if (!connection.isConnected)
4200
3563
  return import_neverthrow5.err(new VexNotConnectedError);
4201
- await this.doAfterConnect();
3564
+ const initialized = await this.doAfterConnect(connection, generation);
3565
+ if (initialized.isErr())
3566
+ return initialized;
4202
3567
  return import_neverthrow5.ok(undefined);
4203
3568
  }
4204
3569
  async disconnect() {
3570
+ this._lifecycleGeneration++;
3571
+ this._refreshGeneration++;
4205
3572
  this._isDisconnecting = true;
4206
3573
  const connection = this.connection;
4207
3574
  this.connection = undefined;
3575
+ this._detachDisconnectListener();
4208
3576
  try {
4209
3577
  await connection?.close();
4210
3578
  } finally {
@@ -4215,16 +3583,24 @@ class V5SerialDevice extends VexSerialDevice {
4215
3583
  this.autoReconnect = false;
4216
3584
  this.autoRefresh = false;
4217
3585
  this._disposed = true;
4218
- if (this._refreshInterval !== undefined) {
4219
- clearInterval(this._refreshInterval);
4220
- this._refreshInterval = undefined;
4221
- }
4222
3586
  await this.disconnect();
4223
3587
  }
4224
3588
  reconnect(timeout = 0) {
4225
- return new import_neverthrow5.ResultAsync(this._reconnect(timeout));
3589
+ if (this._disposed) {
3590
+ return new import_neverthrow5.ResultAsync(Promise.resolve(this._staleLifecycleResult()));
3591
+ }
3592
+ if (timeout < 0) {
3593
+ return new import_neverthrow5.ResultAsync(Promise.resolve(import_neverthrow5.err(new VexInvalidArgumentError("timeout must be non-negative"))));
3594
+ }
3595
+ if (this.isConnected)
3596
+ return new import_neverthrow5.ResultAsync(Promise.resolve(import_neverthrow5.ok(undefined)));
3597
+ const generation = this._isReconnecting ? this._lifecycleGeneration : ++this._lifecycleGeneration;
3598
+ return new import_neverthrow5.ResultAsync(this._reconnect(timeout, generation));
4226
3599
  }
4227
- async _reconnect(timeout) {
3600
+ async _reconnect(timeout, generation = this._lifecycleGeneration) {
3601
+ if (!this._isLifecycleCurrent(generation)) {
3602
+ return this._staleLifecycleResult();
3603
+ }
4228
3604
  if (this.isConnected)
4229
3605
  return import_neverthrow5.ok(undefined);
4230
3606
  if (timeout < 0) {
@@ -4240,6 +3616,9 @@ class V5SerialDevice extends VexSerialDevice {
4240
3616
  return import_neverthrow5.err(new VexNotConnectedError);
4241
3617
  }
4242
3618
  }
3619
+ if (!this._isLifecycleCurrent(generation)) {
3620
+ return this._staleLifecycleResult();
3621
+ }
4243
3622
  if (this.isConnected)
4244
3623
  return import_neverthrow5.ok(undefined);
4245
3624
  }
@@ -4248,19 +3627,30 @@ class V5SerialDevice extends VexSerialDevice {
4248
3627
  while (timeout === 0 || Date.now() < endTime) {
4249
3628
  let tryIdx = 0;
4250
3629
  while (true) {
4251
- const c = new V5SerialConnection(this.defaultSerial);
3630
+ if (!this._isLifecycleCurrent(generation)) {
3631
+ return this._staleLifecycleResult();
3632
+ }
3633
+ const c = this.createConnection();
4252
3634
  const result = await c.open(tryIdx++, false);
3635
+ if (!this._isLifecycleCurrent(generation)) {
3636
+ await c.close();
3637
+ return this._staleLifecycleResult();
3638
+ }
4253
3639
  if (result.isErr()) {
4254
3640
  await c.close();
4255
3641
  return import_neverthrow5.err(result.error);
4256
3642
  }
4257
- if (result.value === undefined)
3643
+ if (result.value === "no-port")
4258
3644
  break;
4259
- if (!result.value) {
3645
+ if (result.value === "busy") {
4260
3646
  await c.close();
4261
3647
  continue;
4262
3648
  }
4263
3649
  const status = await c.getSystemStatus(200);
3650
+ if (!this._isLifecycleCurrent(generation)) {
3651
+ await c.close();
3652
+ return this._staleLifecycleResult();
3653
+ }
4264
3654
  if (status.isErr()) {
4265
3655
  await c.close();
4266
3656
  continue;
@@ -4269,23 +3659,38 @@ class V5SerialDevice extends VexSerialDevice {
4269
3659
  await c.close();
4270
3660
  continue;
4271
3661
  }
4272
- this.connection = c;
3662
+ if (!this._commitConnection(c, generation)) {
3663
+ await c.close();
3664
+ return this._staleLifecycleResult();
3665
+ }
4273
3666
  break;
4274
3667
  }
4275
3668
  if (this.isConnected)
4276
3669
  break;
4277
3670
  const getPortCount = async () => (await this.defaultSerial.getPorts()).length;
4278
3671
  const portsCount = await getPortCount();
3672
+ if (!this._isLifecycleCurrent(generation)) {
3673
+ return this._staleLifecycleResult();
3674
+ }
4279
3675
  await sleepUntilAsync(async () => await getPortCount() !== portsCount, 1000);
3676
+ if (!this._isLifecycleCurrent(generation)) {
3677
+ return this._staleLifecycleResult();
3678
+ }
4280
3679
  }
4281
3680
  } catch (e) {
4282
3681
  return import_neverthrow5.err(toVexSerialError(e));
4283
3682
  } finally {
4284
3683
  this._isReconnecting = false;
4285
3684
  }
4286
- if (!this.isConnected)
3685
+ const connection = this.connection;
3686
+ if (!this._isLifecycleCurrent(generation) || connection == null) {
3687
+ return this._staleLifecycleResult();
3688
+ }
3689
+ if (!connection.isConnected)
4287
3690
  return import_neverthrow5.err(new VexNotConnectedError);
4288
- await this.doAfterConnect();
3691
+ const initialized = await this.doAfterConnect(connection, generation);
3692
+ if (initialized.isErr())
3693
+ return initialized;
4289
3694
  return import_neverthrow5.ok(undefined);
4290
3695
  }
4291
3696
  async waitForReconnectToFinish() {
@@ -4295,15 +3700,69 @@ class V5SerialDevice extends VexSerialDevice {
4295
3700
  return;
4296
3701
  }
4297
3702
  }
4298
- async doAfterConnect() {
4299
- if (this.connection == null)
4300
- return;
4301
- this.connection.on("disconnected", (_data) => {
4302
- if (this.autoReconnect && !this._isDisconnecting) {
4303
- this.reconnect().mapErr((e) => this.emit("error", e));
3703
+ createConnection() {
3704
+ return new V5SerialConnection(this.defaultSerial);
3705
+ }
3706
+ _isLifecycleCurrent(generation) {
3707
+ return generation === this._lifecycleGeneration && !this._disposed;
3708
+ }
3709
+ _staleLifecycleResult() {
3710
+ return import_neverthrow5.err(new VexNotConnectedError("connection attempt was superseded"));
3711
+ }
3712
+ _commitConnection(connection, generation) {
3713
+ if (!this._isLifecycleCurrent(generation))
3714
+ return false;
3715
+ this._detachDisconnectListener();
3716
+ this.connection = connection;
3717
+ return true;
3718
+ }
3719
+ _detachDisconnectListener() {
3720
+ const subscription = this._disconnectListener;
3721
+ this._disconnectListener = undefined;
3722
+ subscription?.connection.remove("disconnected", subscription.listener);
3723
+ }
3724
+ async doAfterConnect(connection, generation) {
3725
+ if (!this._isLifecycleCurrent(generation) || this.connection !== connection) {
3726
+ return this._staleLifecycleResult();
3727
+ }
3728
+ const listener = () => {
3729
+ if (this._isDisconnecting || !this._isLifecycleCurrent(generation) || this.connection !== connection) {
3730
+ return;
4304
3731
  }
4305
- });
4306
- await this.refresh();
3732
+ this._lifecycleGeneration++;
3733
+ this._refreshGeneration++;
3734
+ this.connection = undefined;
3735
+ this._detachDisconnectListener();
3736
+ this._emitSafely("disconnected", undefined);
3737
+ if (this.autoReconnect && !this._disposed) {
3738
+ this.reconnect().mapErr((error) => this._emitSafely("error", error));
3739
+ }
3740
+ };
3741
+ connection.on("disconnected", listener);
3742
+ this._disconnectListener = { connection, listener };
3743
+ const refreshed = await this.refresh();
3744
+ if (!this._isLifecycleCurrent(generation) || this.connection !== connection || !connection.isConnected) {
3745
+ return this._staleLifecycleResult();
3746
+ }
3747
+ if (refreshed.isErr()) {
3748
+ await this._discardConnection(connection, generation);
3749
+ return import_neverthrow5.err(refreshed.error);
3750
+ }
3751
+ if (!refreshed.value) {
3752
+ await this._discardConnection(connection, generation);
3753
+ return import_neverthrow5.err(new VexNotConnectedError("initial device refresh did not produce a current snapshot"));
3754
+ }
3755
+ return import_neverthrow5.ok(undefined);
3756
+ }
3757
+ async _discardConnection(connection, generation) {
3758
+ if (!this._isLifecycleCurrent(generation) || this.connection !== connection) {
3759
+ return;
3760
+ }
3761
+ this._lifecycleGeneration++;
3762
+ this._refreshGeneration++;
3763
+ this.connection = undefined;
3764
+ this._detachDisconnectListener();
3765
+ await connection.close();
4307
3766
  }
4308
3767
  refresh() {
4309
3768
  return new import_neverthrow5.ResultAsync(this._refresh());
@@ -4317,31 +3776,15 @@ class V5SerialDevice extends VexSerialDevice {
4317
3776
  this._applySnapshotIfCurrent(generation, { isAvailable: false });
4318
3777
  return import_neverthrow5.ok(false);
4319
3778
  }
4320
- const ssPacket = await conn.getSystemStatus();
4321
- if (generation !== this._refreshGeneration || this._disposed)
4322
- return import_neverthrow5.ok(false);
4323
- if (ssPacket.isErr()) {
4324
- this._applySnapshotIfCurrent(generation, { isAvailable: false });
4325
- return import_neverthrow5.ok(false);
4326
- }
4327
- const sfPacket = await conn.getSystemFlags();
4328
- if (generation !== this._refreshGeneration || this._disposed)
4329
- return import_neverthrow5.ok(false);
4330
- if (sfPacket.isErr()) {
4331
- this._applySnapshotIfCurrent(generation, { isAvailable: false });
4332
- return import_neverthrow5.ok(false);
4333
- }
4334
- const rdPacket = await conn.getRadioStatus();
3779
+ const [ssPacket, sfPacket, rdPacket, dsPacket] = await Promise.all([
3780
+ conn.getSystemStatus(),
3781
+ conn.getSystemFlags(),
3782
+ conn.getRadioStatus(),
3783
+ conn.getDeviceStatus()
3784
+ ]);
4335
3785
  if (generation !== this._refreshGeneration || this._disposed)
4336
3786
  return import_neverthrow5.ok(false);
4337
- if (rdPacket.isErr()) {
4338
- this._applySnapshotIfCurrent(generation, { isAvailable: false });
4339
- return import_neverthrow5.ok(false);
4340
- }
4341
- const dsPacket = await conn.getDeviceStatus();
4342
- if (generation !== this._refreshGeneration || this._disposed)
4343
- return import_neverthrow5.ok(false);
4344
- if (dsPacket.isErr()) {
3787
+ if (ssPacket.isErr() || sfPacket.isErr() || rdPacket.isErr() || dsPacket.isErr()) {
4345
3788
  this._applySnapshotIfCurrent(generation, { isAvailable: false });
4346
3789
  return import_neverthrow5.ok(false);
4347
3790
  }
@@ -4397,7 +3840,8 @@ class V5SerialDevice extends VexSerialDevice {
4397
3840
  },
4398
3841
  {
4399
3842
  battery: controller1Battery,
4400
- isAvailable: controller1Available
3843
+ isAvailable: controller1Available,
3844
+ isCharging: undefined
4401
3845
  }
4402
3846
  ],
4403
3847
  radio: {
@@ -4424,7 +3868,26 @@ class V5SerialDevice extends VexSerialDevice {
4424
3868
  }
4425
3869
  this.state.matchMode = snapshot.matchMode;
4426
3870
  this.state.isFieldControllerConnected = snapshot.isFieldControllerConnected;
4427
- Object.assign(this.state.brain, snapshot.brain);
3871
+ const brain = this.state.brain;
3872
+ brain.activeProgram = snapshot.brain.activeProgram;
3873
+ brain.battery.batteryPercent = snapshot.brain.battery.batteryPercent;
3874
+ brain.battery.isCharging = snapshot.brain.battery.isCharging;
3875
+ brain.button.isPressed = snapshot.brain.button.isPressed;
3876
+ brain.button.isDoublePressed = snapshot.brain.button.isDoublePressed;
3877
+ if (brain.cpu0Version.compare(snapshot.brain.cpu0Version) !== 0) {
3878
+ brain.cpu0Version = snapshot.brain.cpu0Version;
3879
+ }
3880
+ if (brain.cpu1Version.compare(snapshot.brain.cpu1Version) !== 0) {
3881
+ brain.cpu1Version = snapshot.brain.cpu1Version;
3882
+ }
3883
+ brain.isAvailable = snapshot.brain.isAvailable;
3884
+ brain.settings.isScreenReversed = snapshot.brain.settings.isScreenReversed;
3885
+ brain.settings.isWhiteTheme = snapshot.brain.settings.isWhiteTheme;
3886
+ brain.settings.usingLanguage = snapshot.brain.settings.usingLanguage;
3887
+ if (brain.systemVersion.compare(snapshot.brain.systemVersion) !== 0) {
3888
+ brain.systemVersion = snapshot.brain.systemVersion;
3889
+ }
3890
+ brain.uniqueId = snapshot.brain.uniqueId;
4428
3891
  Object.assign(this.state.controllers[0], snapshot.controllers[0]);
4429
3892
  Object.assign(this.state.controllers[1], snapshot.controllers[1]);
4430
3893
  Object.assign(this.state.radio, snapshot.radio);
@@ -4433,10 +3896,18 @@ class V5SerialDevice extends VexSerialDevice {
4433
3896
  if (device != null)
4434
3897
  next[device.port] = device;
4435
3898
  }
4436
- this.state.devices = next;
3899
+ if (!sameSmartDeviceSlots(this.state.devices, next)) {
3900
+ this.state.devices = next;
3901
+ }
4437
3902
  return true;
4438
3903
  }
4439
3904
  }
3905
+ function sameSmartDeviceSlots(left, right) {
3906
+ return left.length === right.length && left.every((device, index) => {
3907
+ const next = right[index];
3908
+ return device === next || device !== undefined && next !== undefined && device.port === next.port && device.type === next.type && device.status === next.status && device.betaversion === next.betaversion && device.version === next.version && device.bootversion === next.bootversion;
3909
+ });
3910
+ }
4440
3911
  // src/VexIniConfig.ts
4441
3912
  class BaseIniBuilder {
4442
3913
  str = "";
@@ -4445,7 +3916,7 @@ class BaseIniBuilder {
4445
3916
  `;
4446
3917
  }
4447
3918
  addComment(comment) {
4448
- this.addLine("; " + comment);
3919
+ this.addLine(comment.length === 0 ? ";" : "; " + comment);
4449
3920
  return this;
4450
3921
  }
4451
3922
  getContent() {
@@ -4463,24 +3934,28 @@ class IniSectionBuilder extends BaseIniBuilder {
4463
3934
  this.object = object;
4464
3935
  this.keyTransform = keyTransform;
4465
3936
  }
4466
- addSingleObjProperty(key, maxValueLength) {
4467
- if (this.object[key] == null || this.object[key].toString().length === 0)
3937
+ addSingleObjProperty(key, maxValueLength, omitIfEmpty = false) {
3938
+ const rawValue = this.object[key];
3939
+ if (rawValue == null)
4468
3940
  return;
3941
+ const value = rawValue.toString();
3942
+ if (omitIfEmpty && value.length === 0)
3943
+ return;
3944
+ if (/["\u0000-\u001f\u007f]/.test(value)) {
3945
+ throw new Error(`INI value for ${this.name}.${String(key)} contains an unsupported character`);
3946
+ }
4469
3947
  const formattedKey = this.keyTransform(key).padEnd(12).slice(0, 12);
4470
- const trimmedVal = this.object[key].toString().slice(0, maxValueLength);
4471
- const escapedVal = trimmedVal.replace(/["\\\u0000-\u001f\u007f]/g, (character) => `\\x${character.charCodeAt(0).toString(16).padStart(2, "0")}`);
4472
- this.addLine(`${formattedKey} = "${escapedVal}"`);
3948
+ const trimmedValue = value.slice(0, maxValueLength);
3949
+ this.addLine(`${formattedKey} = "${trimmedValue}"`);
4473
3950
  }
4474
- addObjProperty(key, maxValueLength) {
4475
- const keys = Array.isArray(key) ? key : [key];
4476
- for (const k of keys) {
4477
- this.addSingleObjProperty(k, maxValueLength);
3951
+ addObjProperty(key, maxValueLength, omitIfEmpty = false) {
3952
+ for (const k of Array.isArray(key) ? key : [key]) {
3953
+ this.addSingleObjProperty(k, maxValueLength, omitIfEmpty);
4478
3954
  }
4479
3955
  return this;
4480
3956
  }
4481
3957
  addAllObjProps(maxValueLength) {
4482
- const keys = Object.keys(this.object);
4483
- for (const k of keys) {
3958
+ for (const k of Object.keys(this.object)) {
4484
3959
  this.addSingleObjProperty(k, maxValueLength);
4485
3960
  }
4486
3961
  return this;
@@ -4493,6 +3968,11 @@ class IniFileBuilder extends BaseIniBuilder {
4493
3968
  this.str += section.getContent();
4494
3969
  return this;
4495
3970
  }
3971
+ addOptionalSection(section) {
3972
+ if (section.getContent().length === 0)
3973
+ return this;
3974
+ return this.addComment("").addSection(section);
3975
+ }
4496
3976
  }
4497
3977
 
4498
3978
  class ProgramIniConfig {
@@ -4509,33 +3989,26 @@ class ProgramIniConfig {
4509
3989
  date: "",
4510
3990
  timezone: "0"
4511
3991
  };
4512
- config = {};
3992
+ config = { 22: "adi" };
4513
3993
  controller1 = {};
4514
3994
  controller2 = {};
4515
- constructor() {
4516
- this.config = {
4517
- 22: "adi"
4518
- };
4519
- }
4520
3995
  setProgramDate(date) {
4521
- const d = date;
4522
- this.program.date = d.toISOString();
4523
- const tzo = Math.abs(d.getTimezoneOffset());
4524
- const tzh = tzo / 60 >>> 0;
4525
- const tzm = tzo - tzh * 60;
4526
- this.program.timezone = (d.getTimezoneOffset() > 0 ? "-" : "+") + this.dec2(tzh) + ":" + this.dec2(tzm);
3996
+ this.program.date = date.toISOString();
3997
+ const offset = Math.abs(date.getTimezoneOffset());
3998
+ const hours = offset / 60 >>> 0;
3999
+ const minutes = offset - hours * 60;
4000
+ this.program.timezone = (date.getTimezoneOffset() > 0 ? "-" : "+") + this.dec2(hours) + ":" + this.dec2(minutes);
4527
4001
  }
4528
4002
  createIni() {
4529
4003
  if (this.program.date.length === 0) {
4530
4004
  this.setProgramDate(new Date);
4531
4005
  }
4532
- return new IniFileBuilder().addComment("").addComment("VEX program ini file").addComment("Generated by Vex V5 Serial Protocol Library").addComment("").addSection(new IniSectionBuilder("project", this.project).addObjProperty("ide", 16)).addComment("").addSection(new IniSectionBuilder("program", this.program).addObjProperty("name", 32).addObjProperty("description", 256).addObjProperty("icon", 16).addObjProperty("iconalt", 16).addObjProperty("slot", 16)).addComment("").addSection(new IniSectionBuilder("config", this.config, (k) => "port_" + this.dec2(k)).addAllObjProps()).addComment("").addSection(new IniSectionBuilder("controller_1", this.controller1).addAllObjProps()).addComment("").addSection(new IniSectionBuilder("controller_2", this.controller2).addAllObjProps()).getContent();
4006
+ return new IniFileBuilder().addComment("").addComment("VEX program ini file").addComment("Generated by Vex V5 Serial Protocol Library").addComment("").addSection(new IniSectionBuilder("project", this.project).addObjProperty("version").addObjProperty("ide", 16).addObjProperty("file")).addComment("").addSection(new IniSectionBuilder("program", this.program).addObjProperty("version").addObjProperty("name", 32).addObjProperty("slot").addObjProperty("icon", 16).addObjProperty("iconalt", 16, true).addObjProperty("description", 256).addObjProperty("date").addObjProperty("timezone")).addComment("").addSection(new IniSectionBuilder("config", this.config, (k) => "port_" + this.dec2(k)).addAllObjProps()).addOptionalSection(new IniSectionBuilder("controller_1", this.controller1).addAllObjProps()).addOptionalSection(new IniSectionBuilder("controller_2", this.controller2).addAllObjProps()).getContent();
4533
4007
  }
4534
4008
  dec2(value) {
4535
- const str = ("00" + value.toString(10)).substr(-2, 2);
4536
- return str.toUpperCase();
4009
+ return (value % 100).toString(10).padStart(2, "0").toUpperCase();
4537
4010
  }
4538
4011
  }
4539
4012
 
4540
- //# debugId=AF2D48855258B72064756E2164756E21
4013
+ //# debugId=60E9322CE95DA15264756E2164756E21
4541
4014
  //# sourceMappingURL=index.cjs.map