@technoculture/safeserial 0.1.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.
Files changed (33) hide show
  1. package/CMakeLists.txt +66 -0
  2. package/README.md +77 -0
  3. package/deps/include/safeserial/config.hpp +69 -0
  4. package/deps/include/safeserial/protocol/crc32.hpp +19 -0
  5. package/deps/include/safeserial/protocol/packet.hpp +175 -0
  6. package/deps/include/safeserial/protocol/reassembler.hpp +80 -0
  7. package/deps/include/safeserial/resilient_bridge.hpp +106 -0
  8. package/deps/include/safeserial/safeserial.hpp +87 -0
  9. package/deps/include/safeserial/transport/factory.hpp +0 -0
  10. package/deps/include/safeserial/transport/iserial_port.hpp +15 -0
  11. package/deps/include/safeserial/transport/serial_port.hpp +28 -0
  12. package/deps/src/CMakeLists.txt +21 -0
  13. package/deps/src/protocol/packet.cpp +1 -0
  14. package/deps/src/resilient_bridge.cpp +338 -0
  15. package/deps/src/safeserial.cpp +246 -0
  16. package/deps/src/transport/platform/linux/linux_serial.cpp +53 -0
  17. package/deps/src/transport/platform/windows/windows_serial.cpp +51 -0
  18. package/dist/index.d.mts +93 -0
  19. package/dist/index.d.ts +93 -0
  20. package/dist/index.js +202 -0
  21. package/dist/index.mjs +170 -0
  22. package/lib/index.ts +23 -0
  23. package/lib/native.ts +108 -0
  24. package/lib/reliable.ts +94 -0
  25. package/lib/resilient.ts +122 -0
  26. package/package.json +78 -0
  27. package/prebuilds/darwin-arm64/Release/data_bridge_node.node +0 -0
  28. package/prebuilds/darwin-arm64/Release/safeserial_node.node +0 -0
  29. package/scripts/copy_deps.js +44 -0
  30. package/scripts/receiver.js +37 -0
  31. package/scripts/sender.js +48 -0
  32. package/src/addon.cpp +807 -0
  33. package/src/serial_wrapper.cpp +8 -0
