@v5x/cli 0.0.23 → 0.0.24

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.js CHANGED
@@ -1656,7 +1656,7 @@ var lib_default2 = (e, t2) => new o(e, t2);
1656
1656
  var package_default = {
1657
1657
  name: "@v5x/cli",
1658
1658
  description: "modern v5 tools",
1659
- version: "0.0.23",
1659
+ version: "0.0.24",
1660
1660
  module: "dist/index.js",
1661
1661
  type: "module",
1662
1662
  sideEffects: true,
@@ -1681,6 +1681,7 @@ var package_default = {
1681
1681
  typecheck: "tsc --noEmit",
1682
1682
  lint: "oxlint src --type-aware",
1683
1683
  test: "bun test",
1684
+ "benchmark:screenshot": "bun ./benchmarks/screenshot.ts",
1684
1685
  prepack: "bun run --cwd ../serial build && bun run build"
1685
1686
  },
1686
1687
  devDependencies: {
@@ -2209,325 +2210,13 @@ var source_default = chalk;
2209
2210
  import { basename, dirname, join as join2, resolve } from "path";
2210
2211
  import { stat } from "fs/promises";
2211
2212
 
2212
- // ../serial/dist/index.js
2213
- var __defProp2 = Object.defineProperty;
2214
- var __returnValue2 = (v) => v;
2215
- function __exportSetter2(name, newValue) {
2216
- this[name] = __returnValue2.bind(null, newValue);
2217
- }
2218
- var __export2 = (target, all) => {
2219
- for (var name in all)
2220
- __defProp2(target, name, {
2221
- get: all[name],
2222
- enumerable: true,
2223
- configurable: true,
2224
- set: __exportSetter2.bind(all, name)
2225
- });
2226
- };
2227
- var USER_PROG_CHUNK_SIZE = 4096;
2228
- var USER_FLASH_USR_CODE_START = 58720256;
2229
- var USER_FLASH_MAX_FILE_SIZE = 2097152;
2230
- var FileVendor;
2231
- ((FileVendor2) => {
2232
- FileVendor2[FileVendor2["USER"] = 1] = "USER";
2233
- FileVendor2[FileVendor2["SYS"] = 15] = "SYS";
2234
- FileVendor2[FileVendor2["DEV1"] = 16] = "DEV1";
2235
- FileVendor2[FileVendor2["DEV2"] = 24] = "DEV2";
2236
- FileVendor2[FileVendor2["DEV3"] = 32] = "DEV3";
2237
- FileVendor2[FileVendor2["DEV4"] = 40] = "DEV4";
2238
- FileVendor2[FileVendor2["DEV5"] = 48] = "DEV5";
2239
- FileVendor2[FileVendor2["DEV6"] = 56] = "DEV6";
2240
- FileVendor2[FileVendor2["VEXVM"] = 64] = "VEXVM";
2241
- FileVendor2[FileVendor2["VEX"] = 240] = "VEX";
2242
- FileVendor2[FileVendor2["UNDEFINED"] = 241] = "UNDEFINED";
2243
- })(FileVendor ||= {});
2244
- var FileDownloadTarget;
2245
- ((FileDownloadTarget2) => {
2246
- FileDownloadTarget2[FileDownloadTarget2["FILE_TARGET_DDR"] = 0] = "FILE_TARGET_DDR";
2247
- FileDownloadTarget2[FileDownloadTarget2["FILE_TARGET_QSPI"] = 1] = "FILE_TARGET_QSPI";
2248
- FileDownloadTarget2[FileDownloadTarget2["FILE_TARGET_CBUF"] = 2] = "FILE_TARGET_CBUF";
2249
- FileDownloadTarget2[FileDownloadTarget2["FILE_TARGET_VBUF"] = 3] = "FILE_TARGET_VBUF";
2250
- FileDownloadTarget2[FileDownloadTarget2["FILE_TARGET_DDRC"] = 4] = "FILE_TARGET_DDRC";
2251
- FileDownloadTarget2[FileDownloadTarget2["FILE_TARGET_DDRE"] = 5] = "FILE_TARGET_DDRE";
2252
- FileDownloadTarget2[FileDownloadTarget2["FILE_TARGET_FLASH"] = 6] = "FILE_TARGET_FLASH";
2253
- FileDownloadTarget2[FileDownloadTarget2["FILE_TARGET_RADIO"] = 7] = "FILE_TARGET_RADIO";
2254
- FileDownloadTarget2[FileDownloadTarget2["FILE_TARGET_A1"] = 13] = "FILE_TARGET_A1";
2255
- FileDownloadTarget2[FileDownloadTarget2["FILE_TARGET_B1"] = 14] = "FILE_TARGET_B1";
2256
- FileDownloadTarget2[FileDownloadTarget2["FILE_TARGET_B2"] = 15] = "FILE_TARGET_B2";
2257
- })(FileDownloadTarget ||= {});
2258
- var FileInitAction;
2259
- ((FileInitAction2) => {
2260
- FileInitAction2[FileInitAction2["WRITE"] = 1] = "WRITE";
2261
- FileInitAction2[FileInitAction2["READ"] = 2] = "READ";
2262
- })(FileInitAction ||= {});
2263
- var FileInitOption;
2264
- ((FileInitOption2) => {
2265
- FileInitOption2[FileInitOption2["NONE"] = 0] = "NONE";
2266
- FileInitOption2[FileInitOption2["OVERWRITE"] = 1] = "OVERWRITE";
2267
- })(FileInitOption ||= {});
2268
- var FileLoadAction;
2269
- ((FileLoadAction2) => {
2270
- FileLoadAction2[FileLoadAction2["RUN"] = 0] = "RUN";
2271
- FileLoadAction2[FileLoadAction2["STOP"] = 128] = "STOP";
2272
- })(FileLoadAction ||= {});
2273
- var FileExitAction;
2274
- ((FileExitAction2) => {
2275
- FileExitAction2[FileExitAction2["EXIT_NONE"] = 0] = "EXIT_NONE";
2276
- FileExitAction2[FileExitAction2["EXIT_RUN"] = 1] = "EXIT_RUN";
2277
- FileExitAction2[FileExitAction2["EXIT_HALT"] = 3] = "EXIT_HALT";
2278
- })(FileExitAction ||= {});
2279
- var RadioChannelType;
2280
- ((RadioChannelType2) => {
2281
- RadioChannelType2[RadioChannelType2["PIT"] = 0] = "PIT";
2282
- RadioChannelType2[RadioChannelType2["DOWNLOAD"] = 1] = "DOWNLOAD";
2283
- })(RadioChannelType ||= {});
2284
- var AckType;
2285
- ((AckType2) => {
2286
- AckType2[AckType2["CDC2_ACK"] = 118] = "CDC2_ACK";
2287
- AckType2[AckType2["CDC2_NACK"] = 255] = "CDC2_NACK";
2288
- AckType2[AckType2["CDC2_NACK_PACKET_CRC"] = 206] = "CDC2_NACK_PACKET_CRC";
2289
- AckType2[AckType2["CDC2_NACK_CMD_LENGTH"] = 208] = "CDC2_NACK_CMD_LENGTH";
2290
- AckType2[AckType2["CDC2_NACK_SIZE"] = 209] = "CDC2_NACK_SIZE";
2291
- AckType2[AckType2["CDC2_NACK_CRC"] = 210] = "CDC2_NACK_CRC";
2292
- AckType2[AckType2["CDC2_NACK_FILE"] = 211] = "CDC2_NACK_FILE";
2293
- AckType2[AckType2["CDC2_NACK_INIT"] = 212] = "CDC2_NACK_INIT";
2294
- AckType2[AckType2["CDC2_NACK_FUNC"] = 213] = "CDC2_NACK_FUNC";
2295
- AckType2[AckType2["CDC2_NACK_ALIGN"] = 214] = "CDC2_NACK_ALIGN";
2296
- AckType2[AckType2["CDC2_NACK_ADDR"] = 215] = "CDC2_NACK_ADDR";
2297
- AckType2[AckType2["CDC2_NACK_INCOMPLETE"] = 216] = "CDC2_NACK_INCOMPLETE";
2298
- AckType2[AckType2["CDC2_NACK_DIR_INDEX"] = 217] = "CDC2_NACK_DIR_INDEX";
2299
- AckType2[AckType2["CDC2_NACK_MAX_USER_FILES"] = 218] = "CDC2_NACK_MAX_USER_FILES";
2300
- AckType2[AckType2["CDC2_NACK_FILE_EXISTS"] = 219] = "CDC2_NACK_FILE_EXISTS";
2301
- AckType2[AckType2["CDC2_NACK_FILE_SYS_FULL"] = 220] = "CDC2_NACK_FILE_SYS_FULL";
2302
- AckType2[AckType2["TIMEOUT"] = 256] = "TIMEOUT";
2303
- AckType2[AckType2["WRITE_ERROR"] = 257] = "WRITE_ERROR";
2304
- })(AckType ||= {});
2305
- var SmartDeviceType;
2306
- ((SmartDeviceType2) => {
2307
- SmartDeviceType2[SmartDeviceType2["EMPTY"] = 0] = "EMPTY";
2308
- SmartDeviceType2[SmartDeviceType2["V5_POWER"] = 1] = "V5_POWER";
2309
- SmartDeviceType2[SmartDeviceType2["MOTOR"] = 2] = "MOTOR";
2310
- SmartDeviceType2[SmartDeviceType2["LED"] = 3] = "LED";
2311
- SmartDeviceType2[SmartDeviceType2["ABS_ENCODER_SENSOR"] = 4] = "ABS_ENCODER_SENSOR";
2312
- SmartDeviceType2[SmartDeviceType2["CR_MOTOR"] = 5] = "CR_MOTOR";
2313
- SmartDeviceType2[SmartDeviceType2["IMU_SENSOR"] = 6] = "IMU_SENSOR";
2314
- SmartDeviceType2[SmartDeviceType2["DISTANCE_SENSOR"] = 7] = "DISTANCE_SENSOR";
2315
- SmartDeviceType2[SmartDeviceType2["RADIO_SENSOR"] = 8] = "RADIO_SENSOR";
2316
- SmartDeviceType2[SmartDeviceType2["CONTROLLER"] = 9] = "CONTROLLER";
2317
- SmartDeviceType2[SmartDeviceType2["BRAIN"] = 10] = "BRAIN";
2318
- SmartDeviceType2[SmartDeviceType2["VISION_SENSOR"] = 11] = "VISION_SENSOR";
2319
- SmartDeviceType2[SmartDeviceType2["ADI"] = 12] = "ADI";
2320
- SmartDeviceType2[SmartDeviceType2["PARTNER_CONTROLLER"] = 13] = "PARTNER_CONTROLLER";
2321
- SmartDeviceType2[SmartDeviceType2["BATTERY"] = 14] = "BATTERY";
2322
- SmartDeviceType2[SmartDeviceType2["SOL"] = 15] = "SOL";
2323
- SmartDeviceType2[SmartDeviceType2["OPTICAL_SENSOR"] = 16] = "OPTICAL_SENSOR";
2324
- SmartDeviceType2[SmartDeviceType2["MAGNET"] = 17] = "MAGNET";
2325
- SmartDeviceType2[SmartDeviceType2["GPS_SENSOR"] = 20] = "GPS_SENSOR";
2326
- SmartDeviceType2[SmartDeviceType2["UNDEFINED_SENSOR"] = 255] = "UNDEFINED_SENSOR";
2327
- })(SmartDeviceType ||= {});
2328
- var SerialDeviceType;
2329
- ((SerialDeviceType2) => {
2330
- SerialDeviceType2[SerialDeviceType2["V5_BRAIN"] = 1281] = "V5_BRAIN";
2331
- SerialDeviceType2[SerialDeviceType2["V5_BRAIN_DFU"] = 1282] = "V5_BRAIN_DFU";
2332
- SerialDeviceType2[SerialDeviceType2["V5_CONTROLLER"] = 1283] = "V5_CONTROLLER";
2333
- SerialDeviceType2[SerialDeviceType2["XILINX"] = 256] = "XILINX";
2334
- SerialDeviceType2[SerialDeviceType2["IQ_BRAIN"] = 2] = "IQ_BRAIN";
2335
- SerialDeviceType2[SerialDeviceType2["IQ_BRAIN2"] = 3] = "IQ_BRAIN2";
2336
- SerialDeviceType2[SerialDeviceType2["IQ_BRAIN_DFU"] = 255] = "IQ_BRAIN_DFU";
2337
- SerialDeviceType2[SerialDeviceType2["IQ2_BRAIN"] = 512] = "IQ2_BRAIN";
2338
- SerialDeviceType2[SerialDeviceType2["IQ2_BRAIN_DFU"] = 527] = "IQ2_BRAIN_DFU";
2339
- SerialDeviceType2[SerialDeviceType2["IQ2_CONTROLLER"] = 528] = "IQ2_CONTROLLER";
2340
- SerialDeviceType2[SerialDeviceType2["IQ2_CONTROLLER_DFU"] = 543] = "IQ2_CONTROLLER_DFU";
2341
- SerialDeviceType2[SerialDeviceType2["EXP_BRAIN"] = 1536] = "EXP_BRAIN";
2342
- SerialDeviceType2[SerialDeviceType2["EXP_BRAIN_DFU"] = 1551] = "EXP_BRAIN_DFU";
2343
- SerialDeviceType2[SerialDeviceType2["EXP_CONTROLLER"] = 1552] = "EXP_CONTROLLER";
2344
- SerialDeviceType2[SerialDeviceType2["EXP_CONTROLLER_DFU"] = 1567] = "EXP_CONTROLLER_DFU";
2345
- SerialDeviceType2[SerialDeviceType2["PIXY"] = 61440] = "PIXY";
2346
- SerialDeviceType2[SerialDeviceType2["PIXY_DFU"] = 12] = "PIXY_DFU";
2347
- SerialDeviceType2[SerialDeviceType2["VEXCAM"] = 1287] = "VEXCAM";
2348
- SerialDeviceType2[SerialDeviceType2["VEXCAM_DFU"] = 1288] = "VEXCAM_DFU";
2349
- SerialDeviceType2[SerialDeviceType2["SENSOR_TEST"] = 1535] = "SENSOR_TEST";
2350
- SerialDeviceType2[SerialDeviceType2["CORTEX_PROGCBL"] = 10] = "CORTEX_PROGCBL";
2351
- SerialDeviceType2[SerialDeviceType2["CORTEX_BRAIN"] = 11] = "CORTEX_BRAIN";
2352
- SerialDeviceType2[SerialDeviceType2["CORTEX_JOYSTK"] = 12] = "CORTEX_JOYSTK";
2353
- SerialDeviceType2[SerialDeviceType2["VEXNET_KEY_20"] = 7] = "VEXNET_KEY_20";
2354
- SerialDeviceType2[SerialDeviceType2["VEXNET_KEY_10"] = 9587] = "VEXNET_KEY_10";
2355
- SerialDeviceType2[SerialDeviceType2["PROLIFIC"] = 8963] = "PROLIFIC";
2356
- })(SerialDeviceType ||= {});
2357
- var SelectDashScreen;
2358
- ((SelectDashScreen2) => {
2359
- SelectDashScreen2[SelectDashScreen2["HOME"] = 0] = "HOME";
2360
- SelectDashScreen2[SelectDashScreen2["BATTERY"] = 1] = "BATTERY";
2361
- SelectDashScreen2[SelectDashScreen2["LED"] = 3] = "LED";
2362
- SelectDashScreen2[SelectDashScreen2["MATCH_CONFIG"] = 4] = "MATCH_CONFIG";
2363
- SelectDashScreen2[SelectDashScreen2["MATCH_CONFIG_MORE"] = 5] = "MATCH_CONFIG_MORE";
2364
- SelectDashScreen2[SelectDashScreen2["WIRING"] = 6] = "WIRING";
2365
- SelectDashScreen2[SelectDashScreen2["RADIO"] = 8] = "RADIO";
2366
- SelectDashScreen2[SelectDashScreen2["BRAIN"] = 10] = "BRAIN";
2367
- SelectDashScreen2[SelectDashScreen2["RUN_PROGRAM"] = 13] = "RUN_PROGRAM";
2368
- SelectDashScreen2[SelectDashScreen2["DRIVE_PROGRAM_CONTROL_LEFT_MAPPING"] = 14] = "DRIVE_PROGRAM_CONTROL_LEFT_MAPPING";
2369
- SelectDashScreen2[SelectDashScreen2["DRIVE_PROGRAM_MENU"] = 15] = "DRIVE_PROGRAM_MENU";
2370
- SelectDashScreen2[SelectDashScreen2["DEVICES"] = 16] = "DEVICES";
2371
- SelectDashScreen2[SelectDashScreen2["USER_PROGRAM_FOLDER"] = 17] = "USER_PROGRAM_FOLDER";
2372
- SelectDashScreen2[SelectDashScreen2["VEX_PROGRAM_FOLDER"] = 18] = "VEX_PROGRAM_FOLDER";
2373
- SelectDashScreen2[SelectDashScreen2["SETTINGS"] = 19] = "SETTINGS";
2374
- SelectDashScreen2[SelectDashScreen2["SCARY_CONFIGURATION"] = 20] = "SCARY_CONFIGURATION";
2375
- SelectDashScreen2[SelectDashScreen2["LANGUAGE"] = 21] = "LANGUAGE";
2376
- SelectDashScreen2[SelectDashScreen2["DRIVE_MOTOR_CONFIG"] = 22] = "DRIVE_MOTOR_CONFIG";
2377
- SelectDashScreen2[SelectDashScreen2["PROGRAM_MENU"] = 24] = "PROGRAM_MENU";
2378
- SelectDashScreen2[SelectDashScreen2["SHUTDOWN"] = 25] = "SHUTDOWN";
2379
- SelectDashScreen2[SelectDashScreen2["CONTROLLER_2_MAPPING"] = 26] = "CONTROLLER_2_MAPPING";
2380
- SelectDashScreen2[SelectDashScreen2["SCARY_CONFIGURATION_MORE"] = 27] = "SCARY_CONFIGURATION_MORE";
2381
- SelectDashScreen2[SelectDashScreen2["CONFIRM_XX"] = 28] = "CONFIRM_XX";
2382
- SelectDashScreen2[SelectDashScreen2["CONTROLLER_1_MAPPING"] = 29] = "CONTROLLER_1_MAPPING";
2383
- SelectDashScreen2[SelectDashScreen2["DRIVE_PROGRAM_CONTROL_DUAL_MAPPING"] = 30] = "DRIVE_PROGRAM_CONTROL_DUAL_MAPPING";
2384
- SelectDashScreen2[SelectDashScreen2["DRIVE_PROGRAM_CONTROL_SPLIT_MAPPING"] = 31] = "DRIVE_PROGRAM_CONTROL_SPLIT_MAPPING";
2385
- SelectDashScreen2[SelectDashScreen2["DRIVE_PROGRAM_CONTROL_RIGHT_MAPPING"] = 32] = "DRIVE_PROGRAM_CONTROL_RIGHT_MAPPING";
2386
- SelectDashScreen2[SelectDashScreen2["MATCH_2_4_PLAYERS"] = 33] = "MATCH_2_4_PLAYERS";
2387
- SelectDashScreen2[SelectDashScreen2["EVENT_LOG"] = 34] = "EVENT_LOG";
2388
- SelectDashScreen2[SelectDashScreen2["USER_PROGRAM_WIRING"] = 40] = "USER_PROGRAM_WIRING";
2389
- SelectDashScreen2[SelectDashScreen2["CLAWBOT_PROGRAM_MENU"] = 41] = "CLAWBOT_PROGRAM_MENU";
2390
- SelectDashScreen2[SelectDashScreen2["ABOUT"] = 42] = "ABOUT";
2391
- SelectDashScreen2[SelectDashScreen2["LANGUAGE_MORE"] = 43] = "LANGUAGE_MORE";
2392
- SelectDashScreen2[SelectDashScreen2["OBJECT_COLOR"] = 45] = "OBJECT_COLOR";
2393
- SelectDashScreen2[SelectDashScreen2["SIGNATURE_ID"] = 46] = "SIGNATURE_ID";
2394
- SelectDashScreen2[SelectDashScreen2["LOG_DATA"] = 47] = "LOG_DATA";
2395
- })(SelectDashScreen ||= {});
2396
-
2397
- class VexSerialError extends Error {
2398
- kind;
2399
- ackType = undefined;
2400
- constructor(kind, message) {
2401
- super(message);
2402
- this.name = "VexSerialError";
2403
- this.kind = kind;
2404
- }
2405
- }
2406
-
2407
- class VexNotConnectedError extends VexSerialError {
2408
- constructor(message = "no connection to a V5 device") {
2409
- super("not-connected", message);
2410
- this.name = "VexNotConnectedError";
2411
- }
2412
- }
2413
-
2414
- class VexInvalidArgumentError extends VexSerialError {
2415
- constructor(message) {
2416
- super("invalid-argument", message);
2417
- this.name = "VexInvalidArgumentError";
2418
- }
2419
- }
2420
-
2421
- class VexProtocolError extends VexSerialError {
2422
- ackType;
2423
- constructor(message, ackType) {
2424
- super("protocol", message);
2425
- this.name = "VexProtocolError";
2426
- this.ackType = ackType;
2427
- }
2428
- }
2429
-
2430
- class VexTransferError extends VexSerialError {
2431
- constructor(message) {
2432
- super("transfer", message);
2433
- this.name = "VexTransferError";
2434
- }
2435
- }
2436
-
2437
- class VexDownloadError extends VexSerialError {
2438
- constructor(message) {
2439
- super("download", message);
2440
- this.name = "VexDownloadError";
2441
- }
2442
- }
2443
-
2444
- class VexFirmwareError extends VexSerialError {
2445
- constructor(message) {
2446
- super("firmware", message);
2447
- this.name = "VexFirmwareError";
2448
- }
2449
- }
2450
-
2451
- class VexIoError extends VexSerialError {
2452
- constructor(message) {
2453
- super("io", message);
2454
- this.name = "VexIoError";
2455
- }
2456
- }
2457
- function toVexSerialError(error, fallback = "io") {
2458
- if (error instanceof VexSerialError)
2459
- return error;
2460
- const message = error instanceof Error ? error.message : String(error);
2461
- return new VexSerialError(fallback, message);
2462
- }
2463
-
2464
- class VexEventEmitter {
2465
- handlerMap;
2466
- constructor() {
2467
- this.handlerMap = new Map;
2468
- }
2469
- on(eventName, listener) {
2470
- let listeners = this.handlerMap.get(eventName);
2471
- listeners ??= [];
2472
- listeners.push(listener);
2473
- this.handlerMap.set(eventName, listeners);
2474
- }
2475
- remove(eventName, listener) {
2476
- let listeners = this.handlerMap.get(eventName);
2477
- listeners ??= [];
2478
- const index = listeners.indexOf(listener);
2479
- if (index > -1) {
2480
- listeners.splice(index, 1);
2481
- }
2482
- this.handlerMap.set(eventName, listeners);
2483
- }
2484
- emit(eventName, data) {
2485
- const errors = [];
2486
- for (const callback of [...this.handlerMap.get(eventName) ?? []]) {
2487
- try {
2488
- callback(data);
2489
- } catch (error) {
2490
- errors.push(error);
2491
- }
2492
- }
2493
- if (errors.length === 1)
2494
- throw errors[0];
2495
- if (errors.length > 1) {
2496
- throw new AggregateError(errors, `listeners for ${String(eventName)} failed`);
2497
- }
2498
- }
2499
- clearListeners() {
2500
- this.handlerMap.clear();
2501
- }
2502
- }
2503
-
2504
- class VexEventTarget {
2505
- emitter;
2506
- constructor() {
2507
- this.emitter = new VexEventEmitter;
2508
- }
2509
- emit(eventName, data) {
2510
- this.emitter.emit(this.normalizeEventName(eventName), data);
2511
- }
2512
- on(eventName, listener) {
2513
- this.emitter.on(this.normalizeEventName(eventName), listener);
2514
- }
2515
- remove(eventName, listener) {
2516
- this.emitter.remove(this.normalizeEventName(eventName), listener);
2517
- }
2518
- clearListeners() {
2519
- this.emitter.clearListeners();
2520
- }
2521
- normalizeEventName(eventName) {
2522
- return String(eventName);
2523
- }
2524
- }
2213
+ // ../../node_modules/.bun/neverthrow@8.2.0/node_modules/neverthrow/dist/index.es.js
2525
2214
  var defaultErrorConfig = {
2526
2215
  withStackTrace: false
2527
2216
  };
2528
- var createNeverThrowError = (message, result, config2 = defaultErrorConfig) => {
2217
+ var createNeverThrowError = (message, result, config = defaultErrorConfig) => {
2529
2218
  const data = result.isOk() ? { type: "Ok", value: result.value } : { type: "Err", value: result.error };
2530
- const maybeStack = config2.withStackTrace ? new Error().stack : undefined;
2219
+ const maybeStack = config.withStackTrace ? new Error().stack : undefined;
2531
2220
  return {
2532
2221
  data,
2533
2222
  message,
@@ -2655,7 +2344,6 @@ function __asyncValues(o2) {
2655
2344
  }, reject);
2656
2345
  }
2657
2346
  }
2658
-
2659
2347
  class ResultAsync {
2660
2348
  constructor(res) {
2661
2349
  this._promise = res;
@@ -2767,7 +2455,7 @@ class ResultAsync {
2767
2455
  return this._promise.then(successCallback, failureCallback);
2768
2456
  }
2769
2457
  [Symbol.asyncIterator]() {
2770
- return __asyncGenerator(this, arguments, function* _a2() {
2458
+ return __asyncGenerator(this, arguments, function* _a() {
2771
2459
  const result = yield __await(this._promise);
2772
2460
  if (result.isErr()) {
2773
2461
  yield yield __await(errAsync(result.error));
@@ -2837,7 +2525,6 @@ function ok(value) {
2837
2525
  function err(err2) {
2838
2526
  return new Err(err2);
2839
2527
  }
2840
-
2841
2528
  class Ok {
2842
2529
  constructor(value) {
2843
2530
  this.value = value;
@@ -2896,83 +2583,388 @@ class Ok {
2896
2583
  _unsafeUnwrap(_) {
2897
2584
  return this.value;
2898
2585
  }
2899
- _unsafeUnwrapErr(config2) {
2900
- throw createNeverThrowError("Called `_unsafeUnwrapErr` on an Ok", this, config2);
2586
+ _unsafeUnwrapErr(config) {
2587
+ throw createNeverThrowError("Called `_unsafeUnwrapErr` on an Ok", this, config);
2901
2588
  }
2902
2589
  *[Symbol.iterator]() {
2903
2590
  return this.value;
2904
2591
  }
2905
2592
  }
2906
2593
 
2907
- class Err {
2908
- constructor(error) {
2909
- this.error = error;
2910
- }
2911
- isOk() {
2912
- return false;
2913
- }
2914
- isErr() {
2915
- return !this.isOk();
2594
+ class Err {
2595
+ constructor(error) {
2596
+ this.error = error;
2597
+ }
2598
+ isOk() {
2599
+ return false;
2600
+ }
2601
+ isErr() {
2602
+ return !this.isOk();
2603
+ }
2604
+ map(_f) {
2605
+ return err(this.error);
2606
+ }
2607
+ mapErr(f) {
2608
+ return err(f(this.error));
2609
+ }
2610
+ andThrough(_f) {
2611
+ return err(this.error);
2612
+ }
2613
+ andTee(_f) {
2614
+ return err(this.error);
2615
+ }
2616
+ orTee(f) {
2617
+ try {
2618
+ f(this.error);
2619
+ } catch (e) {}
2620
+ return err(this.error);
2621
+ }
2622
+ andThen(_f) {
2623
+ return err(this.error);
2624
+ }
2625
+ orElse(f) {
2626
+ return f(this.error);
2627
+ }
2628
+ asyncAndThen(_f) {
2629
+ return errAsync(this.error);
2630
+ }
2631
+ asyncAndThrough(_f) {
2632
+ return errAsync(this.error);
2633
+ }
2634
+ asyncMap(_f) {
2635
+ return errAsync(this.error);
2636
+ }
2637
+ unwrapOr(v) {
2638
+ return v;
2639
+ }
2640
+ match(_ok, err2) {
2641
+ return err2(this.error);
2642
+ }
2643
+ safeUnwrap() {
2644
+ const error = this.error;
2645
+ return function* () {
2646
+ yield err(error);
2647
+ throw new Error("Do not use this generator out of `safeTry`");
2648
+ }();
2649
+ }
2650
+ _unsafeUnwrap(config) {
2651
+ throw createNeverThrowError("Called `_unsafeUnwrap` on an Err", this, config);
2652
+ }
2653
+ _unsafeUnwrapErr(_) {
2654
+ return this.error;
2655
+ }
2656
+ *[Symbol.iterator]() {
2657
+ const self = this;
2658
+ yield self;
2659
+ return self;
2660
+ }
2661
+ }
2662
+ var fromThrowable = Result.fromThrowable;
2663
+
2664
+ // ../serial/dist/index.js
2665
+ var USER_PROG_CHUNK_SIZE = 4096;
2666
+ var USER_FLASH_USR_CODE_START = 58720256;
2667
+ var USER_FLASH_MAX_FILE_SIZE = 2097152;
2668
+ var FileVendor;
2669
+ ((FileVendor2) => {
2670
+ FileVendor2[FileVendor2["USER"] = 1] = "USER";
2671
+ FileVendor2[FileVendor2["SYS"] = 15] = "SYS";
2672
+ FileVendor2[FileVendor2["DEV1"] = 16] = "DEV1";
2673
+ FileVendor2[FileVendor2["DEV2"] = 24] = "DEV2";
2674
+ FileVendor2[FileVendor2["DEV3"] = 32] = "DEV3";
2675
+ FileVendor2[FileVendor2["DEV4"] = 40] = "DEV4";
2676
+ FileVendor2[FileVendor2["DEV5"] = 48] = "DEV5";
2677
+ FileVendor2[FileVendor2["DEV6"] = 56] = "DEV6";
2678
+ FileVendor2[FileVendor2["VEXVM"] = 64] = "VEXVM";
2679
+ FileVendor2[FileVendor2["VEX"] = 240] = "VEX";
2680
+ FileVendor2[FileVendor2["UNDEFINED"] = 241] = "UNDEFINED";
2681
+ })(FileVendor ||= {});
2682
+ var FileDownloadTarget;
2683
+ ((FileDownloadTarget2) => {
2684
+ FileDownloadTarget2[FileDownloadTarget2["FILE_TARGET_DDR"] = 0] = "FILE_TARGET_DDR";
2685
+ FileDownloadTarget2[FileDownloadTarget2["FILE_TARGET_QSPI"] = 1] = "FILE_TARGET_QSPI";
2686
+ FileDownloadTarget2[FileDownloadTarget2["FILE_TARGET_CBUF"] = 2] = "FILE_TARGET_CBUF";
2687
+ FileDownloadTarget2[FileDownloadTarget2["FILE_TARGET_VBUF"] = 3] = "FILE_TARGET_VBUF";
2688
+ FileDownloadTarget2[FileDownloadTarget2["FILE_TARGET_DDRC"] = 4] = "FILE_TARGET_DDRC";
2689
+ FileDownloadTarget2[FileDownloadTarget2["FILE_TARGET_DDRE"] = 5] = "FILE_TARGET_DDRE";
2690
+ FileDownloadTarget2[FileDownloadTarget2["FILE_TARGET_FLASH"] = 6] = "FILE_TARGET_FLASH";
2691
+ FileDownloadTarget2[FileDownloadTarget2["FILE_TARGET_RADIO"] = 7] = "FILE_TARGET_RADIO";
2692
+ FileDownloadTarget2[FileDownloadTarget2["FILE_TARGET_A1"] = 13] = "FILE_TARGET_A1";
2693
+ FileDownloadTarget2[FileDownloadTarget2["FILE_TARGET_B1"] = 14] = "FILE_TARGET_B1";
2694
+ FileDownloadTarget2[FileDownloadTarget2["FILE_TARGET_B2"] = 15] = "FILE_TARGET_B2";
2695
+ })(FileDownloadTarget ||= {});
2696
+ var FileInitAction;
2697
+ ((FileInitAction2) => {
2698
+ FileInitAction2[FileInitAction2["WRITE"] = 1] = "WRITE";
2699
+ FileInitAction2[FileInitAction2["READ"] = 2] = "READ";
2700
+ })(FileInitAction ||= {});
2701
+ var FileInitOption;
2702
+ ((FileInitOption2) => {
2703
+ FileInitOption2[FileInitOption2["NONE"] = 0] = "NONE";
2704
+ FileInitOption2[FileInitOption2["OVERWRITE"] = 1] = "OVERWRITE";
2705
+ })(FileInitOption ||= {});
2706
+ var FileLoadAction;
2707
+ ((FileLoadAction2) => {
2708
+ FileLoadAction2[FileLoadAction2["RUN"] = 0] = "RUN";
2709
+ FileLoadAction2[FileLoadAction2["STOP"] = 128] = "STOP";
2710
+ })(FileLoadAction ||= {});
2711
+ var FileExitAction;
2712
+ ((FileExitAction2) => {
2713
+ FileExitAction2[FileExitAction2["EXIT_NONE"] = 0] = "EXIT_NONE";
2714
+ FileExitAction2[FileExitAction2["EXIT_RUN"] = 1] = "EXIT_RUN";
2715
+ FileExitAction2[FileExitAction2["EXIT_HALT"] = 3] = "EXIT_HALT";
2716
+ })(FileExitAction ||= {});
2717
+ var RadioChannelType;
2718
+ ((RadioChannelType2) => {
2719
+ RadioChannelType2[RadioChannelType2["PIT"] = 0] = "PIT";
2720
+ RadioChannelType2[RadioChannelType2["DOWNLOAD"] = 1] = "DOWNLOAD";
2721
+ })(RadioChannelType ||= {});
2722
+ var AckType;
2723
+ ((AckType2) => {
2724
+ AckType2[AckType2["CDC2_ACK"] = 118] = "CDC2_ACK";
2725
+ AckType2[AckType2["CDC2_NACK"] = 255] = "CDC2_NACK";
2726
+ AckType2[AckType2["CDC2_NACK_PACKET_CRC"] = 206] = "CDC2_NACK_PACKET_CRC";
2727
+ AckType2[AckType2["CDC2_NACK_CMD_LENGTH"] = 208] = "CDC2_NACK_CMD_LENGTH";
2728
+ AckType2[AckType2["CDC2_NACK_SIZE"] = 209] = "CDC2_NACK_SIZE";
2729
+ AckType2[AckType2["CDC2_NACK_CRC"] = 210] = "CDC2_NACK_CRC";
2730
+ AckType2[AckType2["CDC2_NACK_FILE"] = 211] = "CDC2_NACK_FILE";
2731
+ AckType2[AckType2["CDC2_NACK_INIT"] = 212] = "CDC2_NACK_INIT";
2732
+ AckType2[AckType2["CDC2_NACK_FUNC"] = 213] = "CDC2_NACK_FUNC";
2733
+ AckType2[AckType2["CDC2_NACK_ALIGN"] = 214] = "CDC2_NACK_ALIGN";
2734
+ AckType2[AckType2["CDC2_NACK_ADDR"] = 215] = "CDC2_NACK_ADDR";
2735
+ AckType2[AckType2["CDC2_NACK_INCOMPLETE"] = 216] = "CDC2_NACK_INCOMPLETE";
2736
+ AckType2[AckType2["CDC2_NACK_DIR_INDEX"] = 217] = "CDC2_NACK_DIR_INDEX";
2737
+ AckType2[AckType2["CDC2_NACK_MAX_USER_FILES"] = 218] = "CDC2_NACK_MAX_USER_FILES";
2738
+ AckType2[AckType2["CDC2_NACK_FILE_EXISTS"] = 219] = "CDC2_NACK_FILE_EXISTS";
2739
+ AckType2[AckType2["CDC2_NACK_FILE_SYS_FULL"] = 220] = "CDC2_NACK_FILE_SYS_FULL";
2740
+ AckType2[AckType2["TIMEOUT"] = 256] = "TIMEOUT";
2741
+ AckType2[AckType2["WRITE_ERROR"] = 257] = "WRITE_ERROR";
2742
+ AckType2[AckType2["NOT_CONNECTED"] = 258] = "NOT_CONNECTED";
2743
+ })(AckType ||= {});
2744
+ var SmartDeviceType;
2745
+ ((SmartDeviceType2) => {
2746
+ SmartDeviceType2[SmartDeviceType2["EMPTY"] = 0] = "EMPTY";
2747
+ SmartDeviceType2[SmartDeviceType2["V5_POWER"] = 1] = "V5_POWER";
2748
+ SmartDeviceType2[SmartDeviceType2["MOTOR"] = 2] = "MOTOR";
2749
+ SmartDeviceType2[SmartDeviceType2["LED"] = 3] = "LED";
2750
+ SmartDeviceType2[SmartDeviceType2["ABS_ENCODER_SENSOR"] = 4] = "ABS_ENCODER_SENSOR";
2751
+ SmartDeviceType2[SmartDeviceType2["CR_MOTOR"] = 5] = "CR_MOTOR";
2752
+ SmartDeviceType2[SmartDeviceType2["IMU_SENSOR"] = 6] = "IMU_SENSOR";
2753
+ SmartDeviceType2[SmartDeviceType2["DISTANCE_SENSOR"] = 7] = "DISTANCE_SENSOR";
2754
+ SmartDeviceType2[SmartDeviceType2["RADIO_SENSOR"] = 8] = "RADIO_SENSOR";
2755
+ SmartDeviceType2[SmartDeviceType2["CONTROLLER"] = 9] = "CONTROLLER";
2756
+ SmartDeviceType2[SmartDeviceType2["BRAIN"] = 10] = "BRAIN";
2757
+ SmartDeviceType2[SmartDeviceType2["VISION_SENSOR"] = 11] = "VISION_SENSOR";
2758
+ SmartDeviceType2[SmartDeviceType2["ADI"] = 12] = "ADI";
2759
+ SmartDeviceType2[SmartDeviceType2["PARTNER_CONTROLLER"] = 13] = "PARTNER_CONTROLLER";
2760
+ SmartDeviceType2[SmartDeviceType2["BATTERY"] = 14] = "BATTERY";
2761
+ SmartDeviceType2[SmartDeviceType2["SOL"] = 15] = "SOL";
2762
+ SmartDeviceType2[SmartDeviceType2["OPTICAL_SENSOR"] = 16] = "OPTICAL_SENSOR";
2763
+ SmartDeviceType2[SmartDeviceType2["MAGNET"] = 17] = "MAGNET";
2764
+ SmartDeviceType2[SmartDeviceType2["GPS_SENSOR"] = 20] = "GPS_SENSOR";
2765
+ SmartDeviceType2[SmartDeviceType2["UNDEFINED_SENSOR"] = 255] = "UNDEFINED_SENSOR";
2766
+ })(SmartDeviceType ||= {});
2767
+ var SerialDeviceType;
2768
+ ((SerialDeviceType2) => {
2769
+ SerialDeviceType2[SerialDeviceType2["V5_BRAIN"] = 1281] = "V5_BRAIN";
2770
+ SerialDeviceType2[SerialDeviceType2["V5_BRAIN_DFU"] = 1282] = "V5_BRAIN_DFU";
2771
+ SerialDeviceType2[SerialDeviceType2["V5_CONTROLLER"] = 1283] = "V5_CONTROLLER";
2772
+ SerialDeviceType2[SerialDeviceType2["XILINX"] = 256] = "XILINX";
2773
+ SerialDeviceType2[SerialDeviceType2["IQ_BRAIN"] = 2] = "IQ_BRAIN";
2774
+ SerialDeviceType2[SerialDeviceType2["IQ_BRAIN2"] = 3] = "IQ_BRAIN2";
2775
+ SerialDeviceType2[SerialDeviceType2["IQ_BRAIN_DFU"] = 255] = "IQ_BRAIN_DFU";
2776
+ SerialDeviceType2[SerialDeviceType2["IQ2_BRAIN"] = 512] = "IQ2_BRAIN";
2777
+ SerialDeviceType2[SerialDeviceType2["IQ2_BRAIN_DFU"] = 527] = "IQ2_BRAIN_DFU";
2778
+ SerialDeviceType2[SerialDeviceType2["IQ2_CONTROLLER"] = 528] = "IQ2_CONTROLLER";
2779
+ SerialDeviceType2[SerialDeviceType2["IQ2_CONTROLLER_DFU"] = 543] = "IQ2_CONTROLLER_DFU";
2780
+ SerialDeviceType2[SerialDeviceType2["EXP_BRAIN"] = 1536] = "EXP_BRAIN";
2781
+ SerialDeviceType2[SerialDeviceType2["EXP_BRAIN_DFU"] = 1551] = "EXP_BRAIN_DFU";
2782
+ SerialDeviceType2[SerialDeviceType2["EXP_CONTROLLER"] = 1552] = "EXP_CONTROLLER";
2783
+ SerialDeviceType2[SerialDeviceType2["EXP_CONTROLLER_DFU"] = 1567] = "EXP_CONTROLLER_DFU";
2784
+ SerialDeviceType2[SerialDeviceType2["PIXY"] = 61440] = "PIXY";
2785
+ SerialDeviceType2[SerialDeviceType2["PIXY_DFU"] = 12] = "PIXY_DFU";
2786
+ SerialDeviceType2[SerialDeviceType2["VEXCAM"] = 1287] = "VEXCAM";
2787
+ SerialDeviceType2[SerialDeviceType2["VEXCAM_DFU"] = 1288] = "VEXCAM_DFU";
2788
+ SerialDeviceType2[SerialDeviceType2["SENSOR_TEST"] = 1535] = "SENSOR_TEST";
2789
+ SerialDeviceType2[SerialDeviceType2["CORTEX_PROGCBL"] = 10] = "CORTEX_PROGCBL";
2790
+ SerialDeviceType2[SerialDeviceType2["CORTEX_BRAIN"] = 11] = "CORTEX_BRAIN";
2791
+ SerialDeviceType2[SerialDeviceType2["CORTEX_JOYSTK"] = 12] = "CORTEX_JOYSTK";
2792
+ SerialDeviceType2[SerialDeviceType2["VEXNET_KEY_20"] = 7] = "VEXNET_KEY_20";
2793
+ SerialDeviceType2[SerialDeviceType2["VEXNET_KEY_10"] = 9587] = "VEXNET_KEY_10";
2794
+ SerialDeviceType2[SerialDeviceType2["PROLIFIC"] = 8963] = "PROLIFIC";
2795
+ })(SerialDeviceType ||= {});
2796
+ var SelectDashScreen;
2797
+ ((SelectDashScreen2) => {
2798
+ SelectDashScreen2[SelectDashScreen2["HOME"] = 0] = "HOME";
2799
+ SelectDashScreen2[SelectDashScreen2["BATTERY"] = 1] = "BATTERY";
2800
+ SelectDashScreen2[SelectDashScreen2["LED"] = 3] = "LED";
2801
+ SelectDashScreen2[SelectDashScreen2["MATCH_CONFIG"] = 4] = "MATCH_CONFIG";
2802
+ SelectDashScreen2[SelectDashScreen2["MATCH_CONFIG_MORE"] = 5] = "MATCH_CONFIG_MORE";
2803
+ SelectDashScreen2[SelectDashScreen2["WIRING"] = 6] = "WIRING";
2804
+ SelectDashScreen2[SelectDashScreen2["RADIO"] = 8] = "RADIO";
2805
+ SelectDashScreen2[SelectDashScreen2["BRAIN"] = 10] = "BRAIN";
2806
+ SelectDashScreen2[SelectDashScreen2["RUN_PROGRAM"] = 13] = "RUN_PROGRAM";
2807
+ SelectDashScreen2[SelectDashScreen2["DRIVE_PROGRAM_CONTROL_LEFT_MAPPING"] = 14] = "DRIVE_PROGRAM_CONTROL_LEFT_MAPPING";
2808
+ SelectDashScreen2[SelectDashScreen2["DRIVE_PROGRAM_MENU"] = 15] = "DRIVE_PROGRAM_MENU";
2809
+ SelectDashScreen2[SelectDashScreen2["DEVICES"] = 16] = "DEVICES";
2810
+ SelectDashScreen2[SelectDashScreen2["USER_PROGRAM_FOLDER"] = 17] = "USER_PROGRAM_FOLDER";
2811
+ SelectDashScreen2[SelectDashScreen2["VEX_PROGRAM_FOLDER"] = 18] = "VEX_PROGRAM_FOLDER";
2812
+ SelectDashScreen2[SelectDashScreen2["SETTINGS"] = 19] = "SETTINGS";
2813
+ SelectDashScreen2[SelectDashScreen2["SCARY_CONFIGURATION"] = 20] = "SCARY_CONFIGURATION";
2814
+ SelectDashScreen2[SelectDashScreen2["LANGUAGE"] = 21] = "LANGUAGE";
2815
+ SelectDashScreen2[SelectDashScreen2["DRIVE_MOTOR_CONFIG"] = 22] = "DRIVE_MOTOR_CONFIG";
2816
+ SelectDashScreen2[SelectDashScreen2["PROGRAM_MENU"] = 24] = "PROGRAM_MENU";
2817
+ SelectDashScreen2[SelectDashScreen2["SHUTDOWN"] = 25] = "SHUTDOWN";
2818
+ SelectDashScreen2[SelectDashScreen2["CONTROLLER_2_MAPPING"] = 26] = "CONTROLLER_2_MAPPING";
2819
+ SelectDashScreen2[SelectDashScreen2["SCARY_CONFIGURATION_MORE"] = 27] = "SCARY_CONFIGURATION_MORE";
2820
+ SelectDashScreen2[SelectDashScreen2["CONFIRM_XX"] = 28] = "CONFIRM_XX";
2821
+ SelectDashScreen2[SelectDashScreen2["CONTROLLER_1_MAPPING"] = 29] = "CONTROLLER_1_MAPPING";
2822
+ SelectDashScreen2[SelectDashScreen2["DRIVE_PROGRAM_CONTROL_DUAL_MAPPING"] = 30] = "DRIVE_PROGRAM_CONTROL_DUAL_MAPPING";
2823
+ SelectDashScreen2[SelectDashScreen2["DRIVE_PROGRAM_CONTROL_SPLIT_MAPPING"] = 31] = "DRIVE_PROGRAM_CONTROL_SPLIT_MAPPING";
2824
+ SelectDashScreen2[SelectDashScreen2["DRIVE_PROGRAM_CONTROL_RIGHT_MAPPING"] = 32] = "DRIVE_PROGRAM_CONTROL_RIGHT_MAPPING";
2825
+ SelectDashScreen2[SelectDashScreen2["MATCH_2_4_PLAYERS"] = 33] = "MATCH_2_4_PLAYERS";
2826
+ SelectDashScreen2[SelectDashScreen2["EVENT_LOG"] = 34] = "EVENT_LOG";
2827
+ SelectDashScreen2[SelectDashScreen2["USER_PROGRAM_WIRING"] = 40] = "USER_PROGRAM_WIRING";
2828
+ SelectDashScreen2[SelectDashScreen2["CLAWBOT_PROGRAM_MENU"] = 41] = "CLAWBOT_PROGRAM_MENU";
2829
+ SelectDashScreen2[SelectDashScreen2["ABOUT"] = 42] = "ABOUT";
2830
+ SelectDashScreen2[SelectDashScreen2["LANGUAGE_MORE"] = 43] = "LANGUAGE_MORE";
2831
+ SelectDashScreen2[SelectDashScreen2["OBJECT_COLOR"] = 45] = "OBJECT_COLOR";
2832
+ SelectDashScreen2[SelectDashScreen2["SIGNATURE_ID"] = 46] = "SIGNATURE_ID";
2833
+ SelectDashScreen2[SelectDashScreen2["LOG_DATA"] = 47] = "LOG_DATA";
2834
+ })(SelectDashScreen ||= {});
2835
+
2836
+ class VexSerialError extends Error {
2837
+ kind;
2838
+ ackType = undefined;
2839
+ constructor(kind, message) {
2840
+ super(message);
2841
+ this.name = "VexSerialError";
2842
+ this.kind = kind;
2843
+ }
2844
+ }
2845
+
2846
+ class VexNotConnectedError extends VexSerialError {
2847
+ constructor(message = "no connection to a V5 device") {
2848
+ super("not-connected", message);
2849
+ this.name = "VexNotConnectedError";
2916
2850
  }
2917
- map(_f) {
2918
- return err(this.error);
2851
+ }
2852
+
2853
+ class VexInvalidArgumentError extends VexSerialError {
2854
+ constructor(message) {
2855
+ super("invalid-argument", message);
2856
+ this.name = "VexInvalidArgumentError";
2919
2857
  }
2920
- mapErr(f) {
2921
- return err(f(this.error));
2858
+ }
2859
+
2860
+ class VexProtocolError extends VexSerialError {
2861
+ ackType;
2862
+ constructor(message, ackType) {
2863
+ super("protocol", message);
2864
+ this.name = "VexProtocolError";
2865
+ this.ackType = ackType;
2922
2866
  }
2923
- andThrough(_f) {
2924
- return err(this.error);
2867
+ }
2868
+
2869
+ class VexTransferError extends VexSerialError {
2870
+ constructor(message) {
2871
+ super("transfer", message);
2872
+ this.name = "VexTransferError";
2925
2873
  }
2926
- andTee(_f) {
2927
- return err(this.error);
2874
+ }
2875
+
2876
+ class VexDownloadError extends VexSerialError {
2877
+ constructor(message) {
2878
+ super("download", message);
2879
+ this.name = "VexDownloadError";
2928
2880
  }
2929
- orTee(f) {
2930
- try {
2931
- f(this.error);
2932
- } catch (e) {}
2933
- return err(this.error);
2881
+ }
2882
+
2883
+ class VexFirmwareError extends VexSerialError {
2884
+ constructor(message) {
2885
+ super("firmware", message);
2886
+ this.name = "VexFirmwareError";
2934
2887
  }
2935
- andThen(_f) {
2936
- return err(this.error);
2888
+ }
2889
+
2890
+ class VexIoError extends VexSerialError {
2891
+ constructor(message) {
2892
+ super("io", message);
2893
+ this.name = "VexIoError";
2937
2894
  }
2938
- orElse(f) {
2939
- return f(this.error);
2895
+ }
2896
+ function toVexSerialError(error, fallback = "io") {
2897
+ if (error instanceof VexSerialError)
2898
+ return error;
2899
+ const message = error instanceof Error ? error.message : String(error);
2900
+ return new VexSerialError(fallback, message);
2901
+ }
2902
+
2903
+ class VexEventEmitter {
2904
+ handlerMap;
2905
+ constructor() {
2906
+ this.handlerMap = new Map;
2940
2907
  }
2941
- asyncAndThen(_f) {
2942
- return errAsync(this.error);
2908
+ on(eventName, listener) {
2909
+ let listeners = this.handlerMap.get(eventName);
2910
+ listeners ??= [];
2911
+ listeners.push(listener);
2912
+ this.handlerMap.set(eventName, listeners);
2943
2913
  }
2944
- asyncAndThrough(_f) {
2945
- return errAsync(this.error);
2914
+ remove(eventName, listener) {
2915
+ let listeners = this.handlerMap.get(eventName);
2916
+ listeners ??= [];
2917
+ const index = listeners.indexOf(listener);
2918
+ if (index > -1) {
2919
+ listeners.splice(index, 1);
2920
+ }
2921
+ this.handlerMap.set(eventName, listeners);
2946
2922
  }
2947
- asyncMap(_f) {
2948
- return errAsync(this.error);
2923
+ emit(eventName, data) {
2924
+ const listeners = this.handlerMap.get(eventName);
2925
+ if (listeners === undefined || listeners.length === 0)
2926
+ return;
2927
+ if (listeners.length === 1) {
2928
+ listeners[0](data);
2929
+ return;
2930
+ }
2931
+ const errors = [];
2932
+ for (const callback of [...listeners]) {
2933
+ try {
2934
+ callback(data);
2935
+ } catch (error) {
2936
+ errors.push(error);
2937
+ }
2938
+ }
2939
+ if (errors.length === 1)
2940
+ throw errors[0];
2941
+ if (errors.length > 1) {
2942
+ throw new AggregateError(errors, `listeners for ${String(eventName)} failed`);
2943
+ }
2949
2944
  }
2950
- unwrapOr(v) {
2951
- return v;
2945
+ clearListeners() {
2946
+ this.handlerMap.clear();
2952
2947
  }
2953
- match(_ok, err2) {
2954
- return err2(this.error);
2948
+ }
2949
+
2950
+ class VexEventTarget {
2951
+ emitter;
2952
+ constructor() {
2953
+ this.emitter = new VexEventEmitter;
2955
2954
  }
2956
- safeUnwrap() {
2957
- const error = this.error;
2958
- return function* () {
2959
- yield err(error);
2960
- throw new Error("Do not use this generator out of `safeTry`");
2961
- }();
2955
+ emit(eventName, data) {
2956
+ this.emitter.emit(eventName, data);
2962
2957
  }
2963
- _unsafeUnwrap(config2) {
2964
- throw createNeverThrowError("Called `_unsafeUnwrap` on an Err", this, config2);
2958
+ on(eventName, listener) {
2959
+ this.emitter.on(eventName, listener);
2965
2960
  }
2966
- _unsafeUnwrapErr(_) {
2967
- return this.error;
2961
+ remove(eventName, listener) {
2962
+ this.emitter.remove(eventName, listener);
2968
2963
  }
2969
- *[Symbol.iterator]() {
2970
- const self = this;
2971
- yield self;
2972
- return self;
2964
+ clearListeners() {
2965
+ this.emitter.clearListeners();
2973
2966
  }
2974
2967
  }
2975
- var fromThrowable = Result.fromThrowable;
2976
2968
  var MATCH_STATUS_ALT_ACK = 167;
2977
2969
 
2978
2970
  class Packet {
@@ -3054,86 +3046,122 @@ class CrcGenerator {
3054
3046
  }
3055
3047
  return acc & 65535;
3056
3048
  }
3057
- crc32(buf, initValue) {
3058
- let acc = initValue;
3059
- for (let j = 0;j < buf.length; j++) {
3060
- acc = (acc << 8 ^ CRC32_TABLE[(acc >>> 24 ^ buf[j]) & 255]) >>> 0;
3061
- }
3062
- return acc >>> 0;
3049
+ crc32(buf, initValue) {
3050
+ let acc = initValue;
3051
+ for (let j = 0;j < buf.length; j++) {
3052
+ acc = (acc << 8 ^ CRC32_TABLE[(acc >>> 24 ^ buf[j]) & 255]) >>> 0;
3053
+ }
3054
+ return acc >>> 0;
3055
+ }
3056
+ }
3057
+ var textEncoder = new TextEncoder;
3058
+ var HEADER_TO_DEVICE = Uint8Array.of(201, 54, 184, 71);
3059
+ function encodeFixedText(value, field, maxBytes) {
3060
+ const encoded = textEncoder.encode(value);
3061
+ if (encoded.byteLength > maxBytes) {
3062
+ throw new RangeError(`${field} must be at most ${maxBytes} UTF-8 bytes`);
3063
+ }
3064
+ return encoded;
3065
+ }
3066
+
3067
+ class PacketEncoder {
3068
+ static HEADERS_LENGTH = 4;
3069
+ static HEADER_TO_DEVICE = [201, 54, 184, 71];
3070
+ static HEADER_TO_HOST = [170, 85];
3071
+ static J2000_EPOCH = 946684800;
3072
+ vexVersion = 0;
3073
+ crcgen = new CrcGenerator;
3074
+ allPacketsTable = new Map;
3075
+ static getInstance() {
3076
+ Packet.ENCODER ??= new PacketEncoder;
3077
+ return Packet.ENCODER;
3078
+ }
3079
+ constructor() {}
3080
+ registerPacketTypes(types) {
3081
+ for (const type of types) {
3082
+ let byExtendedId = this.allPacketsTable.get(type.COMMAND_ID);
3083
+ if (byExtendedId === undefined) {
3084
+ byExtendedId = new Map;
3085
+ this.allPacketsTable.set(type.COMMAND_ID, byExtendedId);
3086
+ }
3087
+ byExtendedId.set(type.COMMAND_EXTENDED_ID, type);
3088
+ }
3089
+ }
3090
+ getPacketType(commandId, commandExtendedId) {
3091
+ if (commandId === undefined)
3092
+ return;
3093
+ return this.allPacketsTable.get(commandId)?.get(commandExtendedId);
3094
+ }
3095
+ createHeader(buf) {
3096
+ if (buf === undefined || buf.byteLength < PacketEncoder.HEADERS_LENGTH) {
3097
+ buf = new ArrayBuffer(PacketEncoder.HEADERS_LENGTH);
3098
+ }
3099
+ const h = new Uint8Array(buf);
3100
+ h.set(HEADER_TO_DEVICE);
3101
+ return h;
3102
+ }
3103
+ cdcCommand(cmd) {
3104
+ const h = new Uint8Array(5);
3105
+ h.set(HEADER_TO_DEVICE);
3106
+ h[4] = cmd;
3107
+ return h;
3108
+ }
3109
+ cdcCommandWithData(cmd, data) {
3110
+ const h = new Uint8Array(6 + data.length);
3111
+ h.set(HEADER_TO_DEVICE);
3112
+ h[4] = cmd;
3113
+ h[5] = data.length;
3114
+ h.set(data, 6);
3115
+ return h;
3116
+ }
3117
+ cdc2Command(cmd, ext) {
3118
+ const h = new Uint8Array(9);
3119
+ h.set(HEADER_TO_DEVICE);
3120
+ h[4] = cmd;
3121
+ h[5] = ext;
3122
+ h[6] = 0;
3123
+ this.appendCrc16(h);
3124
+ return h;
3125
+ }
3126
+ cdc2CommandBufferLength(data) {
3127
+ return PacketEncoder.HEADERS_LENGTH + data.length + 5 + (data.length > 127 ? 1 : 0);
3128
+ }
3129
+ cdc2CommandWithData(cmd, ext, data) {
3130
+ const h = new Uint8Array(this.cdc2CommandBufferLength(data));
3131
+ h.set(HEADER_TO_DEVICE);
3132
+ h[4] = cmd;
3133
+ h[5] = ext;
3134
+ if (data.length < 128) {
3135
+ h[6] = data.length;
3136
+ h.set(data, 7);
3137
+ } else {
3138
+ h[6] = data.length >>> 8 | 128;
3139
+ h[7] = data.length & 255;
3140
+ h.set(data, 8);
3141
+ }
3142
+ this.appendCrc16(h);
3143
+ return h;
3144
+ }
3145
+ appendCrc16(h) {
3146
+ const crc2 = this.crcgen.crc16(h.subarray(0, h.length - 2), 0);
3147
+ h[h.length - 2] = crc2 >>> 8;
3148
+ h[h.length - 1] = crc2 & 255;
3149
+ }
3150
+ validateHeader(data) {
3151
+ return data[0] === PacketEncoder.HEADER_TO_HOST[0] && data[1] === PacketEncoder.HEADER_TO_HOST[1];
3152
+ }
3153
+ validateMessageCdc(data) {
3154
+ const crc2 = (data[data.byteLength - 2] << 8) + data[data.byteLength - 1];
3155
+ return this.crcgen.crc16(data.subarray(0, data.byteLength - 2), 0) === crc2;
3156
+ }
3157
+ getPayloadSize(data) {
3158
+ const a2 = data[3];
3159
+ return (a2 & 128) === 0 ? a2 : ((a2 & 127) << 8) + data[4];
3160
+ }
3161
+ getHostHeaderLength(data) {
3162
+ return (data[3] & 128) === 0 ? 4 : 5;
3063
3163
  }
3064
3164
  }
3065
- var exports_VexPacketModels = {};
3066
- __export2(exports_VexPacketModels, {
3067
- WriteKeyValueReplyD2HPacket: () => WriteKeyValueReplyD2HPacket,
3068
- WriteKeyValueH2DPacket: () => WriteKeyValueH2DPacket,
3069
- WriteFileReplyD2HPacket: () => WriteFileReplyD2HPacket,
3070
- WriteFileH2DPacket: () => WriteFileH2DPacket,
3071
- UpdateMatchModeH2DPacket: () => UpdateMatchModeH2DPacket,
3072
- SystemVersionReplyD2HPacket: () => SystemVersionReplyD2HPacket,
3073
- SystemVersionH2DPacket: () => SystemVersionH2DPacket,
3074
- SendDashTouchReplyD2HPacket: () => SendDashTouchReplyD2HPacket,
3075
- SendDashTouchH2DPacket: () => SendDashTouchH2DPacket,
3076
- SelectDashReplyD2HPacket: () => SelectDashReplyD2HPacket,
3077
- SelectDashH2DPacket: () => SelectDashH2DPacket,
3078
- ScreenCaptureReplyD2HPacket: () => ScreenCaptureReplyD2HPacket,
3079
- ScreenCaptureH2DPacket: () => ScreenCaptureH2DPacket,
3080
- ReadLogPageReplyD2HPacket: () => ReadLogPageReplyD2HPacket,
3081
- ReadLogPageH2DPacket: () => ReadLogPageH2DPacket,
3082
- ReadKeyValueReplyD2HPacket: () => ReadKeyValueReplyD2HPacket,
3083
- ReadKeyValueH2DPacket: () => ReadKeyValueH2DPacket,
3084
- ReadFileReplyD2HPacket: () => ReadFileReplyD2HPacket,
3085
- ReadFileH2DPacket: () => ReadFileH2DPacket,
3086
- Query1ReplyD2HPacket: () => Query1ReplyD2HPacket,
3087
- Query1H2DPacket: () => Query1H2DPacket,
3088
- MatchStatusReplyD2HPacket: () => MatchStatusReplyD2HPacket,
3089
- MatchModeReplyD2HPacket: () => MatchModeReplyD2HPacket,
3090
- LoadFileActionReplyD2HPacket: () => LoadFileActionReplyD2HPacket,
3091
- LoadFileActionH2DPacket: () => LoadFileActionH2DPacket,
3092
- LinkFileReplyD2HPacket: () => LinkFileReplyD2HPacket,
3093
- LinkFileH2DPacket: () => LinkFileH2DPacket,
3094
- InitFileTransferReplyD2HPacket: () => InitFileTransferReplyD2HPacket,
3095
- InitFileTransferH2DPacket: () => InitFileTransferH2DPacket,
3096
- GetSystemStatusReplyD2HPacket: () => GetSystemStatusReplyD2HPacket,
3097
- GetSystemStatusH2DPacket: () => GetSystemStatusH2DPacket,
3098
- GetSystemFlagsReplyD2HPacket: () => GetSystemFlagsReplyD2HPacket,
3099
- GetSystemFlagsH2DPacket: () => GetSystemFlagsH2DPacket,
3100
- GetSlot5to8InfoReplyD2HPacket: () => GetSlot5to8InfoReplyD2HPacket,
3101
- GetSlot5to8InfoH2DPacket: () => GetSlot5to8InfoH2DPacket,
3102
- GetSlot1to4InfoReplyD2HPacket: () => GetSlot1to4InfoReplyD2HPacket,
3103
- GetSlot1to4InfoH2DPacket: () => GetSlot1to4InfoH2DPacket,
3104
- GetRadioStatusReplyD2HPacket: () => GetRadioStatusReplyD2HPacket,
3105
- GetRadioStatusH2DPacket: () => GetRadioStatusH2DPacket,
3106
- GetRadioModeH2DPacket: () => GetRadioModeH2DPacket,
3107
- GetProgramSlotInfoReplyD2HPacket: () => GetProgramSlotInfoReplyD2HPacket,
3108
- GetProgramSlotInfoH2DPacket: () => GetProgramSlotInfoH2DPacket,
3109
- GetMatchStatusH2DPacket: () => GetMatchStatusH2DPacket,
3110
- GetLogCountReplyD2HPacket: () => GetLogCountReplyD2HPacket,
3111
- GetLogCountH2DPacket: () => GetLogCountH2DPacket,
3112
- GetFileMetadataReplyD2HPacket: () => GetFileMetadataReplyD2HPacket,
3113
- GetFileMetadataH2DPacket: () => GetFileMetadataH2DPacket,
3114
- GetFdtStatusReplyD2HPacket: () => GetFdtStatusReplyD2HPacket,
3115
- GetFdtStatusH2DPacket: () => GetFdtStatusH2DPacket,
3116
- GetDirectoryFileCountReplyD2HPacket: () => GetDirectoryFileCountReplyD2HPacket,
3117
- GetDirectoryFileCountH2DPacket: () => GetDirectoryFileCountH2DPacket,
3118
- GetDirectoryEntryReplyD2HPacket: () => GetDirectoryEntryReplyD2HPacket,
3119
- GetDirectoryEntryH2DPacket: () => GetDirectoryEntryH2DPacket,
3120
- GetDeviceStatusReplyD2HPacket: () => GetDeviceStatusReplyD2HPacket,
3121
- GetDeviceStatusH2DPacket: () => GetDeviceStatusH2DPacket,
3122
- FileFormatReplyD2HPacket: () => FileFormatReplyD2HPacket,
3123
- FileFormatH2DPacket: () => FileFormatH2DPacket,
3124
- FileControlReplyD2HPacket: () => FileControlReplyD2HPacket,
3125
- FileControlH2DPacket: () => FileControlH2DPacket,
3126
- FileClearUpReplyD2HPacket: () => FileClearUpReplyD2HPacket,
3127
- FileClearUpH2DPacket: () => FileClearUpH2DPacket,
3128
- FactoryStatusReplyD2HPacket: () => FactoryStatusReplyD2HPacket,
3129
- FactoryStatusH2DPacket: () => FactoryStatusH2DPacket,
3130
- FactoryEnableReplyD2HPacket: () => FactoryEnableReplyD2HPacket,
3131
- FactoryEnableH2DPacket: () => FactoryEnableH2DPacket,
3132
- ExitFileTransferReplyD2HPacket: () => ExitFileTransferReplyD2HPacket,
3133
- ExitFileTransferH2DPacket: () => ExitFileTransferH2DPacket,
3134
- EraseFileReplyD2HPacket: () => EraseFileReplyD2HPacket,
3135
- EraseFileH2DPacket: () => EraseFileH2DPacket
3136
- });
3137
3165
 
3138
3166
  class VexFirmwareVersion {
3139
3167
  major;
@@ -3201,7 +3229,7 @@ class PacketView extends DataView {
3201
3229
  super(buffer, offset, length);
3202
3230
  }
3203
3231
  static fromPacket(packet) {
3204
- const view = new PacketView(packet.data.buffer, packet.data.byteOffset);
3232
+ const view = new PacketView(packet.data.buffer, packet.data.byteOffset, packet.data.byteLength);
3205
3233
  view.position = packet.ackIndex + 1;
3206
3234
  return view;
3207
3235
  }
@@ -3261,12 +3289,12 @@ class PacketView extends DataView {
3261
3289
  return result;
3262
3290
  }
3263
3291
  }
3264
- var textEncoder = new TextEncoder;
3292
+ var textEncoder2 = new TextEncoder;
3265
3293
  function filePayload(a2, b, fileName) {
3266
3294
  const payload = new Uint8Array(26);
3267
3295
  payload[0] = a2;
3268
3296
  payload[1] = b;
3269
- payload.set(encodeFixedText(fileName, "Filename", 23), 2);
3297
+ payload.set(encodeFixedText(fileName, "Filename", 24), 2);
3270
3298
  return payload;
3271
3299
  }
3272
3300
  var clamp100 = (value) => value !== undefined && value > 100 ? 100 : value;
@@ -3297,15 +3325,6 @@ class GetMatchStatusH2DPacket extends DeviceBoundPacket {
3297
3325
  static COMMAND_ID = 88;
3298
3326
  static COMMAND_EXTENDED_ID = 194;
3299
3327
  }
3300
-
3301
- class GetRadioModeH2DPacket extends DeviceBoundPacket {
3302
- static COMMAND_ID = 88;
3303
- static COMMAND_EXTENDED_ID = 65;
3304
- constructor(mode) {
3305
- super(Uint8Array.of(mode));
3306
- }
3307
- }
3308
-
3309
3328
  class FileControlH2DPacket extends DeviceBoundPacket {
3310
3329
  static COMMAND_ID = 86;
3311
3330
  static COMMAND_EXTENDED_ID = 16;
@@ -3334,7 +3353,7 @@ class InitFileTransferH2DPacket extends DeviceBoundPacket {
3334
3353
  const timestamp = (Date.now() / 1000 >>> 0) - PacketEncoder.J2000_EPOCH;
3335
3354
  view.setUint32(20, timestamp, true);
3336
3355
  payload.set(version.toUint8Array(), 24);
3337
- payload.set(encodeFixedText(name, "Filename", 23), 28);
3356
+ payload.set(encodeFixedText(name, "Filename", 24), 28);
3338
3357
  super(payload);
3339
3358
  }
3340
3359
  }
@@ -3402,15 +3421,6 @@ class LoadFileActionH2DPacket extends DeviceBoundPacket {
3402
3421
  super(filePayload(vendor, actionId, fileName));
3403
3422
  }
3404
3423
  }
3405
-
3406
- class GetFileMetadataH2DPacket extends DeviceBoundPacket {
3407
- static COMMAND_ID = 86;
3408
- static COMMAND_EXTENDED_ID = 25;
3409
- constructor(vendor, fileName, options) {
3410
- super(filePayload(vendor, options, fileName));
3411
- }
3412
- }
3413
-
3414
3424
  class EraseFileH2DPacket extends DeviceBoundPacket {
3415
3425
  static COMMAND_ID = 86;
3416
3426
  static COMMAND_EXTENDED_ID = 27;
@@ -3434,15 +3444,6 @@ class FileClearUpH2DPacket extends DeviceBoundPacket {
3434
3444
  super(Uint8Array.of(vendor, 0));
3435
3445
  }
3436
3446
  }
3437
-
3438
- class FileFormatH2DPacket extends DeviceBoundPacket {
3439
- static COMMAND_ID = 86;
3440
- static COMMAND_EXTENDED_ID = 31;
3441
- constructor() {
3442
- super(Uint8Array.of(68, 67, 66, 65));
3443
- }
3444
- }
3445
-
3446
3447
  class GetSystemFlagsH2DPacket extends DeviceBoundPacket {
3447
3448
  static COMMAND_ID = 86;
3448
3449
  static COMMAND_EXTENDED_ID = 32;
@@ -3457,29 +3458,6 @@ class GetSystemStatusH2DPacket extends DeviceBoundPacket {
3457
3458
  static COMMAND_ID = 86;
3458
3459
  static COMMAND_EXTENDED_ID = 34;
3459
3460
  }
3460
-
3461
- class GetFdtStatusH2DPacket extends DeviceBoundPacket {
3462
- static COMMAND_ID = 86;
3463
- static COMMAND_EXTENDED_ID = 35;
3464
- }
3465
-
3466
- class GetLogCountH2DPacket extends DeviceBoundPacket {
3467
- static COMMAND_ID = 86;
3468
- static COMMAND_EXTENDED_ID = 36;
3469
- }
3470
-
3471
- class ReadLogPageH2DPacket extends DeviceBoundPacket {
3472
- static COMMAND_ID = 86;
3473
- static COMMAND_EXTENDED_ID = 37;
3474
- constructor(offset, count) {
3475
- const payload = new Uint8Array(8);
3476
- const view = new DataView(payload.buffer);
3477
- view.setUint32(0, offset, true);
3478
- view.setUint32(4, count, true);
3479
- super(payload);
3480
- }
3481
- }
3482
-
3483
3461
  class GetRadioStatusH2DPacket extends DeviceBoundPacket {
3484
3462
  static COMMAND_ID = 86;
3485
3463
  static COMMAND_EXTENDED_ID = 38;
@@ -3529,7 +3507,7 @@ class WriteKeyValueH2DPacket extends DeviceBoundPacket {
3529
3507
  static COMMAND_EXTENDED_ID = 47;
3530
3508
  constructor(key, value) {
3531
3509
  const strk = encodeFixedText(key, "Key", 31);
3532
- const strv = textEncoder.encode(value);
3510
+ const strv = textEncoder2.encode(value);
3533
3511
  if (strk.byteLength + strv.byteLength + 20 > 32767) {
3534
3512
  throw new RangeError("Key and value are too large for a protocol packet");
3535
3513
  }
@@ -3539,17 +3517,6 @@ class WriteKeyValueH2DPacket extends DeviceBoundPacket {
3539
3517
  super(payload);
3540
3518
  }
3541
3519
  }
3542
-
3543
- class GetSlot1to4InfoH2DPacket extends DeviceBoundPacket {
3544
- static COMMAND_ID = 86;
3545
- static COMMAND_EXTENDED_ID = 49;
3546
- }
3547
-
3548
- class GetSlot5to8InfoH2DPacket extends DeviceBoundPacket {
3549
- static COMMAND_ID = 86;
3550
- static COMMAND_EXTENDED_ID = 50;
3551
- }
3552
-
3553
3520
  class FactoryStatusH2DPacket extends DeviceBoundPacket {
3554
3521
  static COMMAND_ID = 86;
3555
3522
  static COMMAND_EXTENDED_ID = 241;
@@ -3692,7 +3659,7 @@ class ReadFileReplyD2HPacket extends HostBoundPacket {
3692
3659
  const view = PacketView.fromPacket(this);
3693
3660
  this.addr = view.nextUint32();
3694
3661
  this.length = this.payloadSize - 8;
3695
- this.buf = this.data.slice(view.position, view.position + this.length).buffer;
3662
+ this.buf = this.data.subarray(view.position, view.position + this.length);
3696
3663
  }
3697
3664
  }
3698
3665
 
@@ -3737,28 +3704,6 @@ class LoadFileActionReplyD2HPacket extends HostBoundPacket {
3737
3704
  static COMMAND_ID = 86;
3738
3705
  static COMMAND_EXTENDED_ID = 24;
3739
3706
  }
3740
-
3741
- class GetFileMetadataReplyD2HPacket extends HostBoundPacket {
3742
- static COMMAND_ID = 86;
3743
- static COMMAND_EXTENDED_ID = 25;
3744
- file;
3745
- constructor(data) {
3746
- super(data);
3747
- if (this.payloadSize <= 4)
3748
- return;
3749
- const view = PacketView.fromPacket(this);
3750
- view.nextUint8();
3751
- this.file = {
3752
- size: view.nextUint32(),
3753
- loadAddress: view.nextUint32(),
3754
- crc32: view.nextUint32(),
3755
- type: view.nextString(4),
3756
- timestamp: view.nextUint32() + PacketEncoder.J2000_EPOCH,
3757
- version: view.nextVersion()
3758
- };
3759
- }
3760
- }
3761
-
3762
3707
  class EraseFileReplyD2HPacket extends HostBoundPacket {
3763
3708
  static COMMAND_ID = 86;
3764
3709
  static COMMAND_EXTENDED_ID = 27;
@@ -3781,12 +3726,6 @@ class FileClearUpReplyD2HPacket extends HostBoundPacket {
3781
3726
  static COMMAND_ID = 86;
3782
3727
  static COMMAND_EXTENDED_ID = 30;
3783
3728
  }
3784
-
3785
- class FileFormatReplyD2HPacket extends HostBoundPacket {
3786
- static COMMAND_ID = 86;
3787
- static COMMAND_EXTENDED_ID = 31;
3788
- }
3789
-
3790
3729
  class GetSystemFlagsReplyD2HPacket extends HostBoundPacket {
3791
3730
  static COMMAND_ID = 86;
3792
3731
  static COMMAND_EXTENDED_ID = 32;
@@ -3889,69 +3828,6 @@ class GetSystemStatusReplyD2HPacket extends HostBoundPacket {
3889
3828
  }
3890
3829
  }
3891
3830
  }
3892
-
3893
- class GetFdtStatusReplyD2HPacket extends HostBoundPacket {
3894
- static COMMAND_ID = 86;
3895
- static COMMAND_EXTENDED_ID = 35;
3896
- count;
3897
- status;
3898
- constructor(data) {
3899
- super(data);
3900
- const view = PacketView.fromPacket(this);
3901
- this.count = view.nextUint8();
3902
- this.status = [];
3903
- for (let i2 = 0;i2 < this.count; i2++) {
3904
- this.status.push({
3905
- index: view.nextUint8(),
3906
- type: view.nextUint8(),
3907
- status: view.nextUint8(),
3908
- betaversion: view.nextUint8(),
3909
- version: view.nextUint16(),
3910
- bootversion: view.nextUint16()
3911
- });
3912
- }
3913
- }
3914
- }
3915
-
3916
- class GetLogCountReplyD2HPacket extends HostBoundPacket {
3917
- static COMMAND_ID = 86;
3918
- static COMMAND_EXTENDED_ID = 36;
3919
- count;
3920
- constructor(data) {
3921
- super(data);
3922
- const view = PacketView.fromPacket(this);
3923
- view.nextUint8();
3924
- this.count = view.nextUint32();
3925
- }
3926
- }
3927
-
3928
- class ReadLogPageReplyD2HPacket extends HostBoundPacket {
3929
- static COMMAND_ID = 86;
3930
- static COMMAND_EXTENDED_ID = 37;
3931
- offset;
3932
- count;
3933
- entries;
3934
- constructor(data) {
3935
- super(data);
3936
- const view = PacketView.fromPacket(this);
3937
- const size = view.nextUint8();
3938
- this.offset = view.nextUint32();
3939
- this.count = view.nextUint16();
3940
- this.entries = [];
3941
- let j = this.ackIndex + 8;
3942
- for (let i2 = 0;i2 < this.count; i2++) {
3943
- this.entries.push({
3944
- code: view.getUint8(j),
3945
- type: view.getUint8(j + 1),
3946
- desc: view.getUint8(j + 2),
3947
- spare: view.getUint8(j + 3),
3948
- time: view.getUint32(j + 4, true)
3949
- });
3950
- j += size;
3951
- }
3952
- }
3953
- }
3954
-
3955
3831
  class GetRadioStatusReplyD2HPacket extends HostBoundPacket {
3956
3832
  static COMMAND_ID = 86;
3957
3833
  static COMMAND_EXTENDED_ID = 38;
@@ -4000,40 +3876,6 @@ class WriteKeyValueReplyD2HPacket extends HostBoundPacket {
4000
3876
  static COMMAND_ID = 86;
4001
3877
  static COMMAND_EXTENDED_ID = 47;
4002
3878
  }
4003
-
4004
- class GetSlot1to4InfoReplyD2HPacket extends HostBoundPacket {
4005
- static COMMAND_ID = 86;
4006
- static COMMAND_EXTENDED_ID = 49;
4007
- slotFlags;
4008
- slots;
4009
- constructor(data, start = 1) {
4010
- super(data);
4011
- const view = PacketView.fromPacket(this);
4012
- this.slotFlags = view.nextUint8();
4013
- this.slots = [];
4014
- for (let i2 = 0;i2 < 4; i2++) {
4015
- if ((this.slotFlags & 1 << start - 1 + i2) === 0)
4016
- continue;
4017
- const icon = view.nextUint16();
4018
- const nameLen = view.nextUint8();
4019
- this.slots.push({
4020
- slot: start + i2,
4021
- icon,
4022
- name: view.nextString(nameLen)
4023
- });
4024
- }
4025
- }
4026
- }
4027
-
4028
- class GetSlot5to8InfoReplyD2HPacket extends GetSlot1to4InfoReplyD2HPacket {
4029
- static COMMAND_ID = 86;
4030
- static COMMAND_EXTENDED_ID = 50;
4031
- slotStartIndex = 5;
4032
- constructor(data) {
4033
- super(data, 5);
4034
- }
4035
- }
4036
-
4037
3879
  class FactoryStatusReplyD2HPacket extends HostBoundPacket {
4038
3880
  static COMMAND_ID = 86;
4039
3881
  static COMMAND_EXTENDED_ID = 241;
@@ -4051,122 +3893,32 @@ class FactoryEnableReplyD2HPacket extends HostBoundPacket {
4051
3893
  static COMMAND_ID = 86;
4052
3894
  static COMMAND_EXTENDED_ID = 255;
4053
3895
  }
4054
- var textEncoder2 = new TextEncoder;
4055
- var HEADER_TO_DEVICE = Uint8Array.of(201, 54, 184, 71);
4056
- function encodeFixedText(value, field, maxBytes) {
4057
- const encoded = textEncoder2.encode(value);
4058
- if (encoded.byteLength > maxBytes) {
4059
- throw new RangeError(`${field} must be at most ${maxBytes} UTF-8 bytes`);
4060
- }
4061
- return encoded;
4062
- }
4063
-
4064
- class PacketEncoder {
4065
- static HEADERS_LENGTH = 4;
4066
- static HEADER_TO_DEVICE = [201, 54, 184, 71];
4067
- static HEADER_TO_HOST = [170, 85];
4068
- static J2000_EPOCH = 946684800;
4069
- vexVersion = 0;
4070
- crcgen = new CrcGenerator;
4071
- allPacketsTable = new Map;
4072
- static getInstance() {
4073
- Packet.ENCODER ??= new PacketEncoder;
4074
- return Packet.ENCODER;
4075
- }
4076
- constructor() {
4077
- for (const packet of Object.values(exports_VexPacketModels)) {
4078
- if (typeof packet === "function" && packet.prototype instanceof HostBoundPacket) {
4079
- const type = packet;
4080
- let byExtendedId = this.allPacketsTable.get(type.COMMAND_ID);
4081
- if (byExtendedId === undefined) {
4082
- byExtendedId = new Map;
4083
- this.allPacketsTable.set(type.COMMAND_ID, byExtendedId);
4084
- }
4085
- byExtendedId.set(type.COMMAND_EXTENDED_ID, type);
4086
- }
4087
- }
4088
- }
4089
- getPacketType(commandId, commandExtendedId) {
4090
- if (commandId === undefined)
4091
- return;
4092
- return this.allPacketsTable.get(commandId)?.get(commandExtendedId);
4093
- }
4094
- createHeader(buf) {
4095
- if (buf === undefined || buf.byteLength < PacketEncoder.HEADERS_LENGTH) {
4096
- buf = new ArrayBuffer(PacketEncoder.HEADERS_LENGTH);
4097
- }
4098
- const h = new Uint8Array(buf);
4099
- h.set(HEADER_TO_DEVICE);
4100
- return h;
4101
- }
4102
- cdcCommand(cmd) {
4103
- const h = new Uint8Array(5);
4104
- h.set(HEADER_TO_DEVICE);
4105
- h[4] = cmd;
4106
- return h;
4107
- }
4108
- cdcCommandWithData(cmd, data) {
4109
- const h = new Uint8Array(6 + data.length);
4110
- h.set(HEADER_TO_DEVICE);
4111
- h[4] = cmd;
4112
- h[5] = data.length;
4113
- h.set(data, 6);
4114
- return h;
4115
- }
4116
- cdc2Command(cmd, ext) {
4117
- const h = new Uint8Array(9);
4118
- h.set(HEADER_TO_DEVICE);
4119
- h[4] = cmd;
4120
- h[5] = ext;
4121
- h[6] = 0;
4122
- this.appendCrc16(h);
4123
- return h;
4124
- }
4125
- cdc2CommandBufferLength(data) {
4126
- return PacketEncoder.HEADERS_LENGTH + data.length + 5 + (data.length > 127 ? 1 : 0);
4127
- }
4128
- cdc2CommandWithData(cmd, ext, data) {
4129
- const h = new Uint8Array(this.cdc2CommandBufferLength(data));
4130
- h.set(HEADER_TO_DEVICE);
4131
- h[4] = cmd;
4132
- h[5] = ext;
4133
- if (data.length < 128) {
4134
- h[6] = data.length;
4135
- h.set(data, 7);
4136
- } else {
4137
- h[6] = data.length >>> 8 | 128;
4138
- h[7] = data.length & 255;
4139
- h.set(data, 8);
4140
- }
4141
- this.appendCrc16(h);
4142
- return h;
4143
- }
4144
- appendCrc16(h) {
4145
- const crc2 = this.crcgen.crc16(h.subarray(0, h.length - 2), 0);
4146
- h[h.length - 2] = crc2 >>> 8;
4147
- h[h.length - 1] = crc2 & 255;
4148
- }
4149
- validateHeader(data) {
4150
- return data[0] === PacketEncoder.HEADER_TO_HOST[0] && data[1] === PacketEncoder.HEADER_TO_HOST[1];
4151
- }
4152
- validateMessageCdc(data) {
4153
- const crc2 = (data[data.byteLength - 2] << 8) + data[data.byteLength - 1];
4154
- return this.crcgen.crc16(data.subarray(0, data.byteLength - 2), 0) === crc2;
4155
- }
4156
- getPayloadSize(data) {
4157
- const a2 = data[3];
4158
- return (a2 & 128) === 0 ? a2 : ((a2 & 127) << 8) + data[4];
4159
- }
4160
- getHostHeaderLength(data) {
4161
- return (data[3] & 128) === 0 ? 4 : 5;
4162
- }
4163
- }
4164
- var thePacketEncoder = PacketEncoder.getInstance();
4165
3896
  var SCREEN_CAPTURE_HEIGHT = 272;
4166
3897
  var SCREEN_CAPTURE_WIDTH = 480;
4167
3898
  var SCREEN_CAPTURE_CHANNELS = 3;
4168
3899
  var SCREEN_CAPTURE_MESSAGE_WIDTH = 512;
4169
3900
  var SCREEN_CAPTURE_MESSAGE_CHANNELS = 4;
3901
+ var SCREEN_CAPTURE_FRAMEBUFFER_SIZE = SCREEN_CAPTURE_MESSAGE_WIDTH * SCREEN_CAPTURE_HEIGHT * SCREEN_CAPTURE_MESSAGE_CHANNELS;
3902
+ function convertScreenCapture(framebuffer) {
3903
+ if (framebuffer.length !== SCREEN_CAPTURE_FRAMEBUFFER_SIZE) {
3904
+ throw new Error(`bad screen capture framebuffer size: ${framebuffer.length}; expected ${SCREEN_CAPTURE_FRAMEBUFFER_SIZE}`);
3905
+ }
3906
+ const pixels = new Uint8Array(SCREEN_CAPTURE_WIDTH * SCREEN_CAPTURE_HEIGHT * SCREEN_CAPTURE_CHANNELS);
3907
+ let source = 0;
3908
+ let target = 0;
3909
+ for (let row = 0;row < SCREEN_CAPTURE_HEIGHT; row++) {
3910
+ for (let column = 0;column < SCREEN_CAPTURE_WIDTH; column++) {
3911
+ pixels[target] = framebuffer[source + 2];
3912
+ pixels[target + 1] = framebuffer[source + 1];
3913
+ pixels[target + 2] = framebuffer[source];
3914
+ source += SCREEN_CAPTURE_MESSAGE_CHANNELS;
3915
+ target += SCREEN_CAPTURE_CHANNELS;
3916
+ }
3917
+ source += (SCREEN_CAPTURE_MESSAGE_WIDTH - SCREEN_CAPTURE_WIDTH) * SCREEN_CAPTURE_MESSAGE_CHANNELS;
3918
+ }
3919
+ return pixels;
3920
+ }
3921
+ var thePacketEncoder = PacketEncoder.getInstance();
4170
3922
 
4171
3923
  class VexSerialConnection extends VexEventTarget {
4172
3924
  filters = [{ usbVendorId: 10376 }];
@@ -4174,12 +3926,31 @@ class VexSerialConnection extends VexEventTarget {
4174
3926
  reader;
4175
3927
  port;
4176
3928
  serial;
4177
- callbacksQueue = [];
3929
+ pendingCallbacks = new Map;
3930
+ rawCallbacks = {
3931
+ head: undefined,
3932
+ tail: undefined
3933
+ };
3934
+ pendingCommandTails = new Map;
4178
3935
  _onPortDisconnect = null;
4179
3936
  _closePromise = null;
3937
+ _openPromise = null;
3938
+ _isClosing = false;
4180
3939
  _wasConnected = false;
4181
3940
  fileTransferTail = Promise.resolve();
4182
3941
  fileTransferDepth = 0;
3942
+ get callbacksQueue() {
3943
+ const callbacks = [];
3944
+ for (const queue of this.pendingCallbacks.values()) {
3945
+ for (let callback = queue.head;callback; callback = callback.next) {
3946
+ callbacks.push(callback);
3947
+ }
3948
+ }
3949
+ for (let callback = this.rawCallbacks.head;callback; callback = callback.next) {
3950
+ callbacks.push(callback);
3951
+ }
3952
+ return callbacks;
3953
+ }
4183
3954
  get isConnected() {
4184
3955
  return this.port !== undefined && this.reader !== undefined && this.writer !== undefined;
4185
3956
  }
@@ -4191,27 +3962,45 @@ class VexSerialConnection extends VexEventTarget {
4191
3962
  this.serial = serial;
4192
3963
  }
4193
3964
  reportWarning(message, details) {
4194
- this.emit("warning", { message, details });
3965
+ this.emitSafely("warning", {
3966
+ message,
3967
+ details
3968
+ });
3969
+ }
3970
+ emitSafely(eventName, data) {
3971
+ try {
3972
+ this.emit(eventName, data);
3973
+ } catch {}
4195
3974
  }
4196
3975
  async close() {
4197
3976
  if (this._closePromise)
4198
3977
  return this._closePromise;
4199
- if (!this._hasOpenResources())
4200
- return;
4201
- this._closePromise = this._doClose();
3978
+ this._isClosing = true;
3979
+ const closing = this._closeAfterOpen();
3980
+ this._closePromise = closing;
4202
3981
  try {
4203
- await this._closePromise;
3982
+ await closing;
4204
3983
  } finally {
4205
- this._closePromise = null;
3984
+ if (this._closePromise === closing)
3985
+ this._closePromise = null;
3986
+ this._isClosing = false;
4206
3987
  }
4207
3988
  }
3989
+ async _closeAfterOpen() {
3990
+ const opening = this._openPromise;
3991
+ if (opening !== null)
3992
+ await opening;
3993
+ if (!this._hasOpenResources())
3994
+ return;
3995
+ await this._doClose();
3996
+ }
4208
3997
  _hasOpenResources() {
4209
- return this.port !== undefined || this.reader !== undefined || this.writer !== undefined || this._onPortDisconnect !== null || this.callbacksQueue.length > 0;
3998
+ return this.port !== undefined || this.reader !== undefined || this.writer !== undefined || this._onPortDisconnect !== null || this.hasPendingCallbacks();
4210
3999
  }
4211
4000
  async _doClose() {
4212
- for (const callback of this.callbacksQueue.splice(0)) {
4001
+ for (const callback of this.drainPendingCallbacks()) {
4213
4002
  clearTimeout(callback.timeout);
4214
- callback.callback(255);
4003
+ callback.callback(258);
4215
4004
  }
4216
4005
  const onDisconnect = this._onPortDisconnect;
4217
4006
  this._onPortDisconnect = null;
@@ -4260,11 +4049,22 @@ class VexSerialConnection extends VexEventTarget {
4260
4049
  }
4261
4050
  if (this._wasConnected) {
4262
4051
  this._wasConnected = false;
4263
- this.emit("disconnected", undefined);
4052
+ this.emitSafely("disconnected", undefined);
4264
4053
  }
4265
4054
  }
4266
4055
  open(use = 0, askUser = true) {
4267
- return new ResultAsync(this._open(use, askUser));
4056
+ if (this._openPromise !== null)
4057
+ return new ResultAsync(this._openPromise);
4058
+ const opening = this._open(use, askUser);
4059
+ this._openPromise = opening;
4060
+ opening.then(() => {
4061
+ if (this._openPromise === opening)
4062
+ this._openPromise = null;
4063
+ }, () => {
4064
+ if (this._openPromise === opening)
4065
+ this._openPromise = null;
4066
+ });
4067
+ return new ResultAsync(opening);
4268
4068
  }
4269
4069
  async _open(use, askUser) {
4270
4070
  if (this._closePromise !== null)
@@ -4297,38 +4097,63 @@ class VexSerialConnection extends VexEventTarget {
4297
4097
  this.reader = this.port.readable.getReader();
4298
4098
  this.startReader();
4299
4099
  this._wasConnected = true;
4300
- this.emit("connected", undefined);
4100
+ this.emitSafely("connected", undefined);
4301
4101
  return ok("opened");
4302
4102
  } catch (e) {
4303
- await this.close();
4103
+ await this._doClose();
4304
4104
  return err(toVexSerialError(e, "io"));
4305
4105
  }
4306
4106
  }
4307
4107
  async writeDataAsync(rawData, timeout = 1000) {
4108
+ if (rawData instanceof DeviceBoundPacket) {
4109
+ return this.serializeCommand(rawData.commandId, rawData.commandExtendedId, () => this.writeDataAsyncUnserialized(rawData, timeout));
4110
+ }
4111
+ return this.writeDataAsyncUnserialized(rawData, timeout);
4112
+ }
4113
+ async serializeCommand(commandId, commandExtendedId, operation) {
4114
+ const key = `${commandId}:${commandExtendedId ?? ""}`;
4115
+ const previous = this.pendingCommandTails.get(key) ?? Promise.resolve();
4116
+ let release = () => {};
4117
+ const current = new Promise((resolve) => {
4118
+ release = resolve;
4119
+ });
4120
+ this.pendingCommandTails.set(key, current);
4121
+ await previous;
4122
+ try {
4123
+ return await operation();
4124
+ } finally {
4125
+ release();
4126
+ if (this.pendingCommandTails.get(key) === current) {
4127
+ this.pendingCommandTails.delete(key);
4128
+ }
4129
+ }
4130
+ }
4131
+ async writeDataAsyncUnserialized(rawData, timeout) {
4308
4132
  return new Promise((resolve) => {
4309
- if (this.writer === undefined) {
4310
- resolve(255);
4133
+ if (this.writer === undefined || this._isClosing) {
4134
+ resolve(258);
4311
4135
  return;
4312
4136
  }
4313
4137
  const data = rawData instanceof DeviceBoundPacket ? rawData.data : rawData;
4138
+ const queue = rawData instanceof DeviceBoundPacket ? this.getPendingQueue(rawData.commandId, rawData.commandExtendedId, true) : this.rawCallbacks;
4314
4139
  const cb = {
4140
+ active: true,
4315
4141
  callback: resolve,
4316
4142
  timeout: setTimeout(() => {
4317
- const index = this.callbacksQueue.indexOf(cb);
4318
- if (index === -1)
4143
+ if (!this.removePendingCallback(cb))
4319
4144
  return;
4320
- this.callbacksQueue.splice(index, 1);
4321
4145
  cb.callback(256);
4322
4146
  }, timeout),
4147
+ next: undefined,
4148
+ previous: undefined,
4149
+ queue,
4323
4150
  wantedCommandId: rawData instanceof DeviceBoundPacket ? rawData.commandId : undefined,
4324
4151
  wantedCommandExId: rawData instanceof DeviceBoundPacket ? rawData.commandExtendedId : undefined
4325
4152
  };
4326
- this.callbacksQueue.push(cb);
4153
+ this.enqueuePendingCallback(cb);
4327
4154
  this.writer.write(data).catch(() => {
4328
- const index = this.callbacksQueue.indexOf(cb);
4329
- if (index === -1)
4155
+ if (!this.removePendingCallback(cb))
4330
4156
  return;
4331
- this.callbacksQueue.splice(index, 1);
4332
4157
  clearTimeout(cb.timeout);
4333
4158
  resolve(257);
4334
4159
  });
@@ -4339,6 +4164,9 @@ class VexSerialConnection extends VexEventTarget {
4339
4164
  const result = await this.writeDataAsync(packet, timeout);
4340
4165
  if (result instanceof ReplyType)
4341
4166
  return ok(result);
4167
+ if (result === 258) {
4168
+ return err(new VexNotConnectedError);
4169
+ }
4342
4170
  return err(new VexProtocolError(expectedReplyMessage(packet, ReplyType, result), typeof result === "number" ? result : undefined));
4343
4171
  })());
4344
4172
  }
@@ -4349,56 +4177,48 @@ class VexSerialConnection extends VexEventTarget {
4349
4177
  const { value: readData, done: isDone } = await this.reader.read();
4350
4178
  if (isDone)
4351
4179
  throw new Error("No data");
4352
- cache = binaryArrayJoin(cache, readData);
4180
+ cache.append(readData);
4353
4181
  }
4354
- return cache;
4355
4182
  }
4356
4183
  async startReader() {
4357
- let cache = new Uint8Array([]);
4184
+ const cache = new ReceiveBuffer;
4358
4185
  let sliceIdx = 0;
4359
4186
  for (;; )
4360
4187
  try {
4361
- cache = await this.readData(cache, 5);
4188
+ await this.readData(cache, 5);
4362
4189
  sliceIdx = 0;
4363
- while (!thePacketEncoder.validateHeader(cache)) {
4364
- const nextHeader = cache.findIndex((byte, index) => index > 0 && byte === PacketEncoder.HEADER_TO_HOST[0] && cache[index + 1] === PacketEncoder.HEADER_TO_HOST[1]);
4190
+ while (!thePacketEncoder.validateHeader(cache.bytes)) {
4191
+ const bytes = cache.bytes;
4192
+ const nextHeader = bytes.findIndex((byte, index) => index > 0 && byte === PacketEncoder.HEADER_TO_HOST[0] && bytes[index + 1] === PacketEncoder.HEADER_TO_HOST[1]);
4365
4193
  if (nextHeader >= 0) {
4366
- cache = cache.slice(nextHeader);
4194
+ cache.discard(nextHeader);
4367
4195
  } else {
4368
- cache = cache.slice(cache.at(-1) === PacketEncoder.HEADER_TO_HOST[0] ? -1 : cache.length);
4196
+ cache.discard(bytes.at(-1) === PacketEncoder.HEADER_TO_HOST[0] ? -1 : bytes.length);
4369
4197
  }
4370
- cache = await this.readData(cache, 5);
4198
+ await this.readData(cache, 5);
4371
4199
  }
4372
- const payloadExpectedSize = thePacketEncoder.getPayloadSize(cache);
4373
- const n2 = thePacketEncoder.getHostHeaderLength(cache);
4200
+ const payloadExpectedSize = thePacketEncoder.getPayloadSize(cache.bytes);
4201
+ const n2 = thePacketEncoder.getHostHeaderLength(cache.bytes);
4374
4202
  const totalSize = n2 + payloadExpectedSize;
4375
- cache = await this.readData(cache, totalSize);
4203
+ await this.readData(cache, totalSize);
4376
4204
  sliceIdx = totalSize;
4377
- const cmdId = cache[2];
4205
+ const packet = cache.copy(totalSize);
4206
+ const cmdId = packet[2];
4378
4207
  const hasExtId = cmdId === 88 || cmdId === 86;
4379
- const cmdExId = hasExtId ? cache[n2] : undefined;
4380
- const ack = cache[n2 + 1];
4208
+ const cmdExId = hasExtId ? packet[n2] : undefined;
4209
+ const ack = packet[n2 + 1];
4381
4210
  if (hasExtId) {
4382
- if (!thePacketEncoder.validateMessageCdc(cache))
4383
- throw new Error("Invalid message CDC");
4384
- }
4385
- let matchIdx = -1;
4386
- let untypedIdx = -1;
4387
- for (let i2 = 0;i2 < this.callbacksQueue.length; i2++) {
4388
- const candidate = this.callbacksQueue[i2];
4389
- if (candidate.wantedCommandId === undefined && candidate.wantedCommandExId === undefined) {
4390
- if (untypedIdx === -1)
4391
- untypedIdx = i2;
4211
+ if (!thePacketEncoder.validateMessageCdc(packet)) {
4212
+ this.reportWarning("discarding a reply with an invalid CDC CRC", {
4213
+ commandId: cmdId,
4214
+ commandExtendedId: cmdExId,
4215
+ ack
4216
+ });
4392
4217
  continue;
4393
4218
  }
4394
- if ((candidate.wantedCommandId === undefined || candidate.wantedCommandId === cmdId) && (candidate.wantedCommandExId === undefined || candidate.wantedCommandExId === cmdExId)) {
4395
- matchIdx = i2;
4396
- break;
4397
- }
4398
4219
  }
4399
- if (matchIdx === -1)
4400
- matchIdx = untypedIdx;
4401
- if (matchIdx === -1) {
4220
+ const callbackInfo = this.shiftPendingCallback(cmdId, cmdExId) ?? this.shiftRawCallback();
4221
+ if (callbackInfo === undefined) {
4402
4222
  this.reportWarning("received a reply with no matching request", {
4403
4223
  commandId: cmdId,
4404
4224
  commandExtendedId: cmdExId,
@@ -4406,38 +4226,117 @@ class VexSerialConnection extends VexEventTarget {
4406
4226
  });
4407
4227
  continue;
4408
4228
  }
4409
- const callbackInfo = this.callbacksQueue[matchIdx];
4410
4229
  const wantedCmdId = callbackInfo.wantedCommandId;
4411
4230
  const wantedCmdExId = callbackInfo.wantedCommandExId;
4412
- const data = cache.slice(0, sliceIdx);
4413
4231
  const PackageType = thePacketEncoder.getPacketType(wantedCmdId, wantedCmdExId);
4414
4232
  if (wantedCmdId === undefined || PackageType === undefined) {
4415
4233
  if (wantedCmdId !== undefined) {
4416
4234
  this.reportWarning("no packet class is registered for the wanted command", { commandId: wantedCmdId, commandExtendedId: wantedCmdExId });
4417
4235
  }
4418
- callbackInfo.callback(data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength));
4236
+ callbackInfo.callback(packet.buffer);
4419
4237
  } else {
4420
- if (!hasExtId || PackageType.isValidPacket(data, n2)) {
4421
- callbackInfo.callback(new PackageType(data));
4238
+ if (PackageType.isValidPacket(packet, n2)) {
4239
+ callbackInfo.callback(new PackageType(packet));
4422
4240
  } else {
4423
4241
  this.reportWarning("reply failed packet validation; delivering its ack instead", { commandId: cmdId, commandExtendedId: cmdExId, ack });
4424
4242
  callbackInfo.callback(ack);
4425
4243
  }
4426
4244
  }
4427
4245
  clearTimeout(callbackInfo.timeout);
4428
- this.callbacksQueue.splice(matchIdx, 1);
4429
4246
  } catch (e) {
4430
4247
  if (!(e instanceof Error && e.message === "No data")) {
4431
4248
  this.reportWarning("reader loop stopped by a read error", {
4432
4249
  error: e,
4433
- pendingBytes: cache
4250
+ pendingBytes: cache.bytes
4434
4251
  });
4435
4252
  }
4436
4253
  await this.close();
4437
4254
  break;
4438
4255
  } finally {
4439
- cache = cache.slice(sliceIdx);
4256
+ cache.discard(sliceIdx);
4257
+ }
4258
+ }
4259
+ pendingKey(commandId, commandExtendedId) {
4260
+ return `${commandId}:${commandExtendedId ?? ""}`;
4261
+ }
4262
+ getPendingQueue(commandId, commandExtendedId, create) {
4263
+ const key = this.pendingKey(commandId, commandExtendedId);
4264
+ let queue = this.pendingCallbacks.get(key);
4265
+ if (queue === undefined && create) {
4266
+ const created = { head: undefined, tail: undefined };
4267
+ this.pendingCallbacks.set(key, created);
4268
+ queue = created;
4269
+ }
4270
+ return queue ?? { head: undefined, tail: undefined };
4271
+ }
4272
+ enqueuePendingCallback(callback) {
4273
+ const tail = callback.queue.tail;
4274
+ callback.previous = tail;
4275
+ if (tail === undefined)
4276
+ callback.queue.head = callback;
4277
+ else
4278
+ tail.next = callback;
4279
+ callback.queue.tail = callback;
4280
+ }
4281
+ removePendingCallback(callback) {
4282
+ if (!callback.active)
4283
+ return false;
4284
+ callback.active = false;
4285
+ const { queue, previous, next } = callback;
4286
+ if (previous === undefined)
4287
+ queue.head = next;
4288
+ else
4289
+ previous.next = next;
4290
+ if (next === undefined)
4291
+ queue.tail = previous;
4292
+ else
4293
+ next.previous = previous;
4294
+ callback.previous = undefined;
4295
+ callback.next = undefined;
4296
+ if (queue !== this.rawCallbacks && queue.head === undefined && callback.wantedCommandId !== undefined) {
4297
+ this.pendingCallbacks.delete(this.pendingKey(callback.wantedCommandId, callback.wantedCommandExId));
4298
+ }
4299
+ return true;
4300
+ }
4301
+ shiftPendingCallback(commandId, commandExtendedId) {
4302
+ const queue = this.getPendingQueue(commandId, commandExtendedId, false);
4303
+ const callback = queue.head;
4304
+ if (callback !== undefined)
4305
+ this.removePendingCallback(callback);
4306
+ return callback;
4307
+ }
4308
+ shiftRawCallback() {
4309
+ const callback = this.rawCallbacks.head;
4310
+ if (callback !== undefined)
4311
+ this.removePendingCallback(callback);
4312
+ return callback;
4313
+ }
4314
+ hasPendingCallbacks() {
4315
+ return this.pendingCallbacks.size > 0 || this.rawCallbacks.head !== undefined;
4316
+ }
4317
+ drainPendingCallbacks() {
4318
+ const callbacks = [];
4319
+ for (const queue of this.pendingCallbacks.values()) {
4320
+ for (let callback = queue.head;callback; ) {
4321
+ const next = callback.next;
4322
+ callback.active = false;
4323
+ callback.previous = undefined;
4324
+ callback.next = undefined;
4325
+ callbacks.push(callback);
4326
+ callback = next;
4440
4327
  }
4328
+ }
4329
+ for (let callback = this.rawCallbacks.head;callback; ) {
4330
+ const next = callback.next;
4331
+ callback.active = false;
4332
+ callback.previous = undefined;
4333
+ callback.next = undefined;
4334
+ callbacks.push(callback);
4335
+ callback = next;
4336
+ }
4337
+ this.pendingCallbacks.clear();
4338
+ this.rawCallbacks = { head: undefined, tail: undefined };
4339
+ return callbacks;
4441
4340
  }
4442
4341
  query1() {
4443
4342
  return this.request(new Query1H2DPacket, Query1ReplyD2HPacket, 100);
@@ -4555,7 +4454,9 @@ class V5SerialConnection extends VexSerialConnection {
4555
4454
  let bufferOffset = 0;
4556
4455
  const fileBuf = new Uint8Array(fileSize);
4557
4456
  while (bufferOffset < fileSize) {
4558
- const requestedSize = Math.min(bufferChunkSize, fileSize - bufferOffset);
4457
+ const remainingSize = fileSize - bufferOffset;
4458
+ const chunkSize = Math.min(bufferChunkSize, remainingSize);
4459
+ const requestedSize = chunkSize + 3 & ~3;
4559
4460
  const p2Result = await this.request(new ReadFileH2DPacket(nextAddress, requestedSize), ReadFileReplyD2HPacket, 3000);
4560
4461
  if (p2Result.isErr())
4561
4462
  throw p2Result.error;
@@ -4566,9 +4467,10 @@ class V5SerialConnection extends VexSerialConnection {
4566
4467
  if (p2.length <= 0 || p2.length > requestedSize || p2.buf.byteLength !== p2.length) {
4567
4468
  throw new VexTransferError(`ReadFileReplyD2HPacket returned invalid length ${p2.length}`);
4568
4469
  }
4569
- fileBuf.set(new Uint8Array(p2.buf), bufferOffset);
4570
- bufferOffset += p2.length;
4571
- nextAddress += p2.length;
4470
+ const receivedSize = Math.min(p2.length, remainingSize);
4471
+ fileBuf.set(p2.buf.subarray(0, receivedSize), bufferOffset);
4472
+ bufferOffset += receivedSize;
4473
+ nextAddress += receivedSize;
4572
4474
  progressCallback?.(bufferOffset, fileSize);
4573
4475
  }
4574
4476
  transferFailed = false;
@@ -4723,7 +4625,7 @@ class V5SerialConnection extends VexSerialConnection {
4723
4625
  filename: "screen",
4724
4626
  vendor: 15,
4725
4627
  loadAddress: 0,
4726
- size: SCREEN_CAPTURE_MESSAGE_WIDTH * SCREEN_CAPTURE_HEIGHT * SCREEN_CAPTURE_MESSAGE_CHANNELS
4628
+ size: SCREEN_CAPTURE_FRAMEBUFFER_SIZE
4727
4629
  }, 2, progressCallback);
4728
4630
  if (framebuffer.isErr())
4729
4631
  return err(framebuffer.error);
@@ -4748,34 +4650,60 @@ class V5SerialConnection extends VexSerialConnection {
4748
4650
  return this.request(new SelectDashH2DPacket(screen, port), SelectDashReplyD2HPacket);
4749
4651
  }
4750
4652
  }
4751
- function binaryArrayJoin(left, right) {
4752
- const leftSize = left != null ? left.byteLength : 0;
4753
- const rightSize = right != null ? right.byteLength : 0;
4754
- const all = new Uint8Array(leftSize + rightSize);
4755
- if (all.length === 0)
4756
- return new Uint8Array;
4757
- if (left != null)
4758
- all.set(new Uint8Array(left), 0);
4759
- if (right != null)
4760
- all.set(new Uint8Array(right), leftSize);
4761
- return all;
4653
+
4654
+ class ReceiveBuffer {
4655
+ storage = new Uint8Array(0);
4656
+ start = 0;
4657
+ end = 0;
4658
+ get byteLength() {
4659
+ return this.end - this.start;
4660
+ }
4661
+ get bytes() {
4662
+ return this.storage.subarray(this.start, this.end);
4663
+ }
4664
+ append(chunk) {
4665
+ this.reserve(chunk.byteLength);
4666
+ this.storage.set(chunk, this.end);
4667
+ this.end += chunk.byteLength;
4668
+ }
4669
+ copy(length) {
4670
+ return this.bytes.slice(0, length);
4671
+ }
4672
+ discard(length) {
4673
+ this.start += length < 0 ? this.byteLength + length : length;
4674
+ if (this.start < 0)
4675
+ this.start = 0;
4676
+ if (this.start > this.end)
4677
+ this.start = this.end;
4678
+ if (this.start === this.end) {
4679
+ this.start = 0;
4680
+ this.end = 0;
4681
+ } else if (this.start >= 4096 && this.start * 2 >= this.storage.length) {
4682
+ this.storage.copyWithin(0, this.start, this.end);
4683
+ this.end -= this.start;
4684
+ this.start = 0;
4685
+ }
4686
+ }
4687
+ reserve(additional) {
4688
+ const required = this.byteLength + additional;
4689
+ if (this.storage.length - this.end >= additional)
4690
+ return;
4691
+ if (this.storage.length >= required) {
4692
+ this.storage.copyWithin(0, this.start, this.end);
4693
+ this.end = this.byteLength;
4694
+ this.start = 0;
4695
+ return;
4696
+ }
4697
+ const storage = new Uint8Array(Math.max(required, Math.max(64, this.storage.length * 2)));
4698
+ storage.set(this.bytes);
4699
+ this.end = this.byteLength;
4700
+ this.start = 0;
4701
+ this.storage = storage;
4702
+ }
4762
4703
  }
4763
4704
  function getTransferChunkSize(windowSize) {
4764
4705
  return windowSize > 0 && windowSize <= USER_PROG_CHUNK_SIZE ? windowSize : USER_PROG_CHUNK_SIZE;
4765
4706
  }
4766
- function convertScreenCapture(framebuffer) {
4767
- const pixels = new Uint8Array(SCREEN_CAPTURE_WIDTH * SCREEN_CAPTURE_HEIGHT * SCREEN_CAPTURE_CHANNELS);
4768
- for (let row = 0;row < SCREEN_CAPTURE_HEIGHT; row++) {
4769
- for (let column = 0;column < SCREEN_CAPTURE_WIDTH; column++) {
4770
- const source = (row * SCREEN_CAPTURE_MESSAGE_WIDTH + column) * SCREEN_CAPTURE_MESSAGE_CHANNELS;
4771
- const target = (row * SCREEN_CAPTURE_WIDTH + column) * SCREEN_CAPTURE_CHANNELS;
4772
- pixels[target] = framebuffer[source + 2] ?? 0;
4773
- pixels[target + 1] = framebuffer[source + 1] ?? 0;
4774
- pixels[target + 2] = framebuffer[source] ?? 0;
4775
- }
4776
- }
4777
- return pixels;
4778
- }
4779
4707
  function wrapTransfer(conn, operation) {
4780
4708
  return new ResultAsync(conn.withFileTransfer(async () => {
4781
4709
  try {
@@ -4945,7 +4873,7 @@ async function flashFactoryImage(conn, options, pcb) {
4945
4873
  autoRun: true,
4946
4874
  linkedFile: undefined
4947
4875
  };
4948
- const upload = await conn.uploadFileToDevice(writeRequest, (c, t2) => {
4876
+ const upload = await conn.uploadFileToDeviceUnlocked(writeRequest, (c, t2) => {
4949
4877
  pcb(`UPLOAD ${label}`, c, t2);
4950
4878
  });
4951
4879
  if (upload.isErr())
@@ -5072,29 +5000,35 @@ async function runUploadFirmware(state, publicUrl, usingVersion, progressCallbac
5072
5000
  }
5073
5001
  pcb("UNZIP VEXOS", 1, 1);
5074
5002
  return state.withRefreshPaused(async () => {
5075
- const boot = images.find((image) => image.name.endsWith("BOOT.bin"));
5076
- if (boot === undefined) {
5077
- return err(new VexFirmwareError("VEXos archive is missing BOOT.bin"));
5078
- }
5079
- const assertImage = images.find((image) => image.name.endsWith("assets.bin"));
5080
- if (assertImage === undefined) {
5081
- return err(new VexFirmwareError("VEXos archive is missing assets.bin"));
5082
- }
5083
- const bootFlash = await flashFactoryImage(conn, {
5084
- image: boot,
5085
- label: "BOOT",
5086
- downloadTarget: 14
5087
- }, pcb);
5088
- if (bootFlash.isErr())
5089
- return err(bootFlash.error);
5090
- const assetsFlash = await flashFactoryImage(conn, {
5091
- image: assertImage,
5092
- label: "ASSETS",
5093
- downloadTarget: 13
5094
- }, pcb);
5095
- if (assetsFlash.isErr())
5096
- return err(assetsFlash.error);
5097
- return ok(true);
5003
+ try {
5004
+ return await conn.withFileTransfer(async () => {
5005
+ const boot = images.find((image) => image.name.endsWith("BOOT.bin"));
5006
+ if (boot === undefined) {
5007
+ return err(new VexFirmwareError("VEXos archive is missing BOOT.bin"));
5008
+ }
5009
+ const assertImage = images.find((image) => image.name.endsWith("assets.bin"));
5010
+ if (assertImage === undefined) {
5011
+ return err(new VexFirmwareError("VEXos archive is missing assets.bin"));
5012
+ }
5013
+ const bootFlash = await flashFactoryImage(conn, {
5014
+ image: boot,
5015
+ label: "BOOT",
5016
+ downloadTarget: 14
5017
+ }, pcb);
5018
+ if (bootFlash.isErr())
5019
+ return err(bootFlash.error);
5020
+ const assetsFlash = await flashFactoryImage(conn, {
5021
+ image: assertImage,
5022
+ label: "ASSETS",
5023
+ downloadTarget: 13
5024
+ }, pcb);
5025
+ if (assetsFlash.isErr())
5026
+ return err(assetsFlash.error);
5027
+ return ok(true);
5028
+ });
5029
+ } catch (e) {
5030
+ return err(toVexSerialError(e, "firmware"));
5031
+ }
5098
5032
  });
5099
5033
  }
5100
5034
  function getValue(state, key) {
@@ -5634,6 +5568,11 @@ function unrefTimerIfPossible(timer) {
5634
5568
  if (typeof unref === "function")
5635
5569
  unref.call(timer);
5636
5570
  }
5571
+ function describePort(port) {
5572
+ const info = port.getInfo();
5573
+ const identifier = info.path ?? info.id ?? info.serialNumber;
5574
+ return typeof identifier === "string" ? identifier : undefined;
5575
+ }
5637
5576
 
5638
5577
  class V5SerialDevice extends VexSerialDevice {
5639
5578
  autoReconnect = true;
@@ -5643,8 +5582,11 @@ class V5SerialDevice extends VexSerialDevice {
5643
5582
  _refreshInterval;
5644
5583
  state = new V5SerialDeviceState(this);
5645
5584
  _disposed = false;
5585
+ _lifecycleGeneration = 0;
5586
+ _disconnectListener;
5646
5587
  _refreshGeneration = 0;
5647
5588
  _autoRefresh = false;
5589
+ _refreshIntervalMs = 200;
5648
5590
  _isLastRefreshComplete = true;
5649
5591
  _brain = new V5Brain(this.state);
5650
5592
  _controllers = [
@@ -5653,8 +5595,15 @@ class V5SerialDevice extends VexSerialDevice {
5653
5595
  ];
5654
5596
  _radio = new V5Radio(this.state);
5655
5597
  _deviceFacades = [];
5656
- constructor(defaultSerial, autoRefresh = false) {
5598
+ _emitSafely(eventName, data) {
5599
+ try {
5600
+ this.emit(eventName, data);
5601
+ } catch {}
5602
+ }
5603
+ constructor(defaultSerial, options = false) {
5657
5604
  super(defaultSerial);
5605
+ const autoRefresh = typeof options === "boolean" ? options : options.autoRefresh ?? false;
5606
+ this.refreshIntervalMs = typeof options === "boolean" ? 200 : options.refreshIntervalMs ?? 200;
5658
5607
  this.autoRefresh = autoRefresh;
5659
5608
  }
5660
5609
  get autoRefresh() {
@@ -5670,6 +5619,21 @@ class V5SerialDevice extends VexSerialDevice {
5670
5619
  this._stopRefreshInterval();
5671
5620
  }
5672
5621
  }
5622
+ get refreshIntervalMs() {
5623
+ return this._refreshIntervalMs;
5624
+ }
5625
+ set refreshIntervalMs(value) {
5626
+ if (!Number.isFinite(value) || value <= 0) {
5627
+ throw new VexInvalidArgumentError("refreshIntervalMs must be a positive finite number");
5628
+ }
5629
+ if (this._refreshIntervalMs === value)
5630
+ return;
5631
+ this._refreshIntervalMs = value;
5632
+ if (this._refreshInterval !== undefined) {
5633
+ this._stopRefreshInterval();
5634
+ this._startRefreshInterval();
5635
+ }
5636
+ }
5673
5637
  _startRefreshInterval() {
5674
5638
  if (this._refreshInterval !== undefined || this._disposed)
5675
5639
  return;
@@ -5687,16 +5651,16 @@ class V5SerialDevice extends VexSerialDevice {
5687
5651
  try {
5688
5652
  const r2 = await this.refresh();
5689
5653
  if (r2.isErr())
5690
- this.emit("error", r2.error);
5654
+ this._emitSafely("error", r2.error);
5691
5655
  } catch (error) {
5692
- this.emit("error", error);
5656
+ this._emitSafely("error", error);
5693
5657
  } finally {
5694
5658
  this._isLastRefreshComplete = true;
5695
5659
  }
5696
5660
  })();
5697
5661
  }
5698
5662
  }
5699
- }, 200);
5663
+ }, this._refreshIntervalMs);
5700
5664
  unrefTimerIfPossible(this._refreshInterval);
5701
5665
  }
5702
5666
  _stopRefreshInterval() {
@@ -5759,58 +5723,125 @@ class V5SerialDevice extends VexSerialDevice {
5759
5723
  })());
5760
5724
  }
5761
5725
  connect(conn) {
5762
- return new ResultAsync(this._connect(conn));
5726
+ if (this._disposed) {
5727
+ return new ResultAsync(Promise.resolve(this._staleLifecycleResult()));
5728
+ }
5729
+ if (this.isConnected)
5730
+ return new ResultAsync(Promise.resolve(ok(undefined)));
5731
+ const generation = ++this._lifecycleGeneration;
5732
+ return new ResultAsync(this._connect(conn, generation));
5763
5733
  }
5764
- async _connect(conn) {
5734
+ async _connect(conn, generation = this._lifecycleGeneration) {
5735
+ if (!this._isLifecycleCurrent(generation)) {
5736
+ return this._staleLifecycleResult();
5737
+ }
5765
5738
  if (this.isConnected)
5766
5739
  return ok(undefined);
5767
5740
  if (conn != null) {
5768
5741
  if (!conn.isConnected) {
5769
5742
  const opened = await conn.open();
5743
+ if (!this._isLifecycleCurrent(generation)) {
5744
+ await conn.close();
5745
+ return this._staleLifecycleResult();
5746
+ }
5770
5747
  if (opened.isErr() || opened.value !== "opened") {
5771
5748
  return err(new VexIoError("failed to open the supplied connection"));
5772
5749
  }
5773
5750
  }
5774
5751
  const q = await conn.query1();
5752
+ if (!this._isLifecycleCurrent(generation)) {
5753
+ await conn.close();
5754
+ return this._staleLifecycleResult();
5755
+ }
5775
5756
  if (q.isErr()) {
5776
5757
  await conn.close();
5777
5758
  return err(q.error);
5778
5759
  }
5779
- this.connection = conn;
5760
+ if (!this._commitConnection(conn, generation)) {
5761
+ await conn.close();
5762
+ return this._staleLifecycleResult();
5763
+ }
5780
5764
  } else {
5781
5765
  let tryIdx = 0;
5766
+ let canRequestPort = true;
5767
+ const attemptedPorts = new Set;
5768
+ const attemptedPortNames = [];
5782
5769
  while (true) {
5783
- const c = new V5SerialConnection(this.defaultSerial);
5784
- const result = await c.open(tryIdx++, true);
5770
+ if (!this._isLifecycleCurrent(generation)) {
5771
+ return this._staleLifecycleResult();
5772
+ }
5773
+ const c = this.createConnection();
5774
+ let result = await c.open(tryIdx++, false);
5775
+ if (!this._isLifecycleCurrent(generation)) {
5776
+ await c.close();
5777
+ return this._staleLifecycleResult();
5778
+ }
5779
+ if (result.isOk() && result.value === "no-port" && canRequestPort) {
5780
+ canRequestPort = false;
5781
+ result = await c.open(tryIdx, true);
5782
+ if (!this._isLifecycleCurrent(generation)) {
5783
+ await c.close();
5784
+ return this._staleLifecycleResult();
5785
+ }
5786
+ }
5785
5787
  if (result.isErr()) {
5786
5788
  await c.close();
5787
5789
  return err(result.error);
5788
5790
  }
5789
5791
  if (result.value === "no-port") {
5790
- return err(new VexNotConnectedError("no V5 device was found"));
5792
+ const attempted = attemptedPortNames.length ? `; attempted ${attemptedPortNames.join(", ")}` : "";
5793
+ return err(new VexNotConnectedError(`no responsive V5 device was found${attempted}`));
5791
5794
  }
5792
5795
  if (result.value === "busy") {
5793
5796
  await c.close();
5794
- continue;
5797
+ return err(new VexNotConnectedError("the selected V5 serial port is busy"));
5798
+ }
5799
+ const port = c.port;
5800
+ if (port !== undefined && attemptedPorts.has(port)) {
5801
+ await c.close();
5802
+ const portName = describePort(port);
5803
+ return 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`));
5804
+ }
5805
+ if (port !== undefined) {
5806
+ attemptedPorts.add(port);
5807
+ const portName = describePort(port);
5808
+ if (portName !== undefined)
5809
+ attemptedPortNames.push(portName);
5795
5810
  }
5796
5811
  const q = await c.query1();
5812
+ if (!this._isLifecycleCurrent(generation)) {
5813
+ await c.close();
5814
+ return this._staleLifecycleResult();
5815
+ }
5797
5816
  if (q.isErr()) {
5798
5817
  await c.close();
5799
5818
  continue;
5800
5819
  }
5801
- this.connection = c;
5820
+ if (!this._commitConnection(c, generation)) {
5821
+ await c.close();
5822
+ return this._staleLifecycleResult();
5823
+ }
5802
5824
  break;
5803
5825
  }
5804
5826
  }
5805
- if (!this.isConnected)
5827
+ const connection = this.connection;
5828
+ if (!this._isLifecycleCurrent(generation) || connection == null) {
5829
+ return this._staleLifecycleResult();
5830
+ }
5831
+ if (!connection.isConnected)
5806
5832
  return err(new VexNotConnectedError);
5807
- await this.doAfterConnect();
5833
+ const initialized = await this.doAfterConnect(connection, generation);
5834
+ if (initialized.isErr())
5835
+ return initialized;
5808
5836
  return ok(undefined);
5809
5837
  }
5810
5838
  async disconnect() {
5839
+ this._lifecycleGeneration++;
5840
+ this._refreshGeneration++;
5811
5841
  this._isDisconnecting = true;
5812
5842
  const connection = this.connection;
5813
5843
  this.connection = undefined;
5844
+ this._detachDisconnectListener();
5814
5845
  try {
5815
5846
  await connection?.close();
5816
5847
  } finally {
@@ -5824,9 +5855,21 @@ class V5SerialDevice extends VexSerialDevice {
5824
5855
  await this.disconnect();
5825
5856
  }
5826
5857
  reconnect(timeout = 0) {
5827
- return new ResultAsync(this._reconnect(timeout));
5858
+ if (this._disposed) {
5859
+ return new ResultAsync(Promise.resolve(this._staleLifecycleResult()));
5860
+ }
5861
+ if (timeout < 0) {
5862
+ return new ResultAsync(Promise.resolve(err(new VexInvalidArgumentError("timeout must be non-negative"))));
5863
+ }
5864
+ if (this.isConnected)
5865
+ return new ResultAsync(Promise.resolve(ok(undefined)));
5866
+ const generation = this._isReconnecting ? this._lifecycleGeneration : ++this._lifecycleGeneration;
5867
+ return new ResultAsync(this._reconnect(timeout, generation));
5828
5868
  }
5829
- async _reconnect(timeout) {
5869
+ async _reconnect(timeout, generation = this._lifecycleGeneration) {
5870
+ if (!this._isLifecycleCurrent(generation)) {
5871
+ return this._staleLifecycleResult();
5872
+ }
5830
5873
  if (this.isConnected)
5831
5874
  return ok(undefined);
5832
5875
  if (timeout < 0) {
@@ -5842,6 +5885,9 @@ class V5SerialDevice extends VexSerialDevice {
5842
5885
  return err(new VexNotConnectedError);
5843
5886
  }
5844
5887
  }
5888
+ if (!this._isLifecycleCurrent(generation)) {
5889
+ return this._staleLifecycleResult();
5890
+ }
5845
5891
  if (this.isConnected)
5846
5892
  return ok(undefined);
5847
5893
  }
@@ -5850,8 +5896,15 @@ class V5SerialDevice extends VexSerialDevice {
5850
5896
  while (timeout === 0 || Date.now() < endTime) {
5851
5897
  let tryIdx = 0;
5852
5898
  while (true) {
5853
- const c = new V5SerialConnection(this.defaultSerial);
5899
+ if (!this._isLifecycleCurrent(generation)) {
5900
+ return this._staleLifecycleResult();
5901
+ }
5902
+ const c = this.createConnection();
5854
5903
  const result = await c.open(tryIdx++, false);
5904
+ if (!this._isLifecycleCurrent(generation)) {
5905
+ await c.close();
5906
+ return this._staleLifecycleResult();
5907
+ }
5855
5908
  if (result.isErr()) {
5856
5909
  await c.close();
5857
5910
  return err(result.error);
@@ -5863,6 +5916,10 @@ class V5SerialDevice extends VexSerialDevice {
5863
5916
  continue;
5864
5917
  }
5865
5918
  const status = await c.getSystemStatus(200);
5919
+ if (!this._isLifecycleCurrent(generation)) {
5920
+ await c.close();
5921
+ return this._staleLifecycleResult();
5922
+ }
5866
5923
  if (status.isErr()) {
5867
5924
  await c.close();
5868
5925
  continue;
@@ -5871,23 +5928,38 @@ class V5SerialDevice extends VexSerialDevice {
5871
5928
  await c.close();
5872
5929
  continue;
5873
5930
  }
5874
- this.connection = c;
5931
+ if (!this._commitConnection(c, generation)) {
5932
+ await c.close();
5933
+ return this._staleLifecycleResult();
5934
+ }
5875
5935
  break;
5876
5936
  }
5877
5937
  if (this.isConnected)
5878
5938
  break;
5879
5939
  const getPortCount = async () => (await this.defaultSerial.getPorts()).length;
5880
5940
  const portsCount = await getPortCount();
5941
+ if (!this._isLifecycleCurrent(generation)) {
5942
+ return this._staleLifecycleResult();
5943
+ }
5881
5944
  await sleepUntilAsync(async () => await getPortCount() !== portsCount, 1000);
5945
+ if (!this._isLifecycleCurrent(generation)) {
5946
+ return this._staleLifecycleResult();
5947
+ }
5882
5948
  }
5883
5949
  } catch (e) {
5884
5950
  return err(toVexSerialError(e));
5885
5951
  } finally {
5886
5952
  this._isReconnecting = false;
5887
5953
  }
5888
- if (!this.isConnected)
5954
+ const connection = this.connection;
5955
+ if (!this._isLifecycleCurrent(generation) || connection == null) {
5956
+ return this._staleLifecycleResult();
5957
+ }
5958
+ if (!connection.isConnected)
5889
5959
  return err(new VexNotConnectedError);
5890
- await this.doAfterConnect();
5960
+ const initialized = await this.doAfterConnect(connection, generation);
5961
+ if (initialized.isErr())
5962
+ return initialized;
5891
5963
  return ok(undefined);
5892
5964
  }
5893
5965
  async waitForReconnectToFinish() {
@@ -5897,18 +5969,69 @@ class V5SerialDevice extends VexSerialDevice {
5897
5969
  return;
5898
5970
  }
5899
5971
  }
5900
- async doAfterConnect() {
5901
- if (this.connection == null)
5902
- return;
5903
- this.connection.on("disconnected", (_data) => {
5904
- if (this._isDisconnecting)
5972
+ createConnection() {
5973
+ return new V5SerialConnection(this.defaultSerial);
5974
+ }
5975
+ _isLifecycleCurrent(generation) {
5976
+ return generation === this._lifecycleGeneration && !this._disposed;
5977
+ }
5978
+ _staleLifecycleResult() {
5979
+ return err(new VexNotConnectedError("connection attempt was superseded"));
5980
+ }
5981
+ _commitConnection(connection, generation) {
5982
+ if (!this._isLifecycleCurrent(generation))
5983
+ return false;
5984
+ this._detachDisconnectListener();
5985
+ this.connection = connection;
5986
+ return true;
5987
+ }
5988
+ _detachDisconnectListener() {
5989
+ const subscription = this._disconnectListener;
5990
+ this._disconnectListener = undefined;
5991
+ subscription?.connection.remove("disconnected", subscription.listener);
5992
+ }
5993
+ async doAfterConnect(connection, generation) {
5994
+ if (!this._isLifecycleCurrent(generation) || this.connection !== connection) {
5995
+ return this._staleLifecycleResult();
5996
+ }
5997
+ const listener = () => {
5998
+ if (this._isDisconnecting || !this._isLifecycleCurrent(generation) || this.connection !== connection) {
5905
5999
  return;
5906
- this.emit("disconnected", undefined);
5907
- if (this.autoReconnect) {
5908
- this.reconnect().mapErr((e) => this.emit("error", e));
5909
6000
  }
5910
- });
5911
- await this.refresh();
6001
+ this._lifecycleGeneration++;
6002
+ this._refreshGeneration++;
6003
+ this.connection = undefined;
6004
+ this._detachDisconnectListener();
6005
+ this._emitSafely("disconnected", undefined);
6006
+ if (this.autoReconnect && !this._disposed) {
6007
+ this.reconnect().mapErr((error) => this._emitSafely("error", error));
6008
+ }
6009
+ };
6010
+ connection.on("disconnected", listener);
6011
+ this._disconnectListener = { connection, listener };
6012
+ const refreshed = await this.refresh();
6013
+ if (!this._isLifecycleCurrent(generation) || this.connection !== connection || !connection.isConnected) {
6014
+ return this._staleLifecycleResult();
6015
+ }
6016
+ if (refreshed.isErr()) {
6017
+ await this._discardConnection(connection, generation);
6018
+ return err(refreshed.error);
6019
+ }
6020
+ if (!refreshed.value) {
6021
+ await this._discardConnection(connection, generation);
6022
+ return err(new VexNotConnectedError("initial device refresh did not produce a current snapshot"));
6023
+ }
6024
+ return ok(undefined);
6025
+ }
6026
+ async _discardConnection(connection, generation) {
6027
+ if (!this._isLifecycleCurrent(generation) || this.connection !== connection) {
6028
+ return;
6029
+ }
6030
+ this._lifecycleGeneration++;
6031
+ this._refreshGeneration++;
6032
+ this.connection = undefined;
6033
+ this._detachDisconnectListener();
6034
+ await connection.close();
5912
6035
  }
5913
6036
  refresh() {
5914
6037
  return new ResultAsync(this._refresh());
@@ -5922,31 +6045,15 @@ class V5SerialDevice extends VexSerialDevice {
5922
6045
  this._applySnapshotIfCurrent(generation, { isAvailable: false });
5923
6046
  return ok(false);
5924
6047
  }
5925
- const ssPacket = await conn.getSystemStatus();
5926
- if (generation !== this._refreshGeneration || this._disposed)
5927
- return ok(false);
5928
- if (ssPacket.isErr()) {
5929
- this._applySnapshotIfCurrent(generation, { isAvailable: false });
5930
- return ok(false);
5931
- }
5932
- const sfPacket = await conn.getSystemFlags();
5933
- if (generation !== this._refreshGeneration || this._disposed)
5934
- return ok(false);
5935
- if (sfPacket.isErr()) {
5936
- this._applySnapshotIfCurrent(generation, { isAvailable: false });
5937
- return ok(false);
5938
- }
5939
- const rdPacket = await conn.getRadioStatus();
5940
- if (generation !== this._refreshGeneration || this._disposed)
5941
- return ok(false);
5942
- if (rdPacket.isErr()) {
5943
- this._applySnapshotIfCurrent(generation, { isAvailable: false });
5944
- return ok(false);
5945
- }
5946
- const dsPacket = await conn.getDeviceStatus();
6048
+ const [ssPacket, sfPacket, rdPacket, dsPacket] = await Promise.all([
6049
+ conn.getSystemStatus(),
6050
+ conn.getSystemFlags(),
6051
+ conn.getRadioStatus(),
6052
+ conn.getDeviceStatus()
6053
+ ]);
5947
6054
  if (generation !== this._refreshGeneration || this._disposed)
5948
6055
  return ok(false);
5949
- if (dsPacket.isErr()) {
6056
+ if (ssPacket.isErr() || sfPacket.isErr() || rdPacket.isErr() || dsPacket.isErr()) {
5950
6057
  this._applySnapshotIfCurrent(generation, { isAvailable: false });
5951
6058
  return ok(false);
5952
6059
  }
@@ -6003,7 +6110,7 @@ class V5SerialDevice extends VexSerialDevice {
6003
6110
  {
6004
6111
  battery: controller1Battery,
6005
6112
  isAvailable: controller1Available,
6006
- isCharging: (flags2 & 128) !== 0
6113
+ isCharging: undefined
6007
6114
  }
6008
6115
  ],
6009
6116
  radio: {
@@ -6030,7 +6137,26 @@ class V5SerialDevice extends VexSerialDevice {
6030
6137
  }
6031
6138
  this.state.matchMode = snapshot.matchMode;
6032
6139
  this.state.isFieldControllerConnected = snapshot.isFieldControllerConnected;
6033
- Object.assign(this.state.brain, snapshot.brain);
6140
+ const brain = this.state.brain;
6141
+ brain.activeProgram = snapshot.brain.activeProgram;
6142
+ brain.battery.batteryPercent = snapshot.brain.battery.batteryPercent;
6143
+ brain.battery.isCharging = snapshot.brain.battery.isCharging;
6144
+ brain.button.isPressed = snapshot.brain.button.isPressed;
6145
+ brain.button.isDoublePressed = snapshot.brain.button.isDoublePressed;
6146
+ if (brain.cpu0Version.compare(snapshot.brain.cpu0Version) !== 0) {
6147
+ brain.cpu0Version = snapshot.brain.cpu0Version;
6148
+ }
6149
+ if (brain.cpu1Version.compare(snapshot.brain.cpu1Version) !== 0) {
6150
+ brain.cpu1Version = snapshot.brain.cpu1Version;
6151
+ }
6152
+ brain.isAvailable = snapshot.brain.isAvailable;
6153
+ brain.settings.isScreenReversed = snapshot.brain.settings.isScreenReversed;
6154
+ brain.settings.isWhiteTheme = snapshot.brain.settings.isWhiteTheme;
6155
+ brain.settings.usingLanguage = snapshot.brain.settings.usingLanguage;
6156
+ if (brain.systemVersion.compare(snapshot.brain.systemVersion) !== 0) {
6157
+ brain.systemVersion = snapshot.brain.systemVersion;
6158
+ }
6159
+ brain.uniqueId = snapshot.brain.uniqueId;
6034
6160
  Object.assign(this.state.controllers[0], snapshot.controllers[0]);
6035
6161
  Object.assign(this.state.controllers[1], snapshot.controllers[1]);
6036
6162
  Object.assign(this.state.radio, snapshot.radio);
@@ -6039,10 +6165,18 @@ class V5SerialDevice extends VexSerialDevice {
6039
6165
  if (device != null)
6040
6166
  next[device.port] = device;
6041
6167
  }
6042
- this.state.devices = next;
6168
+ if (!sameSmartDeviceSlots(this.state.devices, next)) {
6169
+ this.state.devices = next;
6170
+ }
6043
6171
  return true;
6044
6172
  }
6045
6173
  }
6174
+ function sameSmartDeviceSlots(left, right) {
6175
+ return left.length === right.length && left.every((device, index) => {
6176
+ const next = right[index];
6177
+ 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;
6178
+ });
6179
+ }
6046
6180
 
6047
6181
  class BaseIniBuilder {
6048
6182
  str = "";
@@ -6051,7 +6185,7 @@ class BaseIniBuilder {
6051
6185
  `;
6052
6186
  }
6053
6187
  addComment(comment) {
6054
- this.addLine("; " + comment);
6188
+ this.addLine(comment.length === 0 ? ";" : "; " + comment);
6055
6189
  return this;
6056
6190
  }
6057
6191
  getContent() {
@@ -6069,18 +6203,23 @@ class IniSectionBuilder extends BaseIniBuilder {
6069
6203
  this.object = object;
6070
6204
  this.keyTransform = keyTransform;
6071
6205
  }
6072
- addSingleObjProperty(key, maxValueLength) {
6073
- const value = this.object[key];
6074
- if (value == null || value.toString().length === 0)
6206
+ addSingleObjProperty(key, maxValueLength, omitIfEmpty = false) {
6207
+ const rawValue = this.object[key];
6208
+ if (rawValue == null)
6209
+ return;
6210
+ const value = rawValue.toString();
6211
+ if (omitIfEmpty && value.length === 0)
6075
6212
  return;
6213
+ if (/["\u0000-\u001f\u007f]/.test(value)) {
6214
+ throw new Error(`INI value for ${this.name}.${String(key)} contains an unsupported character`);
6215
+ }
6076
6216
  const formattedKey = this.keyTransform(key).padEnd(12).slice(0, 12);
6077
- const trimmedVal = value.toString().slice(0, maxValueLength);
6078
- const escapedVal = trimmedVal.replace(/["\\\u0000-\u001f\u007f]/g, (character) => `\\x${character.charCodeAt(0).toString(16).padStart(2, "0")}`);
6079
- this.addLine(`${formattedKey} = "${escapedVal}"`);
6217
+ const trimmedValue = value.slice(0, maxValueLength);
6218
+ this.addLine(`${formattedKey} = "${trimmedValue}"`);
6080
6219
  }
6081
- addObjProperty(key, maxValueLength) {
6220
+ addObjProperty(key, maxValueLength, omitIfEmpty = false) {
6082
6221
  for (const k of Array.isArray(key) ? key : [key]) {
6083
- this.addSingleObjProperty(k, maxValueLength);
6222
+ this.addSingleObjProperty(k, maxValueLength, omitIfEmpty);
6084
6223
  }
6085
6224
  return this;
6086
6225
  }
@@ -6098,6 +6237,11 @@ class IniFileBuilder extends BaseIniBuilder {
6098
6237
  this.str += section.getContent();
6099
6238
  return this;
6100
6239
  }
6240
+ addOptionalSection(section) {
6241
+ if (section.getContent().length === 0)
6242
+ return this;
6243
+ return this.addComment("").addSection(section);
6244
+ }
6101
6245
  }
6102
6246
 
6103
6247
  class ProgramIniConfig {
@@ -6128,7 +6272,7 @@ class ProgramIniConfig {
6128
6272
  if (this.program.date.length === 0) {
6129
6273
  this.setProgramDate(new Date);
6130
6274
  }
6131
- 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();
6275
+ 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();
6132
6276
  }
6133
6277
  dec2(value) {
6134
6278
  return (value % 100).toString(10).padStart(2, "0").toUpperCase();
@@ -6138,6 +6282,60 @@ class ProgramIniConfig {
6138
6282
  // src/utils/detect.ts
6139
6283
  import { join } from "path";
6140
6284
 
6285
+ // src/errors.ts
6286
+ var CLI_EXIT_CODE = {
6287
+ FAILURE: 1,
6288
+ USAGE: 2,
6289
+ NO_DEVICE: 3,
6290
+ DEVICE: 4,
6291
+ IO: 5
6292
+ };
6293
+
6294
+ class CliError extends Error {
6295
+ exitCode;
6296
+ constructor(message, exitCode = CLI_EXIT_CODE.FAILURE, options) {
6297
+ super(message, options);
6298
+ this.exitCode = exitCode;
6299
+ this.name = "CliError";
6300
+ }
6301
+ }
6302
+ function exitCodeForSerialError(kind) {
6303
+ switch (kind) {
6304
+ case "invalid-argument":
6305
+ return CLI_EXIT_CODE.USAGE;
6306
+ case "not-connected":
6307
+ return CLI_EXIT_CODE.NO_DEVICE;
6308
+ case "protocol":
6309
+ case "transfer":
6310
+ case "firmware":
6311
+ return CLI_EXIT_CODE.DEVICE;
6312
+ case "download":
6313
+ case "io":
6314
+ return CLI_EXIT_CODE.IO;
6315
+ }
6316
+ }
6317
+ function isVerbose(argv = process.argv, environment = process.env) {
6318
+ return argv.includes("--verbose") || environment.V5X_DEBUG === "1";
6319
+ }
6320
+ function isJsonOutput(argv = process.argv) {
6321
+ return argv.includes("--json");
6322
+ }
6323
+ function formatCliJsonError(error) {
6324
+ return JSON.stringify({
6325
+ error: {
6326
+ message: error instanceof Error ? error.message : String(error),
6327
+ exitCode: cliExitCode(error)
6328
+ }
6329
+ });
6330
+ }
6331
+ function formatCliError(error, verbose) {
6332
+ const detail = verbose && error instanceof Error && error.stack ? error.stack : error instanceof Error ? error.message : String(error);
6333
+ return `${source_default.red("error:")} ${detail}`;
6334
+ }
6335
+ function cliExitCode(error) {
6336
+ return error instanceof CliError ? error.exitCode : CLI_EXIT_CODE.FAILURE;
6337
+ }
6338
+
6141
6339
  // src/utils/guards.ts
6142
6340
  function isRecord(value) {
6143
6341
  return typeof value === "object" && value !== null;
@@ -6145,6 +6343,12 @@ function isRecord(value) {
6145
6343
  function hasErrorCode(error, code) {
6146
6344
  return isRecord(error) && error.code === code;
6147
6345
  }
6346
+ function requireOptionValue(value, option) {
6347
+ if (typeof value === "boolean") {
6348
+ throw new CliError(`${option} requires a value`, CLI_EXIT_CODE.USAGE);
6349
+ }
6350
+ return value;
6351
+ }
6148
6352
 
6149
6353
  // src/utils/detect.ts
6150
6354
  function hasVexideDependency(manifest) {
@@ -6206,7 +6410,40 @@ ${stderr}` : message, stderr);
6206
6410
  }
6207
6411
  }
6208
6412
 
6413
+ // src/utils/concurrency.ts
6414
+ async function mapWithConcurrency(values, concurrency, mapper) {
6415
+ if (!Number.isInteger(concurrency) || concurrency < 1) {
6416
+ throw new Error("concurrency must be a positive integer");
6417
+ }
6418
+ if (values.length === 0)
6419
+ return [];
6420
+ const results = new Array(values.length);
6421
+ let nextIndex = 0;
6422
+ let failed = false;
6423
+ async function worker() {
6424
+ while (!failed) {
6425
+ const index = nextIndex++;
6426
+ if (index >= values.length)
6427
+ return;
6428
+ const value = values[index];
6429
+ try {
6430
+ results[index] = await mapper(value, index);
6431
+ } catch (error) {
6432
+ failed = true;
6433
+ throw error;
6434
+ }
6435
+ }
6436
+ }
6437
+ const workers2 = Array.from({ length: Math.min(concurrency, values.length) }, () => worker());
6438
+ const settled = await Promise.allSettled(workers2);
6439
+ const rejection = settled.find((result) => result.status === "rejected");
6440
+ if (rejection !== undefined)
6441
+ throw rejection.reason;
6442
+ return results;
6443
+ }
6444
+
6209
6445
  // src/utils/project.ts
6446
+ var ARTIFACT_DISCOVERY_CONCURRENCY = 8;
6210
6447
  function stringAt(value, keys) {
6211
6448
  let current = value;
6212
6449
  for (const key of keys) {
@@ -6320,21 +6557,44 @@ async function existingFile(path) {
6320
6557
  return;
6321
6558
  }))?.isFile() ? path : undefined;
6322
6559
  }
6323
- async function newestNamedBinary(root, name) {
6324
- if (!(await stat(root).catch(() => {
6560
+ var binaryDiscoveryOperations = {
6561
+ stat,
6562
+ scan(root, name) {
6563
+ return new Bun.Glob(`**/${name}.bin`).scan({ cwd: root, onlyFiles: true });
6564
+ }
6565
+ };
6566
+ function newestCandidate(candidates) {
6567
+ return candidates.filter((candidate) => candidate !== undefined).sort((left, right) => right.modified - left.modified || (left.path < right.path ? -1 : left.path > right.path ? 1 : 0))[0]?.path;
6568
+ }
6569
+ async function newestNamedBinary(root, name, operations = binaryDiscoveryOperations) {
6570
+ if (!(await operations.stat(root).catch(() => {
6325
6571
  return;
6326
6572
  }))?.isDirectory())
6327
6573
  return;
6328
- let newest;
6329
- const glob = new Bun.Glob(`**/${name}.bin`);
6330
- for await (const relativePath of glob.scan({ cwd: root, onlyFiles: true })) {
6574
+ const conventionalPaths = [
6575
+ resolve(root, "armv7a-vex-v5", "release", `${name}.bin`),
6576
+ resolve(root, "release", `${name}.bin`)
6577
+ ];
6578
+ const conventional = await mapWithConcurrency(conventionalPaths, ARTIFACT_DISCOVERY_CONCURRENCY, async (path) => {
6579
+ const info = await operations.stat(path).catch(() => {
6580
+ return;
6581
+ });
6582
+ return info?.isFile() ? { path, modified: info.mtimeMs } : undefined;
6583
+ });
6584
+ const paths = [];
6585
+ const conventionalPathSet = new Set(conventionalPaths);
6586
+ for await (const relativePath of operations.scan(root, name)) {
6331
6587
  const path = resolve(root, relativePath);
6332
- const { mtimeMs } = await stat(path);
6333
- if (newest === undefined || mtimeMs > newest.modified) {
6334
- newest = { path, modified: mtimeMs };
6335
- }
6588
+ if (!conventionalPathSet.has(path))
6589
+ paths.push(path);
6336
6590
  }
6337
- return newest?.path;
6591
+ const candidates = await mapWithConcurrency(paths, ARTIFACT_DISCOVERY_CONCURRENCY, async (path) => {
6592
+ const info = await operations.stat(path).catch(() => {
6593
+ return;
6594
+ });
6595
+ return info?.isFile() ? { path, modified: info.mtimeMs } : undefined;
6596
+ });
6597
+ return newestCandidate([...conventional, ...candidates]);
6338
6598
  }
6339
6599
  async function findProgramArtifact(project, explicitPath) {
6340
6600
  if (explicitPath !== undefined) {
@@ -6376,9 +6636,17 @@ async function validateProgramArtifact(path, role) {
6376
6636
  return { path, size: info.size };
6377
6637
  }
6378
6638
  async function validateProgramArtifacts(artifacts) {
6639
+ const [hot, cold] = await Promise.allSettled([
6640
+ validateProgramArtifact(artifacts.hot, "hot"),
6641
+ artifacts.cold === undefined ? Promise.resolve(undefined) : validateProgramArtifact(artifacts.cold, "cold")
6642
+ ]);
6643
+ if (hot.status === "rejected")
6644
+ throw hot.reason;
6645
+ if (cold.status === "rejected")
6646
+ throw cold.reason;
6379
6647
  return {
6380
- hot: await validateProgramArtifact(artifacts.hot, "hot"),
6381
- cold: artifacts.cold === undefined ? undefined : await validateProgramArtifact(artifacts.cold, "cold")
6648
+ hot: hot.value,
6649
+ cold: cold.value
6382
6650
  };
6383
6651
  }
6384
6652
  async function findProgramArtifacts(project, explicitPath) {
@@ -7768,8 +8036,9 @@ function formatSerialFailure(message, error) {
7768
8036
  return detail === "" ? `${message}: ${error.kind}` : `${message}: ${error.kind}: ${detail}`;
7769
8037
  }
7770
8038
  function unwrapSerial(result, message) {
7771
- if (result.isErr())
7772
- throw new Error(formatSerialFailure(message, result.error));
8039
+ if (result.isErr()) {
8040
+ throw new CliError(formatSerialFailure(message, result.error), exitCodeForSerialError(result.error.kind), { cause: result.error });
8041
+ }
7773
8042
  return result.value;
7774
8043
  }
7775
8044
 
@@ -7845,6 +8114,13 @@ async function listPorts() {
7845
8114
  return (await Promise.resolve().then(() => (init_src(), exports_src))).list();
7846
8115
  }
7847
8116
  var readTextFile = (path) => Bun.file(path).text();
8117
+ var LINUX_DISCOVERY_CONCURRENCY = 8;
8118
+ var linuxDiscoveryOperations = {
8119
+ readdir: readdir2,
8120
+ realpath,
8121
+ readlink: readlink2,
8122
+ readUsbAttributes: readLinuxUsbDeviceAttributes
8123
+ };
7848
8124
  async function readLinuxUsbDeviceAttributes(device, readText = readTextFile) {
7849
8125
  let current = device;
7850
8126
  for (let i2 = 0;i2 < 5; i2++, current = join4(current, "..")) {
@@ -7873,6 +8149,9 @@ class WebSerialPortAdapter extends EventTarget {
7873
8149
  ondisconnect = () => {};
7874
8150
  port = null;
7875
8151
  controller = null;
8152
+ dataListener = null;
8153
+ errorListener = null;
8154
+ nativePaused = false;
7876
8155
  _readable = null;
7877
8156
  _writable = null;
7878
8157
  constructor(path, info) {
@@ -7903,12 +8182,43 @@ class WebSerialPortAdapter extends EventTarget {
7903
8182
  this._readable = new ReadableStream({
7904
8183
  start: (controller) => {
7905
8184
  this.controller = controller;
7906
- port.on("data", (data) => controller.enqueue(data));
7907
- port.on("error", (error) => {
8185
+ this.dataListener = (data) => {
8186
+ if (this.port !== port || this.controller !== controller)
8187
+ return;
8188
+ if ((controller.desiredSize ?? 1) <= 0) {
8189
+ const pause = port.pause;
8190
+ if (typeof pause === "function" && !this.nativePaused) {
8191
+ pause.call(port);
8192
+ this.nativePaused = true;
8193
+ } else {
8194
+ this.failReadableBackpressure(port, controller);
8195
+ return;
8196
+ }
8197
+ }
8198
+ try {
8199
+ controller.enqueue(data);
8200
+ } catch {
8201
+ return;
8202
+ }
8203
+ if ((controller.desiredSize ?? 1) <= 0 && !this.nativePaused) {
8204
+ const pause = port.pause;
8205
+ if (typeof pause === "function") {
8206
+ pause.call(port);
8207
+ this.nativePaused = true;
8208
+ }
8209
+ }
8210
+ };
8211
+ this.errorListener = (error) => {
8212
+ if (this.port !== port || this.controller !== controller)
8213
+ return;
7908
8214
  controller.error(error);
8215
+ this.controller = null;
7909
8216
  this.close().catch(() => {});
7910
- });
8217
+ };
8218
+ port.on("data", this.dataListener);
8219
+ port.on("error", this.errorListener);
7911
8220
  },
8221
+ pull: () => this.resumeNativePort(port),
7912
8222
  cancel: () => void this.close()
7913
8223
  });
7914
8224
  this._writable = new WritableStream({
@@ -7925,6 +8235,7 @@ class WebSerialPortAdapter extends EventTarget {
7925
8235
  if (!port)
7926
8236
  return;
7927
8237
  this.port = null;
8238
+ this.detachNativeListeners(port);
7928
8239
  try {
7929
8240
  this.controller?.close();
7930
8241
  } catch {}
@@ -7940,6 +8251,28 @@ class WebSerialPortAdapter extends EventTarget {
7940
8251
  this.dispatchEvent(new Event("disconnect"));
7941
8252
  }
7942
8253
  }
8254
+ detachNativeListeners(port) {
8255
+ if (this.dataListener !== null)
8256
+ port.off("data", this.dataListener);
8257
+ if (this.errorListener !== null)
8258
+ port.off("error", this.errorListener);
8259
+ this.dataListener = null;
8260
+ this.errorListener = null;
8261
+ this.nativePaused = false;
8262
+ }
8263
+ resumeNativePort(port) {
8264
+ if (this.port !== port || !this.nativePaused)
8265
+ return;
8266
+ this.nativePaused = false;
8267
+ port.resume?.();
8268
+ }
8269
+ failReadableBackpressure(port, controller) {
8270
+ if (this.port !== port || this.controller !== controller)
8271
+ return;
8272
+ controller.error(new Error("serial input exceeded readable-stream capacity; the native backend cannot pause"));
8273
+ this.controller = null;
8274
+ this.close();
8275
+ }
7943
8276
  async forget() {
7944
8277
  await this.close();
7945
8278
  }
@@ -7948,29 +8281,38 @@ class WebSerialPortAdapter extends EventTarget {
7948
8281
  class WebSerialAdapter extends EventTarget {
7949
8282
  os;
7950
8283
  list;
8284
+ linux;
7951
8285
  onconnect = () => {};
7952
8286
  ondisconnect = () => {};
7953
8287
  ports = new Map;
7954
- constructor(os2 = platform4(), list2 = listPorts) {
8288
+ constructor(os2 = platform4(), list2 = listPorts, linux = linuxDiscoveryOperations) {
7955
8289
  super();
7956
8290
  this.os = os2;
7957
8291
  this.list = list2;
8292
+ this.linux = linux;
7958
8293
  }
7959
8294
  async listLinuxPorts() {
7960
- const ttys = await readdir2("/sys/class/tty").catch(() => []);
7961
- const ports = [];
7962
- for (const name of ttys) {
8295
+ const ttys = await this.linux.readdir("/sys/class/tty").then((names) => names.toSorted()).catch(() => []);
8296
+ const usbAttributes = new Map;
8297
+ const ports = await mapWithConcurrency(ttys, LINUX_DISCOVERY_CONCURRENCY, async (name) => {
7963
8298
  try {
7964
- const device = await realpath(`/sys/class/tty/${name}/device`);
7965
- const subsystem = await readlink2(join4(device, "subsystem")).catch(() => "");
8299
+ const device = await this.linux.realpath(`/sys/class/tty/${name}/device`);
8300
+ const subsystem = await this.linux.readlink(join4(device, "subsystem")).catch(() => "");
7966
8301
  const info = { path: `/dev/${name}` };
7967
8302
  if (subsystem.includes("usb")) {
7968
- Object.assign(info, await readLinuxUsbDeviceAttributes(device));
8303
+ let attributes = usbAttributes.get(device);
8304
+ if (attributes === undefined) {
8305
+ attributes = this.linux.readUsbAttributes(device);
8306
+ usbAttributes.set(device, attributes);
8307
+ }
8308
+ Object.assign(info, await attributes);
7969
8309
  }
7970
- ports.push(info);
7971
- } catch {}
7972
- }
7973
- return ports;
8310
+ return info;
8311
+ } catch {
8312
+ return;
8313
+ }
8314
+ });
8315
+ return ports.filter((port) => port !== undefined);
7974
8316
  }
7975
8317
  async getPorts() {
7976
8318
  if (this.os === "win32") {
@@ -8020,7 +8362,7 @@ var serial = new WebSerialAdapter;
8020
8362
  // src/device.ts
8021
8363
  var V5X_PORT_ENV = "V5X_PORT";
8022
8364
  function normalizePortSelector(selector) {
8023
- const trimmed = selector?.trim();
8365
+ const trimmed = requireOptionValue(selector, "--port")?.trim();
8024
8366
  return trimmed === "" ? undefined : trimmed;
8025
8367
  }
8026
8368
  function resolvePortSelector(options = {}, environment = process.env) {
@@ -8065,7 +8407,7 @@ class SelectedSerialAdapter extends EventTarget {
8065
8407
  });
8066
8408
  if (port)
8067
8409
  return port;
8068
- throw new Error(`No port found matching ${this.selector}`);
8410
+ throw new CliError(`No port found matching ${this.selector}`, CLI_EXIT_CODE.NO_DEVICE);
8069
8411
  }
8070
8412
  }
8071
8413
  function selectSerialPort(baseSerial, selector) {
@@ -8079,7 +8421,7 @@ async function connectV5Device(device = createV5Device()) {
8079
8421
  try {
8080
8422
  const result = await device.connect();
8081
8423
  if (result.isErr()) {
8082
- throw new Error(`v5 device not connected: ${result.error.message ?? result.error.kind}`);
8424
+ throw new CliError(`v5 device not connected: ${result.error.message ?? result.error.kind}`, CLI_EXIT_CODE.NO_DEVICE, { cause: result.error });
8083
8425
  }
8084
8426
  return device;
8085
8427
  } catch (error) {
@@ -8153,13 +8495,28 @@ function formatCatText(bytes) {
8153
8495
  `) ? text : `${text}
8154
8496
  `;
8155
8497
  }
8498
+ function toCatJson(file, bytes, output) {
8499
+ return {
8500
+ command: "cat",
8501
+ file,
8502
+ bytes: bytes.length,
8503
+ ...output === undefined ? { encoding: "base64", data: Buffer.from(bytes).toString("base64") } : { output }
8504
+ };
8505
+ }
8156
8506
  function registerCatCommand(program) {
8157
- program.command("cat <file>", "read a file from flash").option("-o, --output", "write the file bytes to a local path").option("--port", "serial port path or id, defaults to V5X_PORT").action(async (file, options) => {
8507
+ program.command("cat <file>", "read a file from flash").option("-o, --output", "write the file bytes to a local path").option("--json", "print machine-readable JSON").option("--port", "serial port path or id, defaults to V5X_PORT").action(async (file, options) => {
8508
+ const output = requireOptionValue(options.output, "--output");
8158
8509
  const handle = parseBrainFilePath(file);
8159
8510
  await withSelectedV5Device(options, async (device) => {
8160
8511
  const bytes = unwrapSerial(await device.brain.readFile(handle), `failed to read ${file}`);
8161
- if (options.output !== undefined) {
8162
- await Bun.write(options.output, bytes);
8512
+ if (output !== undefined) {
8513
+ await Bun.write(output, bytes);
8514
+ if (options.json === true)
8515
+ printJson(toCatJson(file, bytes, output));
8516
+ return;
8517
+ }
8518
+ if (options.json === true) {
8519
+ printJson(toCatJson(file, bytes));
8163
8520
  return;
8164
8521
  }
8165
8522
  if (process.stdout.isTTY === true)
@@ -8922,12 +9279,14 @@ function unzipSync(data, opts) {
8922
9279
  var FETCH_TIMEOUT_MS = 30000;
8923
9280
  var PROS_ARCHIVE_LIMIT = 64 * 1024 * 1024;
8924
9281
  var PROS_EXTRACTED_LIMIT = 256 * 1024 * 1024;
9282
+ var SCAFFOLD_WRITE_CONCURRENCY = 8;
8925
9283
  var DEFAULT_PROS_TEMPLATE = {
8926
9284
  tag: "4.2.2",
8927
9285
  archiveUrl: "https://github.com/purduesigbots/pros/releases/download/4.2.2/kernel%404.2.2.zip",
8928
9286
  sha256: "f019642af93dc3d164d1c3e67a2a7dc75c795ac6a4d550c9221c480e2e7f4899"
8929
9287
  };
8930
9288
  function parseToolchain(type) {
9289
+ requireOptionValue(type, "--type");
8931
9290
  if (type === "pros" || type === "vexide")
8932
9291
  return type;
8933
9292
  if (type === undefined) {
@@ -9025,14 +9384,20 @@ async function fetchBytes(url, description, limit) {
9025
9384
  return Buffer.concat(chunks, length);
9026
9385
  }
9027
9386
  async function writeFiles(root, files) {
9028
- for (const [relativePath, content] of Object.entries(files)) {
9387
+ const entries = Object.entries(files);
9388
+ const paths = entries.map(([relativePath, content]) => {
9029
9389
  if (isAbsolute(relativePath) || relativePath.split(/[\\/]/).includes("..")) {
9030
9390
  throw new Error(`template contains an unsafe path: ${relativePath}`);
9031
9391
  }
9032
- const path = join5(root, relativePath);
9033
- await mkdir(dirname2(path), { recursive: true });
9392
+ return { path: join5(root, relativePath), content };
9393
+ });
9394
+ const directories = [...new Set(paths.map(({ path }) => dirname2(path)))];
9395
+ await mapWithConcurrency(directories, SCAFFOLD_WRITE_CONCURRENCY, async (directory) => {
9396
+ await mkdir(directory, { recursive: true });
9397
+ });
9398
+ await mapWithConcurrency(paths, SCAFFOLD_WRITE_CONCURRENCY, async ({ path, content }) => {
9034
9399
  await Bun.write(path, content);
9035
- }
9400
+ });
9036
9401
  }
9037
9402
  async function createProsProject(path, names, source) {
9038
9403
  const bytes = await fetchBytes(source.archiveUrl, `download PROS kernel ${source.tag}`, PROS_ARCHIVE_LIMIT);
@@ -9157,10 +9522,10 @@ async function createProject(inputPath, toolchain, nameOrOptions = basename3(res
9157
9522
  try {
9158
9523
  await rename(stagingPath, path);
9159
9524
  } catch (error) {
9160
- const destination = await stat2(path).catch(() => {
9525
+ const destination = await stat2(path, { bigint: true }).catch(() => {
9161
9526
  return;
9162
9527
  });
9163
- if (destination?.isDirectory() && (await readdir3(path)).length === 0) {
9528
+ if (reservation !== undefined && destination?.isDirectory() && destination.dev === reservation.device && destination.ino === reservation.inode && (await readdir3(path)).length === 0) {
9164
9529
  await rmdir(path);
9165
9530
  try {
9166
9531
  await rename(stagingPath, path);
@@ -9294,10 +9659,13 @@ function registerKvCommand(program) {
9294
9659
  console.log(value);
9295
9660
  });
9296
9661
  });
9297
- program.command("kv set <key> <value>", "set a system variable on a brain").option("--port", "serial port path or id, defaults to V5X_PORT").action(async (key, value, options) => {
9662
+ program.command("kv set <key> <value>", "set a system variable on a brain").option("--json", "print machine-readable JSON").option("--port", "serial port path or id, defaults to V5X_PORT").action(async (key, value, options) => {
9298
9663
  await withSelectedV5Device(options, async (device) => {
9299
9664
  unwrapSerial(await device.brain.setValue(key, value), `failed to set ${key} to ${value}`);
9300
- console.log(`set ${key} to ${value}`);
9665
+ if (options.json === true)
9666
+ printJson({ command: "kv set", key, value, set: true });
9667
+ else
9668
+ console.log(`set ${key} to ${value}`);
9301
9669
  });
9302
9670
  });
9303
9671
  }
@@ -9312,8 +9680,9 @@ function assertProjectNameArgument(name) {
9312
9680
  function registerNewCommand(program) {
9313
9681
  program.command("new <name>", "create a new V5 program", { alias: "n" }).option("-t, --type", "project toolchain (required: pros or vexide)").option("-p, --path", "destination path").option("--json", "print machine-readable JSON").action(async (name, options) => {
9314
9682
  const toolchain = parseToolchain(options.type);
9683
+ const destinationPath = requireOptionValue(options.path, "--path");
9315
9684
  assertProjectNameArgument(name);
9316
- const destination = options.path === undefined ? join6(process.cwd(), name) : resolve4(options.path);
9685
+ const destination = destinationPath === undefined ? join6(process.cwd(), name) : resolve4(destinationPath);
9317
9686
  const path = await createProject(destination, toolchain, {
9318
9687
  displayName: name,
9319
9688
  cargoPackageName: toolchain === "vexide" ? name : basename5(destination),
@@ -9366,38 +9735,44 @@ function registerProgramsCommand(program) {
9366
9735
  console.log(renderTable(["slot", "requested", "name", "size", "timestamp", "file"], formatProgramRows(programs)));
9367
9736
  });
9368
9737
  });
9369
- program.command("start <slot>", "start a program slot on the V5 brain").option("--port", "serial port path or id, defaults to V5X_PORT").action(async (slot, options) => {
9738
+ program.command("start <slot>", "start a program slot on the V5 brain").option("--json", "print machine-readable JSON").option("--port", "serial port path or id, defaults to V5X_PORT").action(async (slot, options) => {
9370
9739
  const slotNumber = parseSlotArgument(slot);
9371
9740
  await withSelectedV5Device(options, async (device) => {
9372
9741
  unwrapSerial(await device.brain.runProgram(slotNumber), `failed to start slot ${slot}`);
9373
- console.log(`started slot ${slot}`);
9742
+ if (options.json === true)
9743
+ printJson({ command: "start", slot: slotNumber, started: true });
9744
+ else
9745
+ console.log(`started slot ${slot}`);
9374
9746
  });
9375
9747
  });
9376
- program.command("stop", "stop the running program on the V5 brain").option("--port", "serial port path or id, defaults to V5X_PORT").action(async (options) => {
9748
+ program.command("stop", "stop the running program on the V5 brain").option("--json", "print machine-readable JSON").option("--port", "serial port path or id, defaults to V5X_PORT").action(async (options) => {
9377
9749
  await withSelectedV5Device(options, async (device) => {
9378
9750
  unwrapSerial(await device.brain.stopProgram(), "failed to stop program");
9379
- console.log("stopped program");
9751
+ if (options.json === true)
9752
+ printJson({ command: "stop", stopped: true });
9753
+ else
9754
+ console.log("stopped program");
9380
9755
  });
9381
9756
  });
9382
9757
  }
9383
9758
 
9384
9759
  // src/commands/rm.ts
9385
9760
  function registerRmCommand(program) {
9386
- program.command("rm <file>", "erase a file from flash").option("--port", "serial port path or id, defaults to V5X_PORT").action(async (file, options) => {
9761
+ program.command("rm <file>", "erase a file from flash").option("--json", "print machine-readable JSON").option("--port", "serial port path or id, defaults to V5X_PORT").action(async (file, options) => {
9387
9762
  const handle = parseBrainFilePath(file);
9388
9763
  await withSelectedV5Device(options, async (device) => {
9389
9764
  unwrapSerial(await device.brain.removeFile(handle), `failed to erase ${file}`);
9390
- console.log(`erased ${file}`);
9765
+ if (options.json === true)
9766
+ printJson({ command: "rm", file, erased: true });
9767
+ else
9768
+ console.log(`erased ${file}`);
9391
9769
  });
9392
9770
  });
9393
9771
  }
9394
9772
 
9395
9773
  // src/utils/upload.ts
9396
9774
  function resolveSlotOption(slot) {
9397
- if (typeof slot === "boolean") {
9398
- throw new Error("--slot requires a value (e.g. --slot 1); it cannot be passed as a bare flag");
9399
- }
9400
- return Number(slot);
9775
+ return Number(requireOptionValue(slot, "--slot"));
9401
9776
  }
9402
9777
  function resolveBuildOption(build, file) {
9403
9778
  if (build !== undefined)
@@ -9405,14 +9780,18 @@ function resolveBuildOption(build, file) {
9405
9780
  return file === undefined;
9406
9781
  }
9407
9782
  async function uploadProgramFromCommand(path, options, runDefault) {
9783
+ const name = requireOptionValue(options.name, "--name");
9784
+ const description = requireOptionValue(options.description, "--description");
9785
+ const icon = requireOptionValue(options.icon, "--icon");
9786
+ const file = requireOptionValue(options.file, "--file");
9408
9787
  await uploadProgram2({
9409
9788
  path: path ?? process.cwd(),
9410
9789
  slot: resolveSlotOption(options.slot),
9411
- name: options.name,
9412
- description: options.description,
9413
- icon: options.icon,
9414
- artifact: options.file,
9415
- build: resolveBuildOption(options.build, options.file),
9790
+ name,
9791
+ description,
9792
+ icon: icon ?? "default.bmp",
9793
+ artifact: file,
9794
+ build: resolveBuildOption(options.build, file),
9416
9795
  run: options.run ?? runDefault,
9417
9796
  port: options.port,
9418
9797
  command: runDefault ? "run" : "upload",
@@ -9458,11 +9837,17 @@ async function uploadProgram2(options) {
9458
9837
  run: options.run
9459
9838
  });
9460
9839
  const validated = await validateProgramArtifacts(artifacts);
9461
- const bytes = await Bun.file(validated.hot.path).bytes();
9462
- const coldBytes = validated.cold ? await Bun.file(validated.cold.path).bytes() : undefined;
9840
+ const [bytes, coldBytes] = await Promise.allSettled([
9841
+ Bun.file(validated.hot.path).bytes(),
9842
+ validated.cold ? Bun.file(validated.cold.path).bytes() : Promise.resolve(undefined)
9843
+ ]);
9844
+ if (bytes.status === "rejected")
9845
+ throw bytes.reason;
9846
+ if (coldBytes.status === "rejected")
9847
+ throw coldBytes.reason;
9463
9848
  await withSelectedV5Device(options, async (device) => {
9464
9849
  const progress = reportProgress();
9465
- const uploaded = await device.brain.uploadProgram(config2, bytes, coldBytes, progress);
9850
+ const uploaded = await device.brain.uploadProgram(config2, bytes.value, coldBytes.value, progress);
9466
9851
  progress.finish();
9467
9852
  if (uploaded.isErr()) {
9468
9853
  throw new Error(formatSerialFailure("the brain rejected the program upload", uploaded.error));
@@ -9502,18 +9887,25 @@ import { deflateSync } from "zlib";
9502
9887
  var WIDTH = 480;
9503
9888
  var HEIGHT = 272;
9504
9889
  var ROW_BYTES = WIDTH * 3;
9890
+ var KITTY_CHUNK_BYTES = 4096;
9891
+ var KITTY_RAW_CHUNK_BYTES = KITTY_CHUNK_BYTES / 4 * 3;
9892
+ var PNG_CRC32_TABLE = new Uint32Array(256);
9893
+ for (let index = 0;index < PNG_CRC32_TABLE.length; index++) {
9894
+ let value = index;
9895
+ for (let bit = 0;bit < 8; bit++) {
9896
+ value = value >>> 1 ^ (value & 1 ? 3988292384 : 0);
9897
+ }
9898
+ PNG_CRC32_TABLE[index] = value >>> 0;
9899
+ }
9505
9900
  function assertScreenshotSize(bytes) {
9506
9901
  if (bytes.length !== ROW_BYTES * HEIGHT) {
9507
9902
  throw new Error(`bad screenshot size: ${bytes.length}`);
9508
9903
  }
9509
9904
  }
9510
- function crc32(bytes) {
9905
+ function pngCrc32(bytes) {
9511
9906
  let crc2 = 4294967295;
9512
9907
  for (const byte of bytes) {
9513
- crc2 ^= byte;
9514
- for (let bit = 0;bit < 8; bit++) {
9515
- crc2 = crc2 >>> 1 ^ (crc2 & 1 ? 3988292384 : 0);
9516
- }
9908
+ crc2 = crc2 >>> 8 ^ PNG_CRC32_TABLE[(crc2 ^ byte) & 255];
9517
9909
  }
9518
9910
  return (crc2 ^ 4294967295) >>> 0;
9519
9911
  }
@@ -9522,7 +9914,7 @@ function pngChunk(type, data) {
9522
9914
  chunk.writeUInt32BE(data.length, 0);
9523
9915
  chunk.write(type, 4, "ascii");
9524
9916
  chunk.set(data, 8);
9525
- chunk.writeUInt32BE(crc32(chunk.subarray(4, 8 + data.length)), 8 + data.length);
9917
+ chunk.writeUInt32BE(pngCrc32(chunk.subarray(4, 8 + data.length)), 8 + data.length);
9526
9918
  return chunk;
9527
9919
  }
9528
9920
  function encodeScreenshotPng(bytes) {
@@ -9554,6 +9946,7 @@ ${WIDTH} ${HEIGHT}
9554
9946
  ]);
9555
9947
  }
9556
9948
  function parseScreenshotFormat(format) {
9949
+ requireOptionValue(format, "--format");
9557
9950
  if (format === undefined || format === "png" || format === "ppm") {
9558
9951
  return format ?? "png";
9559
9952
  }
@@ -9572,36 +9965,51 @@ function assertScreenshotOptions(options) {
9572
9965
  if (options.json === true && options.output === undefined)
9573
9966
  throw new Error("--json requires --output");
9574
9967
  }
9575
- function shouldPrintKittyRgb() {
9576
- return process.stdout.isTTY === true;
9968
+ function shouldPrintKittyRgb(environment = process.env, isTTY = process.stdout.isTTY) {
9969
+ if (isTTY !== true)
9970
+ return false;
9971
+ const term = environment.TERM?.toLowerCase();
9972
+ const termProgram = environment.TERM_PROGRAM?.toLowerCase();
9973
+ return environment.KITTY_WINDOW_ID !== undefined || environment.WEZTERM_PANE !== undefined || term === "xterm-kitty" || term?.includes("ghostty") === true || termProgram === "kitty" || termProgram === "ghostty" || termProgram === "wezterm";
9577
9974
  }
9578
- function printKittyRgb(bytes) {
9975
+ function* iterateKittyRgb(bytes) {
9579
9976
  assertScreenshotSize(bytes);
9580
- const base64 = Buffer.from(bytes).toString("base64");
9581
- process.stdout.write(`\x1B_Ga=T,f=24,s=${WIDTH},v=${HEIGHT},c=40;${base64}\x1B\\
9582
- `);
9977
+ for (let offset = 0;offset < bytes.length; offset += KITTY_RAW_CHUNK_BYTES) {
9978
+ const payload = Buffer.from(bytes.subarray(offset, offset + KITTY_RAW_CHUNK_BYTES)).toString("base64");
9979
+ const more = offset + KITTY_RAW_CHUNK_BYTES < bytes.length;
9980
+ const control = offset === 0 ? `a=T,f=24,s=${WIDTH},v=${HEIGHT},c=40,m=${more ? 1 : 0}` : `m=${more ? 1 : 0}`;
9981
+ yield `\x1B_G${control};${payload}\x1B\\${more ? "" : `
9982
+ `}`;
9983
+ }
9984
+ }
9985
+ function printKittyRgb(bytes) {
9986
+ for (const chunk of iterateKittyRgb(bytes))
9987
+ process.stdout.write(chunk);
9583
9988
  }
9584
9989
  function registerScreenshotCommand(program) {
9585
9990
  program.command("screenshot", "take a screen capture of the brain", {
9586
9991
  alias: "sc"
9587
9992
  }).option("-o, --output", "write the screenshot to a file").option("--format", "file format for --output: png or ppm", "png").option("--port", "serial port path or id, defaults to V5X_PORT").option("--json", "print machine-readable JSON").action(async (options) => {
9993
+ const output = requireOptionValue(options.output, "--output");
9588
9994
  const format = parseScreenshotFormat(options.format);
9589
- assertScreenshotOptions(options);
9995
+ assertScreenshotOptions({ output, json: options.json });
9590
9996
  await withSelectedV5Device(options, async (device) => {
9591
9997
  const frame = unwrapSerial(await device.brain.captureScreen(), "failed to capture screenshot");
9592
- if (options.output === undefined) {
9998
+ if (output === undefined) {
9593
9999
  if (shouldPrintKittyRgb())
9594
10000
  printKittyRgb(frame);
9595
- else
10001
+ else {
9596
10002
  console.error("use --output to write the screenshot to a file");
10003
+ process.exitCode = 1;
10004
+ }
9597
10005
  return;
9598
10006
  }
9599
10007
  const data = format === "png" ? encodeScreenshotPng(frame) : encodeScreenshotPpm(frame);
9600
- await Bun.write(options.output, data);
10008
+ await Bun.write(output, data);
9601
10009
  if (options.json === true)
9602
- printJson(toScreenshotJson(options.output, format, data.length));
10010
+ printJson(toScreenshotJson(output, format, data.length));
9603
10011
  else
9604
- console.log(`wrote ${options.output}`);
10012
+ console.log(`wrote ${output}`);
9605
10013
  });
9606
10014
  });
9607
10015
  }
@@ -9614,7 +10022,7 @@ function registerUploadCommand(program) {
9614
10022
  }
9615
10023
 
9616
10024
  // src/index.ts
9617
- var program = lib_default2("v5x").version(package_default.version).describe(package_default.description);
10025
+ var program = lib_default2("v5x").version(package_default.version).describe(package_default.description).option("--verbose", "print stack traces for errors");
9618
10026
  var commands = [
9619
10027
  registerBuildCommand,
9620
10028
  registerCleanCommand,
@@ -9637,9 +10045,9 @@ for (const register of commands)
9637
10045
  try {
9638
10046
  await program.parse(process.argv);
9639
10047
  } catch (error) {
9640
- console.error(error instanceof Error ? error.message : String(error));
9641
- process.exitCode = 1;
10048
+ console.error(isJsonOutput() ? formatCliJsonError(error) : formatCliError(error, isVerbose()));
10049
+ process.exitCode = cliExitCode(error);
9642
10050
  }
9643
10051
 
9644
- //# debugId=06B01D0AC71C1A8C64756E2164756E21
10052
+ //# debugId=C77D76845A1AA6D564756E2164756E21
9645
10053
  //# sourceMappingURL=index.js.map