serial-core 0.2.3 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +661 -141
- package/dist/index.cjs +10 -1
- package/dist/index.d.cts +511 -9
- package/dist/index.d.ts +511 -9
- package/dist/index.js +10 -1
- package/llms.txt +191 -0
- package/package.json +23 -23
- package/dist/SerialService.d.cts +0 -99
- package/dist/SerialService.d.ts +0 -99
- package/dist/core/PortRegistry.d.cts +0 -32
- package/dist/core/PortRegistry.d.ts +0 -32
- package/dist/core/PortScanner.d.cts +0 -15
- package/dist/core/PortScanner.d.ts +0 -15
- package/dist/core/QueueManager.d.cts +0 -68
- package/dist/core/QueueManager.d.ts +0 -68
- package/dist/types.d.cts +0 -41
- package/dist/types.d.ts +0 -41
- package/dist/utils/buffers.d.cts +0 -39
- package/dist/utils/buffers.d.ts +0 -39
package/llms.txt
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
# serial-core
|
|
2
|
+
|
|
3
|
+
> Node.js serial transport service with bounded command queuing, discovery, optional verification and automatic recovery. This file documents the repository API for coding assistants and other documentation consumers.
|
|
4
|
+
|
|
5
|
+
## Documentation map
|
|
6
|
+
|
|
7
|
+
- [README.md](README.md): Complete human reference, examples, all methods/options/events, extension contracts, error handling and compatibility.
|
|
8
|
+
- [AGENTS.md](AGENTS.md): Repository contributor/agent guidance; source checkout only, not an npm package file.
|
|
9
|
+
- [src/index.ts](src/index.ts): Authoritative package-root export inventory in a source checkout.
|
|
10
|
+
- [src/SerialService.ts](src/SerialService.ts): Lifecycle, queue integration, event emission, teardown and recovery.
|
|
11
|
+
- [src/types.ts](src/types.ts): JSDoc for every configuration, command, event and discovery field.
|
|
12
|
+
- [src/core/PortScanner.ts](src/core/PortScanner.ts): Exact filtering and enumeration behavior.
|
|
13
|
+
- [src/core/QueueManager.ts](src/core/QueueManager.ts): Internal FIFO and response/timeout race handling.
|
|
14
|
+
- [src/core/PortRegistry.ts](src/core/PortRegistry.ts): Internal module-local exact-path reservations.
|
|
15
|
+
- [src/utils/buffers.ts](src/utils/buffers.ts): Buffer utility implementation and memory ownership.
|
|
16
|
+
- [CONTRIBUTING.md](CONTRIBUTING.md): General contributor workflow.
|
|
17
|
+
|
|
18
|
+
An installed npm package provides README.md, this file and dist declarations/bundles; source checkout links may not exist there. Inspect dist/index.d.ts (ESM) or dist/index.d.cts (CommonJS) for installed signatures and IDE JSDoc. The export map exposes only the package root; documentation files are package files, not importable code subpaths.
|
|
19
|
+
|
|
20
|
+
## Runtime and imports
|
|
21
|
+
|
|
22
|
+
Use Node.js with serialport ^13.0.0 as an installed peer. serialport 13 requires Node >=20; serial-core's current own engine declaration of >=18 does not relax the peer requirement. Node streams and Buffer are used. This is not browser Web Serial.
|
|
23
|
+
|
|
24
|
+
ESM: import { SerialService, SerialStatus, PortScanner } from 'serial-core'.
|
|
25
|
+
CJS: const { SerialService, SerialStatus, PortScanner } = require('serial-core').
|
|
26
|
+
Types: SerialConfig, SendOptions, PortFilter, SerialPortInfo, SerialEvents.
|
|
27
|
+
Functions: toBuffer, toUint8Array, toString, toAscii, toArray.
|
|
28
|
+
Internal QueueManager, PortRegistry and QueueItem are not root exports or supported deep imports.
|
|
29
|
+
|
|
30
|
+
## Complete serial-specific callable API
|
|
31
|
+
|
|
32
|
+
new SerialService(config: SerialConfig)
|
|
33
|
+
SerialService.listPorts(filter?: PortFilter): Promise<SerialPortInfo[]>
|
|
34
|
+
SerialService.findPorts(filter?: PortFilter): Promise<SerialPortInfo[]>
|
|
35
|
+
serial.listPorts(filter?: PortFilter): Promise<SerialPortInfo[]>
|
|
36
|
+
serial.findPorts(filter?: PortFilter): Promise<SerialPortInfo[]>
|
|
37
|
+
serial.connect(): Promise<void>
|
|
38
|
+
serial.disconnect(): Promise<void>
|
|
39
|
+
serial.send(data: string | Buffer, options?: SendOptions): Promise<void>
|
|
40
|
+
serial.queueIsIdle(): boolean
|
|
41
|
+
serial.portIsOpen(): boolean
|
|
42
|
+
serial.getPortPath(): string | null
|
|
43
|
+
serial.getLastAlias(): string | null
|
|
44
|
+
serial.status: SerialStatus (read-only)
|
|
45
|
+
serial.autoConnect: boolean (read-only)
|
|
46
|
+
PortScanner.listPorts(filter?: PortFilter): Promise<SerialPortInfo[]>
|
|
47
|
+
PortScanner.findPorts(filter?: PortFilter): Promise<SerialPortInfo[]>
|
|
48
|
+
PortScanner.findPort(vendorId?: string, productId?: string): Promise<string | null>
|
|
49
|
+
toBuffer(data: string | number[] | Uint8Array | Buffer): Buffer
|
|
50
|
+
toUint8Array(buf: Buffer): Uint8Array
|
|
51
|
+
toString(buf: Buffer, encoding?: BufferEncoding): string
|
|
52
|
+
toAscii(buf: Buffer): string
|
|
53
|
+
toArray(buf: Buffer): number[]
|
|
54
|
+
|
|
55
|
+
Inherited EventEmitter listener/introspection APIs include on/addListener, once, prependListener, prependOnceListener, off/removeListener, removeAllListeners, emit, eventNames, listenerCount, listeners, rawListeners, getMaxListeners and setMaxListeners. Their Node behavior applies. Listener removal does not disconnect. Do not manually emit lifecycle events to perform a serial action or remove internal handshake data listeners with removeAllListeners while verification runs.
|
|
56
|
+
|
|
57
|
+
## Configuration inventory
|
|
58
|
+
|
|
59
|
+
SerialConfig.baudRate: required positive finite number; no default baud rate.
|
|
60
|
+
Supply at least one non-empty path, vendorId, productId or serialNumber.
|
|
61
|
+
path?: fixed OS path; takes precedence over discovery selectors.
|
|
62
|
+
vendorId?, productId?: USB IDs, normalized exact comparison.
|
|
63
|
+
serialNumber?: exact case-sensitive unit identity.
|
|
64
|
+
autoConnect?: false; if true starts in a microtask after construction.
|
|
65
|
+
autoReconnect?: true.
|
|
66
|
+
reconnectInterval?: 2000 ms.
|
|
67
|
+
reconnectBackoffFactor?: 1; finite and >=1.
|
|
68
|
+
maxReconnectInterval?: Math.max(30000, reconnectInterval); >=reconnectInterval.
|
|
69
|
+
maxReconnectAttempts?: Infinity; non-negative integer or Infinity; initial attempt excluded.
|
|
70
|
+
writeTimeout?: 5000 ms; always bounds physical write plus drain, including handshake command.
|
|
71
|
+
responseTimeout?: 5000 ms; starts after writing.
|
|
72
|
+
maxQueueSize?: 1000; positive integer, active plus pending commands.
|
|
73
|
+
parser?: NodeJS.ReadWriteStream, reused across sessions.
|
|
74
|
+
parserFactory?: () => NodeJS.ReadWriteStream, fresh parser each connection recommended.
|
|
75
|
+
parser and parserFactory are mutually exclusive.
|
|
76
|
+
handshake?: nested options below.
|
|
77
|
+
|
|
78
|
+
Omit optional fields to use defaults. Numeric timer values must be positive finite numbers <=2147483647 ms; 0/Infinity are invalid timeouts. Invalid configuration throws synchronously at construction. Driver-level validity remains the driver's responsibility. Config references should not be mutated during use.
|
|
79
|
+
|
|
80
|
+
Handshake fields:
|
|
81
|
+
- command: required string | Buffer, sent outside FIFO after opening.
|
|
82
|
+
- pattern: required string | RegExp; strings are regex sources, not literal comparisons.
|
|
83
|
+
- timeout: required positive deadline in ms, including command transmission.
|
|
84
|
+
- hexPattern?: false; regex uses lowercase hex rather than UTF-8 when true.
|
|
85
|
+
- callback?: ((data: Buffer | string) => boolean) | null; synchronous, priority over regex/hex mode, individual chunks/parsed frames, not accumulated input. pattern remains required by the type even if ignored.
|
|
86
|
+
- maxBufferSize?: 65536 positive integer bytes; regex accumulation only, not callback.
|
|
87
|
+
|
|
88
|
+
## Autodiscovery recipes (omit path)
|
|
89
|
+
|
|
90
|
+
Vendor-only, without productId:
|
|
91
|
+
```typescript
|
|
92
|
+
const serial = new SerialService({ baudRate: 9600, vendorId: '0403', autoConnect: true });
|
|
93
|
+
serial.on('connected', ({ path }) => console.log('Vendor device ready:', path));
|
|
94
|
+
serial.on('error', error => console.error(error.message));
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Product-only, without vendorId:
|
|
98
|
+
```typescript
|
|
99
|
+
const serial = new SerialService({ baudRate: 115200, productId: '6001', autoConnect: true });
|
|
100
|
+
serial.on('connected', ({ path }) => console.log('Product device ready:', path));
|
|
101
|
+
serial.on('error', error => console.error(error.message));
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Both IDs, optional exact unit:
|
|
105
|
+
```typescript
|
|
106
|
+
const serial = new SerialService({
|
|
107
|
+
baudRate: 115200, vendorId: '0403', productId: '6001',
|
|
108
|
+
serialNumber: 'DEVICE-001', autoConnect: true
|
|
109
|
+
});
|
|
110
|
+
serial.on('connected', ({ path }) => console.log('Unit ready:', path));
|
|
111
|
+
serial.on('error', error => console.error(error.message));
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Import SerialService from serial-core for each recipe. Example IDs and baud rates must match the actual device. Discover IDs using await SerialService.listPorts(). VID/PID are hex strings, not numbers. Vendor-only can match multiple products; product-only can match multiple vendors. Both IDs are AND-combined. A configured path bypasses discovery and does not validate its USB identity. With no path, automatic recovery reruns selectors and can follow changed USB paths. autoReconnect defaults true, retry budget defaults Infinity; disconnect stops searching. These are retry-driven connection attempts, not a watchPorts API. See README's dedicated Autodiscovery section for complete examples.
|
|
115
|
+
|
|
116
|
+
## Lifecycle contracts
|
|
117
|
+
|
|
118
|
+
States: DISCONNECTED, SCANNING, CONNECTING, CONNECTED, RECONNECTING.
|
|
119
|
+
SCANNING can include waiting for old transport teardown, including when a fixed path is configured.
|
|
120
|
+
CONNECTING includes handshake. Only CONNECTED permits send().
|
|
121
|
+
connect() starts an attempt; its resolution is not readiness. Register connected/error listeners before calling it. Operational failures use events/retry policy, not a readiness rejection promise.
|
|
122
|
+
Parallel connect calls do not start duplicate attempts. They do not share a readiness promise.
|
|
123
|
+
Explicit connect() while inactive/reconnecting resets the retry budget; calls while scanning/connecting/connected are no-ops.
|
|
124
|
+
A successful verified connection resets the retry count.
|
|
125
|
+
One-based retry delay: min(maxReconnectInterval, reconnectInterval * reconnectBackoffFactor ** (attempt - 1)).
|
|
126
|
+
At most one retry timer per service. maxReconnectAttempts: 0 permits initial attempt only. autoReconnect: false disables retries.
|
|
127
|
+
Fixed paths are reused. Discovery selects the first currently unreserved match on every attempt, allowing changed USB paths. First-match order is driver-defined; use serialNumber to distinguish identical VID/PID devices.
|
|
128
|
+
Errors without a close event also trigger recovery. Old stream callbacks are guarded.
|
|
129
|
+
disconnect() cancels retries, invalidates in-flight discovery, cancels verification and rejects unfinished commands. Pending opens close when their callback arrives.
|
|
130
|
+
DISCONNECTED can be observed before physical closure completes. Await disconnect() for closure.
|
|
131
|
+
OS enumeration/open/close have no service-level timeout. Application wait timeouts do not forcibly cancel the OS operation.
|
|
132
|
+
If closure fails while still open, disconnect() rejects and retains the reservation; call disconnect() again to retry.
|
|
133
|
+
The instance and consumer listeners can be reused after disconnect().
|
|
134
|
+
queueIsIdle() is not connectivity or device idleness. portIsOpen() may be true before handshake readiness. getPortPath() reports only the currently tracked transport, not a configured fallback.
|
|
135
|
+
|
|
136
|
+
## Send and response contracts
|
|
137
|
+
|
|
138
|
+
SendOptions: alias?: string; timeout?: number; waitResponse?: boolean (false); responseMatcher?: (data: Buffer | string) => boolean.
|
|
139
|
+
Commands are FIFO and Buffer input is copied when enqueued. Terminators are not appended automatically.
|
|
140
|
+
send() resolves to void, not the response. Read payloads through data events.
|
|
141
|
+
No queued command is automatically replayed after disconnect. Active/pending commands reject. The device may already have acted; clients determine safe retries.
|
|
142
|
+
Without waitResponse, resolve after write/drain; optional timeout starts when processing begins and bounds queue write wait.
|
|
143
|
+
With waitResponse, timeout overrides responseTimeout and starts after writing. Physical writeTimeout applies independently.
|
|
144
|
+
Waiting in the queue has no independent enqueue deadline. Capacity is bounded; individual command cancellation is not an API.
|
|
145
|
+
An early response is retained until writing completes. A timed-out writer cannot overlap the next physical writer; it may remain busy until its callback/physical timeout.
|
|
146
|
+
No matcher means next chunk/frame counts, possibly unsolicited input. Matcher false ignores a frame; exceptions reject the command. Async matchers are unsupported.
|
|
147
|
+
Raw stream data is arbitrary chunks, not complete messages. Use a parser and protocol-aware matcher for framing.
|
|
148
|
+
A response timeout rejects that command and advances the queue without an inherent reconnect. Late replies require protocol IDs/types to avoid being mistaken for the next response.
|
|
149
|
+
Alias is local active/latest context, not a protocol ID or acknowledgement; it can persist while idle and label unsolicited input. Cleanup clears it. Capture the data event alias rather than reading mutable last context later.
|
|
150
|
+
|
|
151
|
+
## Events
|
|
152
|
+
|
|
153
|
+
SerialEvents maps event names to listener function signatures (not tuple arrays):
|
|
154
|
+
status: (status: SerialStatus) => void; only changed values.
|
|
155
|
+
data: (data: Buffer | string, alias?: string) => void; includes handshake traffic.
|
|
156
|
+
error: (err: Error) => void; operational diagnostics, emitted only with an attached listener. Missing listeners do not stop recovery.
|
|
157
|
+
connected: (info: { path: string; baudRate: number }) => void; each verified ready connection.
|
|
158
|
+
disconnected: (reason: string) => void; current reasons Manual / Unexpected; not every failed scan/open.
|
|
159
|
+
reconnecting: (info: { attempt: number; delay: number }) => void; scheduled retry, 1-based attempt, ms.
|
|
160
|
+
reconnectFailed: (info: { attempts: number }) => void; exhausted retry budget, initial attempt excluded; not for disabled autoReconnect.
|
|
161
|
+
|
|
162
|
+
Listeners run synchronously. Do not throw; handle async listener rejections inside the listener. captureRejections is not enabled. Returned send/disconnect/scanner promises must be handled independently of error listeners. Not every command rejection emits error. Error text is diagnostic, not a stable coded error API.
|
|
163
|
+
Node events.once(serial, 'connected', { signal }) rejects on error/abort; using it for readiness is fail-fast on the first connection error even if recovery continues. Aborting such a wait does not disconnect.
|
|
164
|
+
|
|
165
|
+
## Discovery contracts
|
|
166
|
+
|
|
167
|
+
PortFilter: path?, vendorId?, productId?, serialNumber?, manufacturer? (strings), availableOnly? (false).
|
|
168
|
+
All provided filters combine with AND. VID/PID normalization: trim, lowercase, remove leading 0x and leading zeros, then exact compare. Other strings are case-sensitive exact matches.
|
|
169
|
+
Metadata is passed through from serialport; optional manufacturer/serialNumber/pnpId/locationId/VID/PID may be absent. Missing fields cannot match a supplied selector.
|
|
170
|
+
Empty filters list all devices; result order is not sorted.
|
|
171
|
+
Enumeration never opens/reserves ports. Empty [] or null means no match. Enumeration/driver errors reject and are not swallowed.
|
|
172
|
+
findPort requires at least one non-empty VID/PID, returns first path or null, and does not filter reservations.
|
|
173
|
+
availableOnly checks this loaded module's exact-path registry only, not other apps/processes/workers/module copies or path aliases. Availability can change before opening.
|
|
174
|
+
Instance discovery methods do not implicitly apply the service config.
|
|
175
|
+
|
|
176
|
+
## Parsers, handshake and memory ownership
|
|
177
|
+
|
|
178
|
+
Use parserFactory for a fresh parser with Buffer/string output each session. The service unpipes without ending parsers and removes its own listeners; does not destroy parsers/manage their external resources. Shared parser buffer/reset state belongs to the client. Parser errors trigger recovery.
|
|
179
|
+
Regex handshake matching accumulates bounded bytes and decodes UTF-8/hex; split input can match and regex lastIndex resets per test. Callback mode gets individual chunks/frames. Callback exceptions, overflow, timeout or write failure trigger recovery.
|
|
180
|
+
Readiness requires both a matched response and successful handshake transmission. Temporary verification listeners/timers are canceled on success/failure/disconnect.
|
|
181
|
+
toBuffer(Buffer) returns the same object; toBuffer(Uint8Array) shares backing storage. Strings use UTF-8 and arrays allocate via Buffer.from.
|
|
182
|
+
toUint8Array(Buffer) is the same object, not a copy. toArray allocates an independent array. toString defaults to UTF-8; toAscii uses Node's ASCII high-bit masking semantics.
|
|
183
|
+
Use Buffer.from(existingBuffer) when an independent copy is required. These helpers perform no protocol framing/checksum/endianness handling.
|
|
184
|
+
|
|
185
|
+
## Extension and validation notes
|
|
186
|
+
|
|
187
|
+
Protected hooks: setStatus(status), openPort(path), performWrite(data), handleConnectionFailure(reason), scheduleReconnect(), cleanup(error?), performHandshake(). These are implementation hooks, not stable transport adapters or public client actions. See README and source JSDoc for their ownership and lifecycle requirements.
|
|
188
|
+
Internal QueueManager exposes constructor(writer,maxSize,responseTimeout), add, currentAlias, clear, notifyResponse and isIdle. Its writer supplies physical deadlines; clear() does not cancel that writer. Internal PortRegistry exposes getInstance, register, unregister and isLocked for exact-path reservations.
|
|
189
|
+
No public watchPorts, reconnect, destroy, dispose, sendAndReceive, browser chooser, individual command cancellation, live baud-rate update or control-signal API exists. Do not invent these methods.
|
|
190
|
+
Source validation: pnpm typecheck; pnpm exec vitest run; pnpm lint; pnpm build.
|
|
191
|
+
Build names must continue matching package exports and declarations. Keep JSDoc in generated declarations. Automated mocks do not establish real hardware/driver reliability.
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "serial-core",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.3.0",
|
|
5
5
|
"description": "Resilient serial communication service with automatic reconnection and queues.",
|
|
6
6
|
"author": "Danidoble <danidoble@gmail.com>",
|
|
7
7
|
"license": "GPL-3.0-only",
|
|
@@ -24,18 +24,9 @@
|
|
|
24
24
|
}
|
|
25
25
|
},
|
|
26
26
|
"files": [
|
|
27
|
-
"dist"
|
|
27
|
+
"dist",
|
|
28
|
+
"llms.txt"
|
|
28
29
|
],
|
|
29
|
-
"scripts": {
|
|
30
|
-
"dev": "tsdown --watch",
|
|
31
|
-
"build": "prettier --write ./src/ && tsdown",
|
|
32
|
-
"typecheck": "tsc --noEmit",
|
|
33
|
-
"test": "vitest",
|
|
34
|
-
"prepublishOnly": "npm run build",
|
|
35
|
-
"clean": "rm -rf ./dist/",
|
|
36
|
-
"format": "prettier --write ./src/",
|
|
37
|
-
"lint": "eslint ."
|
|
38
|
-
},
|
|
39
30
|
"keywords": [
|
|
40
31
|
"serialport",
|
|
41
32
|
"iot",
|
|
@@ -50,23 +41,32 @@
|
|
|
50
41
|
"event-driven"
|
|
51
42
|
],
|
|
52
43
|
"devDependencies": {
|
|
53
|
-
"@eslint/js": "^
|
|
54
|
-
"@types/node": "^20.19.
|
|
55
|
-
"bumpp": "^10.4.
|
|
56
|
-
"eslint": "^
|
|
57
|
-
"globals": "^17.
|
|
58
|
-
"jiti": "^2.
|
|
59
|
-
"prettier": "^3.
|
|
44
|
+
"@eslint/js": "^10.0.1",
|
|
45
|
+
"@types/node": "^20.19.43",
|
|
46
|
+
"bumpp": "^10.4.1",
|
|
47
|
+
"eslint": "^10.13.0",
|
|
48
|
+
"globals": "^17.13.0",
|
|
49
|
+
"jiti": "^2.7.0",
|
|
50
|
+
"prettier": "^3.9.10",
|
|
60
51
|
"serialport": "^13.0.0",
|
|
61
|
-
"tsdown": "^0.2
|
|
52
|
+
"tsdown": "^0.23.2",
|
|
62
53
|
"typescript": "^5.9.3",
|
|
63
|
-
"typescript-eslint": "^8.
|
|
64
|
-
"vitest": "^4.
|
|
54
|
+
"typescript-eslint": "^8.72.0",
|
|
55
|
+
"vitest": "^4.1.12"
|
|
65
56
|
},
|
|
66
57
|
"peerDependencies": {
|
|
67
58
|
"serialport": "^13.0.0"
|
|
68
59
|
},
|
|
69
60
|
"engines": {
|
|
70
61
|
"node": ">=18.0.0"
|
|
62
|
+
},
|
|
63
|
+
"scripts": {
|
|
64
|
+
"dev": "tsdown --watch",
|
|
65
|
+
"build": "prettier --write ./src/ && tsdown",
|
|
66
|
+
"typecheck": "tsc --noEmit",
|
|
67
|
+
"test": "vitest",
|
|
68
|
+
"clean": "rm -rf ./dist/",
|
|
69
|
+
"format": "prettier --write ./src/",
|
|
70
|
+
"lint": "eslint ."
|
|
71
71
|
}
|
|
72
|
-
}
|
|
72
|
+
}
|
package/dist/SerialService.d.cts
DELETED
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
import { SerialPort } from 'serialport';
|
|
2
|
-
import { EventEmitter } from 'events';
|
|
3
|
-
import type { SerialStatus as SerialStatusType, SerialConfig } from './types.ts';
|
|
4
|
-
import { SerialStatus } from './types.ts';
|
|
5
|
-
import { QueueManager } from './core/QueueManager.ts';
|
|
6
|
-
/**
|
|
7
|
-
* Main service. Implements a robust state machine.
|
|
8
|
-
* Extends EventEmitter to notify the rest of the app about changes.
|
|
9
|
-
*/
|
|
10
|
-
export declare class SerialService extends EventEmitter {
|
|
11
|
-
protected port: SerialPort | null;
|
|
12
|
-
protected queue: QueueManager;
|
|
13
|
-
protected _status: SerialStatusType;
|
|
14
|
-
protected config: SerialConfig;
|
|
15
|
-
protected intentionalDisconnect: boolean;
|
|
16
|
-
protected reconnectTimer: NodeJS.Timeout | null;
|
|
17
|
-
/**
|
|
18
|
-
* Initializes the SerialService with the provided configuration.
|
|
19
|
-
* @param config - Configuration object for serial connection
|
|
20
|
-
*/
|
|
21
|
-
constructor(config: SerialConfig);
|
|
22
|
-
/**
|
|
23
|
-
* Gets the autoConnect configuration value.
|
|
24
|
-
* @returns Whether the service should automatically connect on instantiation
|
|
25
|
-
*/
|
|
26
|
-
get autoConnect(): boolean;
|
|
27
|
-
/**
|
|
28
|
-
* Gets the current connection status.
|
|
29
|
-
* @returns The current serial connection status
|
|
30
|
-
*/
|
|
31
|
-
get status(): SerialStatus;
|
|
32
|
-
/**
|
|
33
|
-
* Updates the service status and emits the corresponding event.
|
|
34
|
-
* @param newStatus - The new status to set
|
|
35
|
-
*/
|
|
36
|
-
protected setStatus(newStatus: SerialStatus): void;
|
|
37
|
-
/**
|
|
38
|
-
* Starts the connection process.
|
|
39
|
-
* Handles both direct connection (known path) and scanning (VID/PID).
|
|
40
|
-
* @throws Will emit error events if connection fails
|
|
41
|
-
*/
|
|
42
|
-
connect(): Promise<void>;
|
|
43
|
-
/**
|
|
44
|
-
* Physically opens the serial port and configures all necessary listeners.
|
|
45
|
-
* @param path - The port path to open (e.g., '/dev/ttyUSB0' or 'COM3')
|
|
46
|
-
*/
|
|
47
|
-
protected openPort(path: string): void;
|
|
48
|
-
/**
|
|
49
|
-
* Manually disconnects from the serial port.
|
|
50
|
-
* @returns A promise that resolves when the port is fully closed
|
|
51
|
-
*/
|
|
52
|
-
disconnect(): Promise<void>;
|
|
53
|
-
/**
|
|
54
|
-
* Sends data through the serial port using the internal queue.
|
|
55
|
-
* @param data - The data to send (string or Buffer)
|
|
56
|
-
* @param options - Optional send configuration
|
|
57
|
-
* @param options.alias - A command identifier to correlate with responses
|
|
58
|
-
* @param options.timeout - Maximum time to wait for the operation in milliseconds
|
|
59
|
-
* @param options.waitResponse - If true, wait for a response before processing the next item in the queue
|
|
60
|
-
* @returns A promise that resolves when the data has been written to the port
|
|
61
|
-
* @throws If the port is not connected
|
|
62
|
-
*/
|
|
63
|
-
send(data: string | Buffer, options?: {
|
|
64
|
-
alias?: string;
|
|
65
|
-
timeout?: number;
|
|
66
|
-
waitResponse?: boolean;
|
|
67
|
-
}): Promise<void>;
|
|
68
|
-
/**
|
|
69
|
-
* Low-level write logic executed by the QueueManager.
|
|
70
|
-
* Handles backpressure through the 'drain' event.
|
|
71
|
-
* @param data - The data to write to the port
|
|
72
|
-
* @returns A promise that resolves when the data has been fully written
|
|
73
|
-
*/
|
|
74
|
-
protected performWrite(data: string | Buffer): Promise<void>;
|
|
75
|
-
/**
|
|
76
|
-
* Handles connection failures in a centralized manner.
|
|
77
|
-
* Emits error events and schedules reconnection attempts if not intentional.
|
|
78
|
-
* @param reason - The reason for the connection failure
|
|
79
|
-
*/
|
|
80
|
-
protected handleConnectionFailure(reason: string): void;
|
|
81
|
-
/**
|
|
82
|
-
* Schedules the next connection attempt with a simple backoff strategy.
|
|
83
|
-
*/
|
|
84
|
-
protected scheduleReconnect(): void;
|
|
85
|
-
/**
|
|
86
|
-
* Cleans up resources and listeners.
|
|
87
|
-
* Unregisters the port and removes all event listeners.
|
|
88
|
-
*/
|
|
89
|
-
protected cleanup(): void;
|
|
90
|
-
/**
|
|
91
|
-
* Executes the connection handshake to verify device responsiveness.
|
|
92
|
-
* Sends a handshake command and waits for a pattern match in the response.
|
|
93
|
-
*/
|
|
94
|
-
protected performHandshake(): void;
|
|
95
|
-
queueIsIdle(): boolean;
|
|
96
|
-
portIsOpen(): boolean;
|
|
97
|
-
getPortPath(): string | null;
|
|
98
|
-
getLastAlias(): string | null;
|
|
99
|
-
}
|
package/dist/SerialService.d.ts
DELETED
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
import { SerialPort } from 'serialport';
|
|
2
|
-
import { EventEmitter } from 'events';
|
|
3
|
-
import type { SerialStatus as SerialStatusType, SerialConfig } from './types.ts';
|
|
4
|
-
import { SerialStatus } from './types.ts';
|
|
5
|
-
import { QueueManager } from './core/QueueManager.ts';
|
|
6
|
-
/**
|
|
7
|
-
* Main service. Implements a robust state machine.
|
|
8
|
-
* Extends EventEmitter to notify the rest of the app about changes.
|
|
9
|
-
*/
|
|
10
|
-
export declare class SerialService extends EventEmitter {
|
|
11
|
-
protected port: SerialPort | null;
|
|
12
|
-
protected queue: QueueManager;
|
|
13
|
-
protected _status: SerialStatusType;
|
|
14
|
-
protected config: SerialConfig;
|
|
15
|
-
protected intentionalDisconnect: boolean;
|
|
16
|
-
protected reconnectTimer: NodeJS.Timeout | null;
|
|
17
|
-
/**
|
|
18
|
-
* Initializes the SerialService with the provided configuration.
|
|
19
|
-
* @param config - Configuration object for serial connection
|
|
20
|
-
*/
|
|
21
|
-
constructor(config: SerialConfig);
|
|
22
|
-
/**
|
|
23
|
-
* Gets the autoConnect configuration value.
|
|
24
|
-
* @returns Whether the service should automatically connect on instantiation
|
|
25
|
-
*/
|
|
26
|
-
get autoConnect(): boolean;
|
|
27
|
-
/**
|
|
28
|
-
* Gets the current connection status.
|
|
29
|
-
* @returns The current serial connection status
|
|
30
|
-
*/
|
|
31
|
-
get status(): SerialStatus;
|
|
32
|
-
/**
|
|
33
|
-
* Updates the service status and emits the corresponding event.
|
|
34
|
-
* @param newStatus - The new status to set
|
|
35
|
-
*/
|
|
36
|
-
protected setStatus(newStatus: SerialStatus): void;
|
|
37
|
-
/**
|
|
38
|
-
* Starts the connection process.
|
|
39
|
-
* Handles both direct connection (known path) and scanning (VID/PID).
|
|
40
|
-
* @throws Will emit error events if connection fails
|
|
41
|
-
*/
|
|
42
|
-
connect(): Promise<void>;
|
|
43
|
-
/**
|
|
44
|
-
* Physically opens the serial port and configures all necessary listeners.
|
|
45
|
-
* @param path - The port path to open (e.g., '/dev/ttyUSB0' or 'COM3')
|
|
46
|
-
*/
|
|
47
|
-
protected openPort(path: string): void;
|
|
48
|
-
/**
|
|
49
|
-
* Manually disconnects from the serial port.
|
|
50
|
-
* @returns A promise that resolves when the port is fully closed
|
|
51
|
-
*/
|
|
52
|
-
disconnect(): Promise<void>;
|
|
53
|
-
/**
|
|
54
|
-
* Sends data through the serial port using the internal queue.
|
|
55
|
-
* @param data - The data to send (string or Buffer)
|
|
56
|
-
* @param options - Optional send configuration
|
|
57
|
-
* @param options.alias - A command identifier to correlate with responses
|
|
58
|
-
* @param options.timeout - Maximum time to wait for the operation in milliseconds
|
|
59
|
-
* @param options.waitResponse - If true, wait for a response before processing the next item in the queue
|
|
60
|
-
* @returns A promise that resolves when the data has been written to the port
|
|
61
|
-
* @throws If the port is not connected
|
|
62
|
-
*/
|
|
63
|
-
send(data: string | Buffer, options?: {
|
|
64
|
-
alias?: string;
|
|
65
|
-
timeout?: number;
|
|
66
|
-
waitResponse?: boolean;
|
|
67
|
-
}): Promise<void>;
|
|
68
|
-
/**
|
|
69
|
-
* Low-level write logic executed by the QueueManager.
|
|
70
|
-
* Handles backpressure through the 'drain' event.
|
|
71
|
-
* @param data - The data to write to the port
|
|
72
|
-
* @returns A promise that resolves when the data has been fully written
|
|
73
|
-
*/
|
|
74
|
-
protected performWrite(data: string | Buffer): Promise<void>;
|
|
75
|
-
/**
|
|
76
|
-
* Handles connection failures in a centralized manner.
|
|
77
|
-
* Emits error events and schedules reconnection attempts if not intentional.
|
|
78
|
-
* @param reason - The reason for the connection failure
|
|
79
|
-
*/
|
|
80
|
-
protected handleConnectionFailure(reason: string): void;
|
|
81
|
-
/**
|
|
82
|
-
* Schedules the next connection attempt with a simple backoff strategy.
|
|
83
|
-
*/
|
|
84
|
-
protected scheduleReconnect(): void;
|
|
85
|
-
/**
|
|
86
|
-
* Cleans up resources and listeners.
|
|
87
|
-
* Unregisters the port and removes all event listeners.
|
|
88
|
-
*/
|
|
89
|
-
protected cleanup(): void;
|
|
90
|
-
/**
|
|
91
|
-
* Executes the connection handshake to verify device responsiveness.
|
|
92
|
-
* Sends a handshake command and waits for a pattern match in the response.
|
|
93
|
-
*/
|
|
94
|
-
protected performHandshake(): void;
|
|
95
|
-
queueIsIdle(): boolean;
|
|
96
|
-
portIsOpen(): boolean;
|
|
97
|
-
getPortPath(): string | null;
|
|
98
|
-
getLastAlias(): string | null;
|
|
99
|
-
}
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Singleton that keeps track of which serial ports are in use
|
|
3
|
-
* by this application. Prevents conflicts if multiple SerialServices
|
|
4
|
-
* are instantiated for the same port.
|
|
5
|
-
*/
|
|
6
|
-
export declare class PortRegistry {
|
|
7
|
-
private static instance;
|
|
8
|
-
private lockedPorts;
|
|
9
|
-
private constructor();
|
|
10
|
-
/**
|
|
11
|
-
* Gets the singleton instance of the PortRegistry.
|
|
12
|
-
* @returns The PortRegistry singleton instance
|
|
13
|
-
*/
|
|
14
|
-
static getInstance(): PortRegistry;
|
|
15
|
-
/**
|
|
16
|
-
* Attempts to register (lock) exclusive use of a port.
|
|
17
|
-
* @param path - The port path to register
|
|
18
|
-
* @returns True if successfully registered, false if already in use
|
|
19
|
-
*/
|
|
20
|
-
register(path: string): boolean;
|
|
21
|
-
/**
|
|
22
|
-
* Releases the port so it can be used again.
|
|
23
|
-
* @param path - The port path to unregister
|
|
24
|
-
*/
|
|
25
|
-
unregister(path: string): void;
|
|
26
|
-
/**
|
|
27
|
-
* Checks if a port is currently locked by the library.
|
|
28
|
-
* @param path - The port path to check
|
|
29
|
-
* @returns True if the port is in use, false otherwise
|
|
30
|
-
*/
|
|
31
|
-
isLocked(path: string): boolean;
|
|
32
|
-
}
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Singleton that keeps track of which serial ports are in use
|
|
3
|
-
* by this application. Prevents conflicts if multiple SerialServices
|
|
4
|
-
* are instantiated for the same port.
|
|
5
|
-
*/
|
|
6
|
-
export declare class PortRegistry {
|
|
7
|
-
private static instance;
|
|
8
|
-
private lockedPorts;
|
|
9
|
-
private constructor();
|
|
10
|
-
/**
|
|
11
|
-
* Gets the singleton instance of the PortRegistry.
|
|
12
|
-
* @returns The PortRegistry singleton instance
|
|
13
|
-
*/
|
|
14
|
-
static getInstance(): PortRegistry;
|
|
15
|
-
/**
|
|
16
|
-
* Attempts to register (lock) exclusive use of a port.
|
|
17
|
-
* @param path - The port path to register
|
|
18
|
-
* @returns True if successfully registered, false if already in use
|
|
19
|
-
*/
|
|
20
|
-
register(path: string): boolean;
|
|
21
|
-
/**
|
|
22
|
-
* Releases the port so it can be used again.
|
|
23
|
-
* @param path - The port path to unregister
|
|
24
|
-
*/
|
|
25
|
-
unregister(path: string): void;
|
|
26
|
-
/**
|
|
27
|
-
* Checks if a port is currently locked by the library.
|
|
28
|
-
* @param path - The port path to check
|
|
29
|
-
* @returns True if the port is in use, false otherwise
|
|
30
|
-
*/
|
|
31
|
-
isLocked(path: string): boolean;
|
|
32
|
-
}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Utility static class to find devices.
|
|
3
|
-
* Solves the problem of "on which COM/tty is my Arduino?".
|
|
4
|
-
*/
|
|
5
|
-
export declare class PortScanner {
|
|
6
|
-
/**
|
|
7
|
-
* Searches for a serial port matching the provided Vendor ID and/or Product ID.
|
|
8
|
-
* Normalizes strings to handle case sensitivity and '0x' prefixes.
|
|
9
|
-
* @param vendorId - The USB vendor ID to search for (optional if productId provided)
|
|
10
|
-
* @param productId - The USB product ID to search for (optional)
|
|
11
|
-
* @returns The port path if found, null otherwise
|
|
12
|
-
* @throws Error if neither vendorId nor productId is provided
|
|
13
|
-
*/
|
|
14
|
-
static findPort(vendorId?: string, productId?: string): Promise<string | null>;
|
|
15
|
-
}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Utility static class to find devices.
|
|
3
|
-
* Solves the problem of "on which COM/tty is my Arduino?".
|
|
4
|
-
*/
|
|
5
|
-
export declare class PortScanner {
|
|
6
|
-
/**
|
|
7
|
-
* Searches for a serial port matching the provided Vendor ID and/or Product ID.
|
|
8
|
-
* Normalizes strings to handle case sensitivity and '0x' prefixes.
|
|
9
|
-
* @param vendorId - The USB vendor ID to search for (optional if productId provided)
|
|
10
|
-
* @param productId - The USB product ID to search for (optional)
|
|
11
|
-
* @returns The port path if found, null otherwise
|
|
12
|
-
* @throws Error if neither vendorId nor productId is provided
|
|
13
|
-
*/
|
|
14
|
-
static findPort(vendorId?: string, productId?: string): Promise<string | null>;
|
|
15
|
-
}
|
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* QueueManager acts as an intermediate buffer.
|
|
3
|
-
* It prevents serial port saturation and guarantees messages
|
|
4
|
-
* leave in the order they arrived (FIFO).
|
|
5
|
-
*/
|
|
6
|
-
export declare class QueueManager {
|
|
7
|
-
private queue;
|
|
8
|
-
private isProcessing;
|
|
9
|
-
private _currentAlias;
|
|
10
|
-
private waitingForResponse;
|
|
11
|
-
private currentTimeoutTimer;
|
|
12
|
-
private currentItem;
|
|
13
|
-
private writeHandler;
|
|
14
|
-
/**
|
|
15
|
-
* Initializes the QueueManager with a write handler function.
|
|
16
|
-
* @param writeHandler - Function to execute when writing data to the port
|
|
17
|
-
*/
|
|
18
|
-
constructor(writeHandler: (data: Buffer | string) => Promise<void>);
|
|
19
|
-
/**
|
|
20
|
-
* Adds a command to the queue and returns a promise that resolves when processed.
|
|
21
|
-
* @param data - The data to queue (string or Buffer)
|
|
22
|
-
* @param options - Optional queue item configuration
|
|
23
|
-
* @param options.alias - Command identifier for response correlation
|
|
24
|
-
* @param options.timeout - Maximum time to wait for operation/response in milliseconds
|
|
25
|
-
* @param options.waitResponse - If true, block queue until response is received
|
|
26
|
-
* @returns Promise that resolves when the command has been processed
|
|
27
|
-
*/
|
|
28
|
-
add(data: Buffer | string, options?: {
|
|
29
|
-
alias?: string;
|
|
30
|
-
timeout?: number;
|
|
31
|
-
waitResponse?: boolean;
|
|
32
|
-
}): Promise<void>;
|
|
33
|
-
/**
|
|
34
|
-
* Gets the alias of the command currently processing or just processed.
|
|
35
|
-
* Useful for correlating received responses with sent commands.
|
|
36
|
-
* @returns The alias of the current command, or undefined if none
|
|
37
|
-
*/
|
|
38
|
-
get currentAlias(): string | undefined;
|
|
39
|
-
/**
|
|
40
|
-
* Clears all pending items from the queue.
|
|
41
|
-
* Useful when handling abrupt disconnections.
|
|
42
|
-
* All pending items will be rejected with a disconnection error.
|
|
43
|
-
*/
|
|
44
|
-
clear(): void;
|
|
45
|
-
/**
|
|
46
|
-
* Notifies the queue that a response has been received.
|
|
47
|
-
* If waiting for a response, this triggers resolution of the current command
|
|
48
|
-
* and processes the next item in the queue.
|
|
49
|
-
*/
|
|
50
|
-
notifyResponse(): void;
|
|
51
|
-
/**
|
|
52
|
-
* Processes the queue recursively, executing items one by one.
|
|
53
|
-
* Handles timeouts and response waiting when configured.
|
|
54
|
-
* @private
|
|
55
|
-
*/
|
|
56
|
-
private process;
|
|
57
|
-
/**
|
|
58
|
-
* Handles timeout when waiting for a response to a sent command.
|
|
59
|
-
* @private
|
|
60
|
-
* @param item - The queue item that timed out
|
|
61
|
-
*/
|
|
62
|
-
private handleResponseTimeout;
|
|
63
|
-
/**
|
|
64
|
-
* Checks if the queue is idle (no pending items, not processing, not waiting for response).
|
|
65
|
-
* @returns True if the queue has no pending operations
|
|
66
|
-
*/
|
|
67
|
-
isIdle(): boolean;
|
|
68
|
-
}
|