@v5x/serial 0.5.5 → 0.5.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -4,9 +4,8 @@ TypeScript implementation of the VEX V5 serial protocol.
4
4
 
5
5
  See the [serial library documentation](https://docs.v5x.dev/serial/overview) for browser setup, guides, and high-level API reference.
6
6
 
7
- This package provides helpers for connecting to V5 devices over the Web Serial
8
- API, reading device state, transferring files, and working with protocol
9
- packets.
7
+ It covers connecting to V5 devices over the Web Serial API, reading device
8
+ state, transferring files, and working with protocol packets.
10
9
 
11
10
  ## Install
12
11
 
@@ -30,7 +29,10 @@ if (opened.isErr()) {
30
29
  console.error(opened.error); // VexSerialError with a stable .kind
31
30
  return;
32
31
  }
33
- // opened.value: true | false | undefined (opened / busy / no port selected)
32
+ if (opened.value !== "opened") {
33
+ console.error(`Unable to open a compatible port: ${opened.value}`);
34
+ return;
35
+ }
34
36
 
35
37
  const status = await connection.getSystemStatus();
36
38
  if (status.isOk()) {
@@ -47,7 +49,7 @@ can open.
47
49
  The package ships ESM, CommonJS, and TypeScript declarations. Packet and version
48
50
  utilities work in Bun or Node.js, but direct browser device connections require
49
51
  the Web Serial API. The `@v5x/cli` package is separate and currently supports
50
- Linux only.
52
+ Linux and macOS. Windows requires a different CLI serial backend.
51
53
 
52
54
  ## Common Exports
53
55
 
@@ -64,10 +66,9 @@ Linux only.
64
66
 
65
67
  ## Result-returning async APIs
66
68
 
67
- All public async methods return `ResultAsync<T, VexSerialError>` (or
68
- `ResultAsync<T, VexSerialError>`-yielding helpers) so failures are explicit in
69
- the type system rather than thrown. State-changing operations are awaitable and
70
- report errors through the result channel:
69
+ All public async methods return `ResultAsync<T, VexSerialError>`, so failures
70
+ are explicit in the type system rather than thrown. State-changing operations
71
+ are awaitable and report errors through the result channel:
71
72
 
72
73
  ```ts
73
74
  const r = await device.setMatchMode("disabled");
@@ -83,9 +84,9 @@ removed. Migrate to the corresponding `setMatchMode()` / `setActiveProgram()`
83
84
  methods and handle the returned `Result`.
84
85
 
85
86
  Legacy methods that resolved to `false`, `null`, or `undefined` on failure now
86
- return a typed `Err` instead, preserving the prior success value semantics on
87
- the `Ok` channel (e.g. `open()` still resolves to `Ok(true | false |
88
- undefined)`).
87
+ return a typed `Err` instead. `open()` reports non-error connection outcomes on
88
+ the `Ok` channel as `"opened"`, `"busy"`, or `"no-port"`; only `"opened"`
89
+ indicates an established connection.
89
90
 
90
91
  ## Transfers and timeouts
91
92
 
@@ -97,7 +98,7 @@ transfer is active by default.
97
98
  Protocol requests default to a 1,000 ms response timeout. Individual transfer
98
99
  phases use longer timeouts where erase, write, or exit operations need them.
99
100
  `reconnect(0)` waits indefinitely, while a positive reconnect timeout is a total
100
- deadline in milliseconds. A timeout or NACK is reported as a `VexProtocolError`
101
+ deadline in milliseconds. A timeout or NACK surfaces as a `VexProtocolError`
101
102
  (or a related `VexSerialError` subclass) through the `Result` error channel; it
102
103
  does not cancel work already running on the device.
103
104
 
@@ -114,3 +115,33 @@ bun run build
114
115
  ```
115
116
 
116
117
  The build emits JavaScript bundles and TypeScript declarations under `dist/`.
118
+
119
+ ### Packet registry and browser bundle measurement
120
+
121
+ The complete entry point registers every reply class explicitly. Packet
122
+ decoding no longer discovers classes by enumerating the packet-model module at
123
+ runtime. Consumers that only need framing, CRC, packet base classes, and a
124
+ custom reply registry can import `@v5x/serial/packet-core`; that entry point
125
+ does not retain file-transfer, firmware, screenshot, or dashboard models.
126
+ Register only the reply classes the application accepts with
127
+ `PacketEncoder.getInstance().registerPacketTypes(...)`.
128
+
129
+ Run `bun run measure:bundles` in this package to report reproducible minified
130
+ byte sizes for the complete browser entry and the packet-core entry.
131
+
132
+ ### File-transfer window semantics
133
+
134
+ The `windowSize` returned by `InitFileTransferReplyD2HPacket` is the maximum
135
+ data size for one `ReadFile` or `WriteFile` block. It is not a count of blocks
136
+ that may be outstanding. This follows from the wire model: read and write
137
+ replies carry only the shared CDC2 command and function IDs; write replies have
138
+ no address, sequence number, or request identifier with which to correlate an
139
+ individual outstanding block. A read address can validate a reply after it is
140
+ routed, but cannot make write failures unambiguous.
141
+
142
+ Transfers therefore intentionally remain stop-and-wait. The advertised value
143
+ is clamped to the library's 4096-byte block limit and used only as the chunk
144
+ size. There is no safe before/after pipelining benchmark to run because the
145
+ protocol does not expose the correlation needed to enable pipelining without
146
+ risking data corruption. Hardware throughput remains dependent on one USB
147
+ round trip per block.
package/dist/Vex.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ /// <reference types="dom-serial" preserve="true" />
1
2
  import { type VexFirmwareVersion } from "./VexFirmwareVersion.js";
2
3
  import { type HostBoundPacket } from "./VexPacket.js";
3
4
  export declare const USER_PROG_CHUNK_SIZE = 4096;
@@ -134,7 +135,8 @@ export declare enum AckType {
134
135
  CDC2_NACK_FILE_EXISTS = 219,
135
136
  CDC2_NACK_FILE_SYS_FULL = 220,
136
137
  TIMEOUT = 256,
137
- WRITE_ERROR = 257
138
+ WRITE_ERROR = 257,
139
+ NOT_CONNECTED = 258
138
140
  }
139
141
  export declare enum SmartDeviceType {
140
142
  EMPTY = 0,
package/dist/VexCRC.d.ts CHANGED
@@ -2,17 +2,6 @@ export declare class CrcGenerator {
2
2
  crc32Table: Uint32Array;
3
3
  static POLYNOMIAL_CRC32: number;
4
4
  static POLYNOMIAL_CRC16: number;
5
- constructor();
6
- /**
7
- * Calculate CRC16 for buffer
8
- */
9
5
  crc16(buf: Uint8Array, initValue: number): number;
10
- /**
11
- * Generate CRC32 reverse table
12
- */
13
- crc32GenTable(): void;
14
- /**
15
- * Calculate CRC32 for buffer
16
- */
17
6
  crc32(buf: Uint8Array, initValue: number): number;
18
7
  }
@@ -1,3 +1,4 @@
1
+ /// <reference types="dom-serial" preserve="true" />
1
2
  import { AckType, FileDownloadTarget, type IFileBasicInfo, type IFileWriteRequest, type IPacketCallback, type MatchMode, type SlotNumber, type SelectDashScreen } from "./Vex.js";
2
3
  import { VexSerialError } from "./VexError.js";
3
4
  import { VexEventTarget } from "./VexEvent.js";
@@ -5,40 +6,90 @@ import { type ProgramIniConfig } from "./VexIniConfig.js";
5
6
  import { Result, ResultAsync } from "neverthrow";
6
7
  import { MatchStatusReplyD2HPacket, DeviceBoundPacket, MatchModeReplyD2HPacket, GetSystemStatusReplyD2HPacket, type HostBoundPacket, Query1ReplyD2HPacket, LoadFileActionReplyD2HPacket, GetSystemFlagsReplyD2HPacket, GetRadioStatusReplyD2HPacket, GetDeviceStatusReplyD2HPacket, SendDashTouchReplyD2HPacket, SelectDashReplyD2HPacket, ScreenCaptureReplyD2HPacket } from "./VexPacket.js";
7
8
  import { type VexFirmwareVersion } from "./VexFirmwareVersion.js";
9
+ type HostBoundPacketType<T extends HostBoundPacket> = {
10
+ new (data: ArrayBuffer | Uint8Array): T;
11
+ name: string;
12
+ };
13
+ /** Outcome of {@link VexSerialConnection.open}. */
14
+ export type OpenResult = "opened" | "busy" | "no-port";
15
+ /**
16
+ * Payload of the `warning` event: a non-fatal condition the library
17
+ * recovered from, surfaced so embedders can log or ignore it.
18
+ */
19
+ export interface ConnectionWarning {
20
+ message: string;
21
+ details?: unknown;
22
+ }
23
+ export interface VexSerialConnectionEvents {
24
+ connected: undefined;
25
+ disconnected: undefined;
26
+ warning: ConnectionWarning;
27
+ }
8
28
  /**
9
29
  * A connection to a V5 device.
10
- * Emit events: connected, disconnected
30
+ * Emit events: connected, disconnected, warning
11
31
  */
12
- export declare class VexSerialConnection extends VexEventTarget {
32
+ export declare class VexSerialConnection extends VexEventTarget<VexSerialConnectionEvents> {
13
33
  filters: SerialPortFilter[];
14
34
  writer: WritableStreamDefaultWriter<unknown> | undefined;
15
35
  reader: ReadableStreamDefaultReader<unknown> | undefined;
16
36
  port: SerialPort | undefined;
17
37
  serial: Serial;
18
- callbacksQueue: IPacketCallback[];
38
+ private pendingCallbacks;
39
+ private rawCallbacks;
40
+ private pendingCommandTails;
19
41
  private _onPortDisconnect;
20
42
  private _closePromise;
43
+ private _openPromise;
44
+ private _isClosing;
21
45
  private _wasConnected;
22
46
  protected fileTransferTail: Promise<unknown>;
23
47
  protected fileTransferDepth: number;
48
+ /** Pending callbacks, exposed as a snapshot for backwards compatibility. */
49
+ get callbacksQueue(): IPacketCallback[];
24
50
  get isConnected(): boolean;
25
51
  get isFileTransferring(): boolean;
26
52
  constructor(serial: Serial);
53
+ protected reportWarning(message: string, details?: unknown): void;
54
+ /**
55
+ * Connection events are notifications only: a consumer callback must not
56
+ * alter the lifecycle of the serial transport that produced it.
57
+ */
58
+ private emitSafely;
27
59
  close(): Promise<void>;
60
+ private _closeAfterOpen;
28
61
  private _hasOpenResources;
29
62
  private _doClose;
30
63
  /**
31
- * Open a port. The result is `Err` only when a connection is already
32
- * open (programmer error); otherwise the underlying `boolean |
33
- * undefined` semantics are preserved: `true` = opened, `false` =
34
- * the port was busy, `undefined` = no matching port was selected.
64
+ * Open a port. Resolves `"opened"` when a connection is established,
65
+ * `"busy"` when the matching port is already held elsewhere, and
66
+ * `"no-port"` when no matching port was selected. The result is `Err`
67
+ * when a connection is already open (programmer error) or when the
68
+ * port fails to open (permissions, dead device, ...). Concurrent calls
69
+ * join the same open attempt and receive its result.
35
70
  */
36
- open(use?: number | undefined, askUser?: boolean): ResultAsync<boolean | undefined, VexSerialError>;
71
+ open(use?: number, askUser?: boolean): ResultAsync<OpenResult, VexSerialError>;
37
72
  private _open;
38
- writeData(rawData: DeviceBoundPacket | Uint8Array, resolve: (data: HostBoundPacket | ArrayBuffer | AckType) => void, timeout?: number): void;
39
73
  writeDataAsync(rawData: DeviceBoundPacket | Uint8Array, timeout?: number): Promise<HostBoundPacket | ArrayBuffer | AckType>;
40
- protected readData(cache: Uint8Array, expectedSize: number): Promise<Uint8Array>;
74
+ /**
75
+ * A command ID identifies a reply packet type, not an individual request.
76
+ * Keep requests with the same reply identifiers off the wire until the
77
+ * preceding request has resolved, while allowing unrelated commands to run
78
+ * concurrently.
79
+ */
80
+ private serializeCommand;
81
+ private writeDataAsyncUnserialized;
82
+ request<T extends HostBoundPacket>(packet: DeviceBoundPacket, ReplyType: HostBoundPacketType<T>, timeout?: number): ResultAsync<T, VexSerialError>;
83
+ protected readData(cache: ReceiveBuffer, expectedSize: number): Promise<void>;
41
84
  protected startReader(): Promise<void>;
85
+ private pendingKey;
86
+ private getPendingQueue;
87
+ private enqueuePendingCallback;
88
+ private removePendingCallback;
89
+ private shiftPendingCallback;
90
+ private shiftRawCallback;
91
+ private hasPendingCallbacks;
92
+ private drainPendingCallbacks;
42
93
  query1(): ResultAsync<Query1ReplyD2HPacket, VexSerialError>;
43
94
  getSystemVersion(): ResultAsync<VexFirmwareVersion, VexSerialError>;
44
95
  }
@@ -101,3 +152,19 @@ export declare class V5SerialConnection extends VexSerialConnection {
101
152
  mockTouch(x: number, y: number, press: boolean): ResultAsync<SendDashTouchReplyD2HPacket, VexSerialError>;
102
153
  openScreen(screen: number | SelectDashScreen, port: number): ResultAsync<SelectDashReplyD2HPacket, VexSerialError>;
103
154
  }
155
+ /**
156
+ * A growable receive queue. Parsed packets are copied out before their bytes
157
+ * are discarded, so views into this reusable storage never escape the reader.
158
+ */
159
+ declare class ReceiveBuffer {
160
+ private storage;
161
+ private start;
162
+ private end;
163
+ get byteLength(): number;
164
+ get bytes(): Uint8Array<ArrayBuffer>;
165
+ append(chunk: Uint8Array): void;
166
+ copy(length: number): Uint8Array<ArrayBuffer>;
167
+ discard(length: number): void;
168
+ private reserve;
169
+ }
170
+ export {};
@@ -1,3 +1,4 @@
1
+ /// <reference types="dom-serial" preserve="true" />
1
2
  import { type MatchMode } from "./Vex.js";
2
3
  import { V5SerialConnection } from "./VexConnection.js";
3
4
  import { V5Brain, V5Controller, V5Radio, V5SerialDeviceState, V5SmartDevice, VexSerialDevice } from "./VexDeviceState.js";
@@ -5,17 +6,40 @@ import { VexSerialError } from "./VexError.js";
5
6
  import { ResultAsync } from "neverthrow";
6
7
  export { VexSerialDevice, V5Brain, V5Battery, V5BrainButton, V5BrainSettings, V5Controller, V5SmartDevice, V5Radio, V5SerialDeviceState, } from "./VexDeviceState.js";
7
8
  export { sleep, sleepUntil, sleepUntilAsync, downloadFileFromInternet, uploadFirmware, } from "./VexFirmware.js";
9
+ export interface V5SerialDeviceOptions {
10
+ autoRefresh?: boolean;
11
+ refreshIntervalMs?: number;
12
+ }
8
13
  export declare class V5SerialDevice extends VexSerialDevice {
9
14
  autoReconnect: boolean;
10
- autoRefresh: boolean;
11
15
  pauseRefreshOnFileTransfer: boolean;
12
16
  protected _isReconnecting: boolean;
13
17
  private _isDisconnecting;
14
18
  private _refreshInterval;
15
19
  state: V5SerialDeviceState;
16
20
  private _disposed;
21
+ private _lifecycleGeneration;
22
+ private _disconnectListener;
17
23
  private _refreshGeneration;
18
- constructor(defaultSerial: Serial);
24
+ private _autoRefresh;
25
+ private _refreshIntervalMs;
26
+ private _isLastRefreshComplete;
27
+ private readonly _brain;
28
+ private readonly _controllers;
29
+ private readonly _radio;
30
+ private readonly _deviceFacades;
31
+ /**
32
+ * Device lifecycle events are notifications only: consumer callbacks must
33
+ * not alter automatic refresh or reconnect work that produced them.
34
+ */
35
+ private _emitSafely;
36
+ constructor(defaultSerial: Serial, options?: boolean | V5SerialDeviceOptions);
37
+ get autoRefresh(): boolean;
38
+ set autoRefresh(value: boolean);
39
+ get refreshIntervalMs(): number;
40
+ set refreshIntervalMs(value: number);
41
+ private _startRefreshInterval;
42
+ private _stopRefreshInterval;
19
43
  get isV5Controller(): boolean;
20
44
  get brain(): V5Brain;
21
45
  get controllers(): [V5Controller, V5Controller];
@@ -47,7 +71,13 @@ export declare class V5SerialDevice extends VexSerialDevice {
47
71
  reconnect(timeout?: number): ResultAsync<void, VexSerialError>;
48
72
  private _reconnect;
49
73
  private waitForReconnectToFinish;
74
+ protected createConnection(): V5SerialConnection;
75
+ private _isLifecycleCurrent;
76
+ private _staleLifecycleResult;
77
+ private _commitConnection;
78
+ private _detachDisconnectListener;
50
79
  private doAfterConnect;
80
+ private _discardConnection;
51
81
  /**
52
82
  * Refresh the high-level device snapshot. All required replies are
53
83
  * collected before any public state is mutated, so callers never see
@@ -1,3 +1,4 @@
1
+ /// <reference types="dom-serial" preserve="true" />
1
2
  import { type MatchMode, SerialDeviceType, type SlotNumber, type ISmartDeviceInfo, SmartDeviceType, FileVendor, type IProgramInfo, type IFileHandle, type IFileBasicInfo, type IFileWriteRequest, FileDownloadTarget, RadioChannelType } from "./Vex.js";
2
3
  import { type V5SerialConnection } from "./VexConnection.js";
3
4
  import { VexEventTarget } from "./VexEvent.js";
@@ -6,7 +7,11 @@ import { type ProgramIniConfig } from "./VexIniConfig.js";
6
7
  import { VexSerialError } from "./VexError.js";
7
8
  import { ResultAsync } from "neverthrow";
8
9
  import type { V5SerialDevice } from "./VexDevice.js";
9
- export declare abstract class VexSerialDevice extends VexEventTarget {
10
+ export interface VexSerialDeviceEvents {
11
+ disconnected: undefined;
12
+ error: unknown;
13
+ }
14
+ export declare abstract class VexSerialDevice extends VexEventTarget<VexSerialDeviceEvents> {
10
15
  connection: V5SerialConnection | undefined;
11
16
  defaultSerial: Serial;
12
17
  get isConnected(): boolean;
@@ -19,18 +24,24 @@ export declare class V5SerialDeviceState {
19
24
  _instance: V5SerialDevice;
20
25
  /**
21
26
  * Counter used only to pause automatic refresh while a file transfer
22
- * is in flight. This is not a mutex: serialisation of transfer
23
- * operations lives on the {@link V5SerialConnection} transaction
24
- * queue. The counter is exposed via {@link isFileTransferring} so the
25
- * refresh loop can skip work during transfers.
27
+ * is in flight. This is not a mutex: serialization of transfer
28
+ * operations lives on the {@link V5SerialConnection} transaction queue.
26
29
  */
27
30
  private refreshPauseDepth;
31
+ get isRefreshPaused(): boolean;
32
+ /**
33
+ * @deprecated Use {@link isRefreshPaused}. This flag only means that
34
+ * automatic refresh is paused, not that transfer operations are locked.
35
+ */
28
36
  get isFileTransferring(): boolean;
29
37
  /**
30
38
  * Increment the refresh-pause depth, run the operation, and decrement
31
- * the depth again. The actual transfer mutex lives on the connection,
32
- * so this method does not serialise anything; it only signals the
33
- * device that a transfer is in progress so refresh polling is paused.
39
+ * the depth again. The actual transfer mutex lives on the connection.
40
+ */
41
+ withRefreshPaused<T>(operation: () => PromiseLike<T>): Promise<T>;
42
+ /**
43
+ * @deprecated Use {@link withRefreshPaused}. This only pauses automatic
44
+ * refresh and does not lock file transfers.
34
45
  */
35
46
  withFileTransfer<T>(operation: () => PromiseLike<T>): Promise<T>;
36
47
  brain: {
@@ -54,15 +65,18 @@ export declare class V5SerialDeviceState {
54
65
  systemVersion: VexFirmwareVersion;
55
66
  uniqueId: number;
56
67
  };
57
- controllers: ({
58
- battery: number;
59
- isAvailable: boolean;
60
- isCharging: boolean;
61
- } | {
62
- battery: number;
63
- isAvailable: boolean;
64
- isCharging?: undefined;
65
- })[];
68
+ controllers: [
69
+ {
70
+ battery: number;
71
+ isAvailable: boolean;
72
+ isCharging: boolean | undefined;
73
+ },
74
+ {
75
+ battery: number;
76
+ isAvailable: boolean;
77
+ isCharging: boolean | undefined;
78
+ }
79
+ ];
66
80
  devices: Array<ISmartDeviceInfo | undefined>;
67
81
  isFieldControllerConnected: boolean;
68
82
  matchMode: MatchMode;
@@ -80,6 +94,9 @@ export declare class V5SerialDeviceState {
80
94
  }
81
95
  export declare class V5Brain {
82
96
  private readonly state;
97
+ private readonly batteryFacade;
98
+ private readonly buttonFacade;
99
+ private readonly settingsFacade;
83
100
  constructor(state: V5SerialDeviceState);
84
101
  get isRunningProgram(): boolean;
85
102
  get activeProgram(): number;
@@ -160,6 +177,11 @@ export declare class V5Controller {
160
177
  get batteryPercent(): number;
161
178
  get isMasterController(): boolean;
162
179
  get isAvailable(): boolean;
180
+ /**
181
+ * Whether the controller is charging. The V5 system-status response only
182
+ * reports this state for the primary controller, so the partner controller
183
+ * returns `undefined`.
184
+ */
163
185
  get isCharging(): boolean | undefined;
164
186
  }
165
187
  export declare class V5SmartDevice {
@@ -1,3 +1,5 @@
1
+ /// <reference types="dom-serial" preserve="true" />
2
+ import { type AckType } from "./Vex.js";
1
3
  /**
2
4
  * Typed error hierarchy for the serial protocol package.
3
5
  *
@@ -10,6 +12,7 @@ export type VexSerialErrorKind = "not-connected" | "invalid-argument" | "protoco
10
12
  /** Base class for every failure surfaced by the serial package. */
11
13
  export declare class VexSerialError extends Error {
12
14
  readonly kind: VexSerialErrorKind;
15
+ readonly ackType: AckType | undefined;
13
16
  constructor(kind: VexSerialErrorKind, message: string);
14
17
  }
15
18
  /** No connection is currently open to a device. */
@@ -22,7 +25,8 @@ export declare class VexInvalidArgumentError extends VexSerialError {
22
25
  }
23
26
  /** The device refused, timed out, or replied unexpectedly to a command. */
24
27
  export declare class VexProtocolError extends VexSerialError {
25
- constructor(message: string);
28
+ readonly ackType: AckType | undefined;
29
+ constructor(message: string, ackType?: AckType);
26
30
  }
27
31
  /** A file-transfer handshake, read, write, or exit failed. */
28
32
  export declare class VexTransferError extends VexSerialError {
@@ -1,16 +1,20 @@
1
- export declare class VexEventEmitter {
2
- handlerMap: Map<string | symbol, Array<(...args: unknown[]) => void>>;
1
+ type EventName = string | symbol;
2
+ type EventMapKey<TEvents> = Extract<keyof TEvents, EventName>;
3
+ type EventListener<TValue> = (data: TValue) => void;
4
+ export declare class VexEventEmitter<TEvents extends object = Record<EventName, unknown>> {
5
+ handlerMap: Map<EventName, Array<EventListener<unknown>>>;
3
6
  constructor();
4
- on(eventName: string | symbol, listener: (...args: unknown[]) => void): void;
5
- remove(eventName: string | symbol, listener: (...args: unknown[]) => void): void;
6
- emit(eventName: string | symbol, data: unknown): void;
7
+ on<K extends EventMapKey<TEvents>>(eventName: K, listener: EventListener<TEvents[K]>): void;
8
+ remove<K extends EventMapKey<TEvents>>(eventName: K, listener: EventListener<TEvents[K]>): void;
9
+ emit<K extends EventMapKey<TEvents>>(eventName: K, data: TEvents[K]): void;
7
10
  clearListeners(): void;
8
11
  }
9
- export declare class VexEventTarget {
10
- emitter: VexEventEmitter;
12
+ export declare class VexEventTarget<TEvents extends object = Record<EventName, unknown>> {
13
+ emitter: VexEventEmitter<TEvents>;
11
14
  constructor();
12
- emit(eventName: string | symbol, data: unknown): void;
13
- on(eventName: string | symbol, listener: (...args: unknown[]) => void): void;
14
- remove(eventName: string | symbol, listener: (...args: unknown[]) => void): void;
15
+ emit<K extends EventMapKey<TEvents>>(eventName: K, data: TEvents[K]): void;
16
+ on<K extends EventMapKey<TEvents>>(eventName: K, listener: EventListener<TEvents[K]>): void;
17
+ remove<K extends EventMapKey<TEvents>>(eventName: K, listener: EventListener<TEvents[K]>): void;
15
18
  clearListeners(): void;
16
19
  }
20
+ export {};
@@ -1,3 +1,4 @@
1
+ /// <reference types="dom-serial" preserve="true" />
1
2
  import type { V5SerialDeviceState } from "./VexDeviceState.js";
2
3
  import { VexSerialError } from "./VexError.js";
3
4
  import { ResultAsync } from "neverthrow";
@@ -36,10 +37,12 @@ export declare function sleepUntil(f: () => boolean, timeout: number, interval?:
36
37
  * when `ms` is negative.
37
38
  */
38
39
  export declare function sleep(ms: number): ResultAsync<void, VexSerialError>;
40
+ type FirmwareProgressCallback = (state: string, current: number, total: number) => void;
39
41
  /**
40
42
  * Upload a VEXos firmware archive to a connected brain. Network and
41
43
  * archive validation failures are returned as {@link VexSerialError}
42
44
  * values rather than thrown; a device that refuses a step or a missing
43
- * connection surfaces as {@link VexProtocolError} / {@link VexNotConnectedError}.
45
+ * connection surfaces as {@link VexFirmwareError} / {@link VexNotConnectedError}.
44
46
  */
45
- export declare function uploadFirmware(state: V5SerialDeviceState, publicUrl?: string, usingVersion?: string, progressCallback?: (state: string, current: number, total: number) => void): ResultAsync<boolean, VexSerialError>;
47
+ export declare function uploadFirmware(state: V5SerialDeviceState, publicUrl?: string, usingVersion?: string, progressCallback?: FirmwareProgressCallback): ResultAsync<boolean, VexSerialError>;
48
+ export {};
@@ -1,55 +1,16 @@
1
- /**
2
- * This is a class to make it easier to handle passing the VEXos version
3
- * information around in the application. This provides ways to get any
4
- * form of the string representation and quickly compare versions.
5
- */
6
1
  export declare class VexFirmwareVersion {
7
- major: number;
8
- minor: number;
9
- build: number;
10
- beta: number;
2
+ readonly major: number;
3
+ readonly minor: number;
4
+ readonly build: number;
5
+ readonly beta: number;
11
6
  constructor(major: number, minor: number, build: number, beta: number);
12
- /**
13
- * Take a a string of MAJOR.MINOR.BUILD.bBETA and converts it to a
14
- * VexFirmwareVersion instance
15
- * @param version the string to process
16
- * @returns a VexFirmwareVersion representing the provided string
17
- */
18
7
  static fromString(version: string): VexFirmwareVersion;
19
- /**
20
- * Take a a Uint8Array and converts it to a VexFirmwareVersion instance
21
- * @param data the array to process
22
- * @param offset the offset to start at
23
- * @returns a VexFirmwareVersion representing the provided string
24
- */
25
8
  static fromUint8Array(data: Uint8Array, offset?: number, reverse?: boolean): VexFirmwareVersion;
26
9
  static allZero(): VexFirmwareVersion;
27
- /**
28
- * Take a a string of MAJOR_MINOR_BUILD_BETA and converts it to a
29
- * VexFirmwareVersion instance
30
- * @param version the string to process
31
- * @returns a VexFirmwareVersion representing the provided string
32
- */
33
10
  static fromCatalogString(version: string): VexFirmwareVersion;
34
11
  isBeta(): boolean;
35
- /**
36
- * returns version as Uint Array
37
- */
38
12
  toUint8Array(reverse?: boolean): Uint8Array;
39
- /**
40
- * returns version as major.minor.build
41
- */
42
13
  toUserString(): string;
43
- /**
44
- * returns version as ${major}.${minor}.{build}.b${beta}
45
- */
46
14
  toInternalString(): string;
47
- /**
48
- * compares this version to the provided version.
49
- * * if this < b: negative
50
- * * if this = b: 0
51
- * * if this > b: positive
52
- * @param that the version to compare again
53
- */
54
15
  compare(that: VexFirmwareVersion): number;
55
16
  }
@@ -20,7 +20,6 @@ export declare class ProgramIniConfig {
20
20
  config: Record<number, string>;
21
21
  controller1: Record<string, string>;
22
22
  controller2: Record<string, string>;
23
- constructor();
24
23
  setProgramDate(date: Date): void;
25
24
  createIni(): string;
26
25
  dec2(value: number): string;
@@ -1,18 +1,21 @@
1
1
  import { AckType, type DataArray } from "./Vex.js";
2
2
  import type { PacketEncoder } from "./VexPacketEncoder.js";
3
3
  export declare abstract class Packet {
4
+ /** Raw bytes sent to or received from the device. */
4
5
  data: Uint8Array;
5
6
  static ENCODER: PacketEncoder;
6
7
  constructor(rawData: DataArray);
7
8
  }
8
9
  export declare class DeviceBoundPacket extends Packet {
9
- get commandId(): number;
10
- get commandExtendedId(): number | undefined;
11
10
  static COMMAND_ID: number;
12
11
  static COMMAND_EXTENDED_ID: number | undefined;
12
+ get commandId(): number;
13
+ get commandExtendedId(): number | undefined;
13
14
  constructor(payload?: Uint8Array);
14
15
  }
15
16
  export declare class HostBoundPacket extends Packet {
17
+ static COMMAND_ID: number;
18
+ static COMMAND_EXTENDED_ID: number | undefined;
16
19
  ack: AckType;
17
20
  payloadSize: number;
18
21
  ackIndex: number;
@@ -0,0 +1,5 @@
1
+ export * from "./VexCRC.js";
2
+ export * from "./VexFirmwareVersion.js";
3
+ export * from "./VexPacketBase.js";
4
+ export * from "./VexPacketEncoder.js";
5
+ export * from "./VexPacketView.js";
@@ -8,46 +8,29 @@ export declare class PacketEncoder {
8
8
  static J2000_EPOCH: number;
9
9
  vexVersion: number;
10
10
  crcgen: CrcGenerator;
11
- allPacketsTable: Record<string, typeof HostBoundPacket>;
11
+ allPacketsTable: Map<number, Map<number | undefined, typeof HostBoundPacket>>;
12
12
  static getInstance(): PacketEncoder;
13
13
  private constructor();
14
- /**
15
- * Create the vex CDC header
16
- * @param buf the bytes to send
17
- */
14
+ /** Register reply packet classes without retaining an entire module object. */
15
+ registerPacketTypes(types: Iterable<typeof HostBoundPacket>): void;
16
+ /** Look up the reply class registered for a command ID pair. */
17
+ getPacketType(commandId: number | undefined, commandExtendedId: number | undefined): typeof HostBoundPacket | undefined;
18
+ /** Create the vex CDC header. */
18
19
  createHeader(buf: ArrayBuffer | undefined): Uint8Array;
19
- /**
20
- * Create the vex CDC simple message
21
- * @param cmd the CDC command byte
22
- */
20
+ /** Create a simple CDC message. */
23
21
  cdcCommand(cmd: number): Uint8Array;
24
- /**
25
- * Create the vex CDC simple message
26
- * @param cmd the CDC command byte
27
- * @param data the data to send
28
- */
22
+ /** Create a simple CDC message carrying data. */
29
23
  cdcCommandWithData(cmd: number, data: Uint8Array): Uint8Array;
30
- /**
31
- * Create the vex CDC extended message
32
- * @param cmd the CDC command byte
33
- * @param ext the CDC extended command byte
34
- * @return a message
35
- */
24
+ /** Create a CDC2 (extended) message with no payload. */
36
25
  cdc2Command(cmd: number, ext: number): Uint8Array;
37
26
  /**
38
- * Calculate buffer length for new CDC extended command
39
- * @param data the CDC extended command payload
40
- * @returns the required buffer length of the command message
27
+ * Buffer length for a CDC2 command: header + command byte + function
28
+ * byte + length byte (two bytes when payload > 127) + payload + CRC16.
41
29
  */
42
30
  cdc2CommandBufferLength(data: Uint8Array): number;
43
- /**
44
- * Create the vex CDC extended message with some data
45
- * @param cmd the CDC command byte
46
- * @param ext the CDC extended command byte
47
- * @param data the CDC extended command payload
48
- * @return a message
49
- */
31
+ /** Create a CDC2 (extended) message carrying a payload. */
50
32
  cdc2CommandWithData(cmd: number, ext: number, data: Uint8Array): Uint8Array;
33
+ private appendCrc16;
51
34
  validateHeader(data: Uint8Array): boolean;
52
35
  validateMessageCdc(data: Uint8Array): boolean;
53
36
  getPayloadSize(data: Uint8Array): number;