@@ -0,0 +1,51 @@
1
+ #include <safeserial/transport/serial_port.hpp>
2
+ #include <windows.h>
3
+
4
+ struct SerialPort::Impl {
5
+ HANDLE hSerial = INVALID_HANDLE_VALUE;
6
+ };
7
+
8
+ SerialPort::SerialPort() : pimpl(std::make_unique<Impl>()) {}
9
+ SerialPort::~SerialPort() { close(); }
10
+
11
+ bool SerialPort::open(const std::string& port, int baud) {
12
+ pimpl->hSerial = CreateFileA(port.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
13
+ if (pimpl->hSerial == INVALID_HANDLE_VALUE) return false;
14
+
15
+ DCB dcb = {0};
16
+ dcb.DCBlength = sizeof(dcb);
17
+ GetCommState(pimpl->hSerial, &dcb);
18
+ dcb.BaudRate = CBR_115200;
19
+ dcb.ByteSize = 8;
20
+ dcb.StopBits = ONESTOPBIT;
21
+ dcb.Parity = NOPARITY;
22
+ SetCommState(pimpl->hSerial, &dcb);
23
+
24
+ // TIMEOUTS - Prevents the app from freezing
25
+ COMMTIMEOUTS timeouts = { 0 };
26
+ timeouts.ReadIntervalTimeout = 50;
27
+ timeouts.ReadTotalTimeoutConstant = 50;
28
+ timeouts.ReadTotalTimeoutMultiplier = 10;
29
+ SetCommTimeouts(pimpl->hSerial, &timeouts);
30
+
31
+ return true;
32
+ }
33
+
34
+ int SerialPort::write(const std::vector<uint8_t>& data) {
35
+ DWORD written;
36
+ WriteFile(pimpl->hSerial, data.data(), (DWORD)data.size(), &written, NULL);
37
+ return (int)written;
38
+ }
39
+
40
+ int SerialPort::read(uint8_t* buffer, size_t size) {
41
+ DWORD read;
42
+ if (!ReadFile(pimpl->hSerial, buffer, (DWORD)size, &read, NULL)) return -1;
43
+ return (int)read;
44
+ }
45
+
46
+ void SerialPort::close() {
47
+ if (pimpl->hSerial != INVALID_HANDLE_VALUE) {
48
+ CloseHandle(pimpl->hSerial);
49
+ pimpl->hSerial = INVALID_HANDLE_VALUE;
50
+ }
51
+ }
@@ -0,0 +1,93 @@
1
+ import { EventEmitter } from 'events';
2
+
3
+ interface DataBridgeOptions {
4
+ baudRate?: number;
5
+ maxRetries?: number;
6
+ ackTimeoutMs?: number;
7
+ fragmentSize?: number;
8
+ }
9
+ /**
10
+ * ReliableDataBridge
11
+ *
12
+ * Thin wrapper around the native C++ DataBridge implementation.
13
+ */
14
+ declare class ReliableDataBridge extends EventEmitter {
15
+ private bridge;
16
+ private isOpen_;
17
+ private options;
18
+ constructor(options?: DataBridgeOptions);
19
+ static open(port: string, baudOrOptions?: number | DataBridgeOptions, cb?: (data: Buffer) => void): Promise<ReliableDataBridge>;
20
+ open(port: string, baud?: number): Promise<boolean>;
21
+ close(): Promise<void>;
22
+ get isOpen(): boolean;
23
+ send(data: string | Buffer): Promise<void>;
24
+ }
25
+
26
+ interface SerialPort {
27
+ open(port: string, baud: number, callback: (data: Buffer) => void): boolean;
28
+ write(data: Buffer): number;
29
+ close(): boolean;
30
+ isOpen(): boolean;
31
+ }
32
+ declare const SerialPort: {
33
+ new (): SerialPort;
34
+ };
35
+
36
+ /**
37
+ * ResilientDataBridge - Connection-resilient wrapper
38
+ *
39
+ * Thin wrapper around the native C++ ResilientDataBridge implementation.
40
+ */
41
+
42
+ interface ResilientOptions extends DataBridgeOptions {
43
+ /** Enable auto-reconnection (default: true) */
44
+ reconnect?: boolean;
45
+ /** Initial reconnect delay in ms (default: 1000) */
46
+ reconnectDelay?: number;
47
+ /** Maximum reconnect delay in ms (default: 30000) */
48
+ maxReconnectDelay?: number;
49
+ /** Maximum queue size before dropping old messages (default: 1000) */
50
+ maxQueueSize?: number;
51
+ }
52
+ interface ResilientEvents {
53
+ data: (data: Buffer) => void;
54
+ error: (error: Error) => void;
55
+ close: () => void;
56
+ disconnect: () => void;
57
+ reconnecting: (attempt: number, delay: number) => void;
58
+ reconnected: () => void;
59
+ }
60
+ declare class ResilientDataBridge extends EventEmitter {
61
+ private bridge;
62
+ private port;
63
+ private options;
64
+ private constructor();
65
+ /**
66
+ * Open a resilient serial connection.
67
+ *
68
+ * @param port - Serial port path
69
+ * @param options - Configuration including reconnection settings
70
+ */
71
+ static open(port: string, options?: ResilientOptions): Promise<ResilientDataBridge>;
72
+ open(): Promise<boolean>;
73
+ /**
74
+ * Send data with guaranteed delivery.
75
+ *
76
+ * @param data - Data to send
77
+ * @returns Promise that resolves when data is acknowledged
78
+ */
79
+ send(data: Buffer | string): Promise<void>;
80
+ /**
81
+ * Close the connection permanently.
82
+ */
83
+ close(): Promise<void>;
84
+ /** Current connection state */
85
+ get connected(): boolean;
86
+ /** Number of messages waiting in queue */
87
+ get queueLength(): number;
88
+ on<K extends keyof ResilientEvents>(event: K, listener: ResilientEvents[K]): this;
89
+ once<K extends keyof ResilientEvents>(event: K, listener: ResilientEvents[K]): this;
90
+ emit<K extends keyof ResilientEvents>(event: K, ...args: Parameters<ResilientEvents[K]>): boolean;
91
+ }
92
+
93
+ export { ReliableDataBridge as DataBridge, type DataBridgeOptions, SerialPort as RawSerialPort, ResilientDataBridge, type ResilientEvents, type ResilientOptions };
@@ -0,0 +1,93 @@
1
+ import { EventEmitter } from 'events';
2
+
3
+ interface DataBridgeOptions {
4
+ baudRate?: number;
5
+ maxRetries?: number;
6
+ ackTimeoutMs?: number;
7
+ fragmentSize?: number;
8
+ }
9
+ /**
10
+ * ReliableDataBridge
11
+ *
12
+ * Thin wrapper around the native C++ DataBridge implementation.
13
+ */
14
+ declare class ReliableDataBridge extends EventEmitter {
15
+ private bridge;
16
+ private isOpen_;
17
+ private options;
18
+ constructor(options?: DataBridgeOptions);
19
+ static open(port: string, baudOrOptions?: number | DataBridgeOptions, cb?: (data: Buffer) => void): Promise<ReliableDataBridge>;
20
+ open(port: string, baud?: number): Promise<boolean>;
21
+ close(): Promise<void>;
22
+ get isOpen(): boolean;
23
+ send(data: string | Buffer): Promise<void>;
24
+ }
25
+
26
+ interface SerialPort {
27
+ open(port: string, baud: number, callback: (data: Buffer) => void): boolean;
28
+ write(data: Buffer): number;
29
+ close(): boolean;
30
+ isOpen(): boolean;
31
+ }
32
+ declare const SerialPort: {
33
+ new (): SerialPort;
34
+ };
35
+
36
+ /**
37
+ * ResilientDataBridge - Connection-resilient wrapper
38
+ *
39
+ * Thin wrapper around the native C++ ResilientDataBridge implementation.
40
+ */
41
+
42
+ interface ResilientOptions extends DataBridgeOptions {
43
+ /** Enable auto-reconnection (default: true) */
44
+ reconnect?: boolean;
45
+ /** Initial reconnect delay in ms (default: 1000) */
46
+ reconnectDelay?: number;
47
+ /** Maximum reconnect delay in ms (default: 30000) */
48
+ maxReconnectDelay?: number;
49
+ /** Maximum queue size before dropping old messages (default: 1000) */
50
+ maxQueueSize?: number;
51
+ }
52
+ interface ResilientEvents {
53
+ data: (data: Buffer) => void;
54
+ error: (error: Error) => void;
55
+ close: () => void;
56
+ disconnect: () => void;
57
+ reconnecting: (attempt: number, delay: number) => void;
58
+ reconnected: () => void;
59
+ }
60
+ declare class ResilientDataBridge extends EventEmitter {
61
+ private bridge;
62
+ private port;
63
+ private options;
64
+ private constructor();
65
+ /**
66
+ * Open a resilient serial connection.
67
+ *
68
+ * @param port - Serial port path
69
+ * @param options - Configuration including reconnection settings
70
+ */
71
+ static open(port: string, options?: ResilientOptions): Promise<ResilientDataBridge>;
72
+ open(): Promise<boolean>;
73
+ /**
74
+ * Send data with guaranteed delivery.
75
+ *
76
+ * @param data - Data to send
77
+ * @returns Promise that resolves when data is acknowledged
78
+ */
79
+ send(data: Buffer | string): Promise<void>;
80
+ /**
81
+ * Close the connection permanently.
82
+ */
83
+ close(): Promise<void>;
84
+ /** Current connection state */
85
+ get connected(): boolean;
86
+ /** Number of messages waiting in queue */
87
+ get queueLength(): number;
88
+ on<K extends keyof ResilientEvents>(event: K, listener: ResilientEvents[K]): this;
89
+ once<K extends keyof ResilientEvents>(event: K, listener: ResilientEvents[K]): this;
90
+ emit<K extends keyof ResilientEvents>(event: K, ...args: Parameters<ResilientEvents[K]>): boolean;
91
+ }
92
+
93
+ export { ReliableDataBridge as DataBridge, type DataBridgeOptions, SerialPort as RawSerialPort, ResilientDataBridge, type ResilientEvents, type ResilientOptions };
package/dist/index.js ADDED
@@ -0,0 +1,202 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // lib/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ DataBridge: () => ReliableDataBridge,
34
+ RawSerialPort: () => SerialPort,
35
+ ResilientDataBridge: () => ResilientDataBridge
36
+ });
37
+ module.exports = __toCommonJS(index_exports);
38
+
39
+ // lib/reliable.ts
40
+ var import_events = require("events");
41
+
42
+ // lib/native.ts
43
+ var import_path = __toESM(require("path"));
44
+ function loadAddon() {
45
+ const possiblePaths = [
46
+ `../prebuilds/${process.platform}-${process.arch}/safeserial_node.node`,
47
+ "../build/Release/safeserial_node.node"
48
+ ];
49
+ for (const p of possiblePaths) {
50
+ try {
51
+ return require(import_path.default.join(__dirname, p));
52
+ } catch {
53
+ continue;
54
+ }
55
+ }
56
+ throw new Error("Failed to load native addon. Run `npm run build` first.");
57
+ }
58
+ var addon = loadAddon();
59
+ var SerialPort = addon.SerialPort;
60
+ var NativeDataBridge = addon.DataBridge;
61
+ var NativeResilientDataBridge = addon.ResilientDataBridge;
62
+ var Packet = addon.Packet;
63
+ var Reassembler = addon.Reassembler;
64
+
65
+ // lib/reliable.ts
66
+ var ReliableDataBridge = class _ReliableDataBridge extends import_events.EventEmitter {
67
+ bridge;
68
+ isOpen_ = false;
69
+ // Config
70
+ options;
71
+ constructor(options = {}) {
72
+ super();
73
+ this.bridge = new NativeDataBridge();
74
+ this.options = {
75
+ baudRate: options.baudRate ?? 115200,
76
+ maxRetries: options.maxRetries ?? 10,
77
+ ackTimeoutMs: options.ackTimeoutMs ?? 500,
78
+ fragmentSize: options.fragmentSize ?? 200
79
+ };
80
+ this.bridge.onData((data) => {
81
+ this.emit("data", data);
82
+ });
83
+ }
84
+ static async open(port, baudOrOptions, cb) {
85
+ let opts = {};
86
+ if (typeof baudOrOptions === "number") {
87
+ opts.baudRate = baudOrOptions;
88
+ } else if (baudOrOptions) {
89
+ opts = baudOrOptions;
90
+ }
91
+ const bridge = new _ReliableDataBridge(opts);
92
+ if (cb) bridge.on("data", cb);
93
+ await bridge.open(port);
94
+ return bridge;
95
+ }
96
+ async open(port, baud) {
97
+ if (this.isOpen_) return true;
98
+ const baudRate = baud ?? this.options.baudRate;
99
+ const success = this.bridge.open(port, baudRate);
100
+ if (!success) {
101
+ throw new Error(`Failed to open ${port}`);
102
+ }
103
+ this.isOpen_ = true;
104
+ return true;
105
+ }
106
+ async close() {
107
+ if (this.isOpen_) {
108
+ this.bridge.close();
109
+ this.isOpen_ = false;
110
+ this.emit("close");
111
+ }
112
+ }
113
+ get isOpen() {
114
+ return this.isOpen_;
115
+ }
116
+ async send(data) {
117
+ if (!this.isOpen_) throw new Error("Port not open");
118
+ const buf = Buffer.isBuffer(data) ? data : Buffer.from(data);
119
+ this.bridge.send(
120
+ buf,
121
+ this.options.ackTimeoutMs,
122
+ this.options.maxRetries,
123
+ this.options.fragmentSize
124
+ );
125
+ }
126
+ };
127
+
128
+ // lib/resilient.ts
129
+ var import_events2 = require("events");
130
+ var ResilientDataBridge = class _ResilientDataBridge extends import_events2.EventEmitter {
131
+ bridge;
132
+ port;
133
+ options;
134
+ constructor(port, options) {
135
+ super();
136
+ this.port = port;
137
+ this.options = options;
138
+ this.bridge = new NativeResilientDataBridge(options);
139
+ this.bridge.onData((data) => this.emit("data", data));
140
+ this.bridge.onError((message) => this.emit("error", new Error(message)));
141
+ this.bridge.onDisconnect(() => this.emit("disconnect"));
142
+ this.bridge.onReconnecting(
143
+ (attempt, delay) => this.emit("reconnecting", attempt, delay)
144
+ );
145
+ this.bridge.onReconnected(() => this.emit("reconnected"));
146
+ this.bridge.onClose(() => this.emit("close"));
147
+ }
148
+ /**
149
+ * Open a resilient serial connection.
150
+ *
151
+ * @param port - Serial port path
152
+ * @param options - Configuration including reconnection settings
153
+ */
154
+ static async open(port, options = {}) {
155
+ const instance = new _ResilientDataBridge(port, options);
156
+ await instance.open();
157
+ return instance;
158
+ }
159
+ async open() {
160
+ return this.bridge.open(this.port);
161
+ }
162
+ /**
163
+ * Send data with guaranteed delivery.
164
+ *
165
+ * @param data - Data to send
166
+ * @returns Promise that resolves when data is acknowledged
167
+ */
168
+ async send(data) {
169
+ const buffer = typeof data === "string" ? Buffer.from(data) : data;
170
+ this.bridge.send(buffer);
171
+ }
172
+ /**
173
+ * Close the connection permanently.
174
+ */
175
+ async close() {
176
+ this.bridge.close();
177
+ }
178
+ /** Current connection state */
179
+ get connected() {
180
+ return this.bridge.isConnected();
181
+ }
182
+ /** Number of messages waiting in queue */
183
+ get queueLength() {
184
+ return this.bridge.queueLength();
185
+ }
186
+ // Type-safe event emitter
187
+ on(event, listener) {
188
+ return super.on(event, listener);
189
+ }
190
+ once(event, listener) {
191
+ return super.once(event, listener);
192
+ }
193
+ emit(event, ...args) {
194
+ return super.emit(event, ...args);
195
+ }
196
+ };
197
+ // Annotate the CommonJS export names for ESM import in node:
198
+ 0 && (module.exports = {
199
+ DataBridge,
200
+ RawSerialPort,
201
+ ResilientDataBridge
202
+ });
package/dist/index.mjs ADDED
@@ -0,0 +1,170 @@
1
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
2
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
3
+ }) : x)(function(x) {
4
+ if (typeof require !== "undefined") return require.apply(this, arguments);
5
+ throw Error('Dynamic require of "' + x + '" is not supported');
6
+ });
7
+
8
+ // lib/reliable.ts
9
+ import { EventEmitter } from "events";
10
+
11
+ // lib/native.ts
12
+ import path from "path";
13
+ function loadAddon() {
14
+ const possiblePaths = [
15
+ `../prebuilds/${process.platform}-${process.arch}/safeserial_node.node`,
16
+ "../build/Release/safeserial_node.node"
17
+ ];
18
+ for (const p of possiblePaths) {
19
+ try {
20
+ return __require(path.join(__dirname, p));
21
+ } catch {
22
+ continue;
23
+ }
24
+ }
25
+ throw new Error("Failed to load native addon. Run `npm run build` first.");
26
+ }
27
+ var addon = loadAddon();
28
+ var SerialPort = addon.SerialPort;
29
+ var NativeDataBridge = addon.DataBridge;
30
+ var NativeResilientDataBridge = addon.ResilientDataBridge;
31
+ var Packet = addon.Packet;
32
+ var Reassembler = addon.Reassembler;
33
+
34
+ // lib/reliable.ts
35
+ var ReliableDataBridge = class _ReliableDataBridge extends EventEmitter {
36
+ bridge;
37
+ isOpen_ = false;
38
+ // Config
39
+ options;
40
+ constructor(options = {}) {
41
+ super();
42
+ this.bridge = new NativeDataBridge();
43
+ this.options = {
44
+ baudRate: options.baudRate ?? 115200,
45
+ maxRetries: options.maxRetries ?? 10,
46
+ ackTimeoutMs: options.ackTimeoutMs ?? 500,
47
+ fragmentSize: options.fragmentSize ?? 200
48
+ };
49
+ this.bridge.onData((data) => {
50
+ this.emit("data", data);
51
+ });
52
+ }
53
+ static async open(port, baudOrOptions, cb) {
54
+ let opts = {};
55
+ if (typeof baudOrOptions === "number") {
56
+ opts.baudRate = baudOrOptions;
57
+ } else if (baudOrOptions) {
58
+ opts = baudOrOptions;
59
+ }
60
+ const bridge = new _ReliableDataBridge(opts);
61
+ if (cb) bridge.on("data", cb);
62
+ await bridge.open(port);
63
+ return bridge;
64
+ }
65
+ async open(port, baud) {
66
+ if (this.isOpen_) return true;
67
+ const baudRate = baud ?? this.options.baudRate;
68
+ const success = this.bridge.open(port, baudRate);
69
+ if (!success) {
70
+ throw new Error(`Failed to open ${port}`);
71
+ }
72
+ this.isOpen_ = true;
73
+ return true;
74
+ }
75
+ async close() {
76
+ if (this.isOpen_) {
77
+ this.bridge.close();
78
+ this.isOpen_ = false;
79
+ this.emit("close");
80
+ }
81
+ }
82
+ get isOpen() {
83
+ return this.isOpen_;
84
+ }
85
+ async send(data) {
86
+ if (!this.isOpen_) throw new Error("Port not open");
87
+ const buf = Buffer.isBuffer(data) ? data : Buffer.from(data);
88
+ this.bridge.send(
89
+ buf,
90
+ this.options.ackTimeoutMs,
91
+ this.options.maxRetries,
92
+ this.options.fragmentSize
93
+ );
94
+ }
95
+ };
96
+
97
+ // lib/resilient.ts
98
+ import { EventEmitter as EventEmitter2 } from "events";
99
+ var ResilientDataBridge = class _ResilientDataBridge extends EventEmitter2 {
100
+ bridge;
101
+ port;
102
+ options;
103
+ constructor(port, options) {
104
+ super();
105
+ this.port = port;
106
+ this.options = options;
107
+ this.bridge = new NativeResilientDataBridge(options);
108
+ this.bridge.onData((data) => this.emit("data", data));
109
+ this.bridge.onError((message) => this.emit("error", new Error(message)));
110
+ this.bridge.onDisconnect(() => this.emit("disconnect"));
111
+ this.bridge.onReconnecting(
112
+ (attempt, delay) => this.emit("reconnecting", attempt, delay)
113
+ );
114
+ this.bridge.onReconnected(() => this.emit("reconnected"));
115
+ this.bridge.onClose(() => this.emit("close"));
116
+ }
117
+ /**
118
+ * Open a resilient serial connection.
119
+ *
120
+ * @param port - Serial port path
121
+ * @param options - Configuration including reconnection settings
122
+ */
123
+ static async open(port, options = {}) {
124
+ const instance = new _ResilientDataBridge(port, options);
125
+ await instance.open();
126
+ return instance;
127
+ }
128
+ async open() {
129
+ return this.bridge.open(this.port);
130
+ }
131
+ /**
132
+ * Send data with guaranteed delivery.
133
+ *
134
+ * @param data - Data to send
135
+ * @returns Promise that resolves when data is acknowledged
136
+ */
137
+ async send(data) {
138
+ const buffer = typeof data === "string" ? Buffer.from(data) : data;
139
+ this.bridge.send(buffer);
140
+ }
141
+ /**
142
+ * Close the connection permanently.
143
+ */
144
+ async close() {
145
+ this.bridge.close();
146
+ }
147
+ /** Current connection state */
148
+ get connected() {
149
+ return this.bridge.isConnected();
150
+ }
151
+ /** Number of messages waiting in queue */
152
+ get queueLength() {
153
+ return this.bridge.queueLength();
154
+ }
155
+ // Type-safe event emitter
156
+ on(event, listener) {
157
+ return super.on(event, listener);
158
+ }
159
+ once(event, listener) {
160
+ return super.once(event, listener);
161
+ }
162
+ emit(event, ...args) {
163
+ return super.emit(event, ...args);
164
+ }
165
+ };
166
+ export {
167
+ ReliableDataBridge as DataBridge,
168
+ SerialPort as RawSerialPort,
169
+ ResilientDataBridge
170
+ };
package/lib/index.ts ADDED
@@ -0,0 +1,23 @@
1
+
2
+ /**
3
+ * SafeSerial - Guaranteed Reliable Serial Communication
4
+ *
5
+ * TypeScript wrapper for the native Node-API addon.
6
+ * Provides a clean, async-friendly API for Electron applications.
7
+ */
8
+
9
+ import { ReliableDataBridge, DataBridgeOptions } from './reliable';
10
+ import { SerialPort } from './native';
11
+
12
+ // Export the Reliable implementation as the default DataBridge
13
+ export { ReliableDataBridge as DataBridge };
14
+ export { DataBridgeOptions };
15
+
16
+ // Export Raw SerialPort for advanced users
17
+ export { SerialPort as RawSerialPort };
18
+
19
+ // Export Resilient wrapper (connection resilience)
20
+ // Note: ResilientDataBridge wraps DataBridge.
21
+ // If DataBridge is now Reliable, ResilientDataBridge will hold a ReliableDataBridge.
22
+ // This gives us Connection Resilience + Data Reliability.
23
+ export { ResilientDataBridge, ResilientOptions, ResilientEvents } from './resilient';