react-native-web-serial-api 0.0.3 → 0.2.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 (177) hide show
  1. package/README.md +198 -104
  2. package/TESTING.md +542 -0
  3. package/android/build.gradle +16 -2
  4. package/android/src/main/java/dev/webserialapi/NativeUsbSerialModule.java +74 -11
  5. package/android/src/main/java/dev/webserialapi/PortPickerActivity.java +61 -59
  6. package/bin/expose-serial.js +205 -0
  7. package/lib/commonjs/UsbSerial.js +58 -26
  8. package/lib/commonjs/UsbSerial.js.map +1 -1
  9. package/lib/commonjs/WebSerial.js +273 -77
  10. package/lib/commonjs/WebSerial.js.map +1 -1
  11. package/lib/commonjs/index.js +15 -3
  12. package/lib/commonjs/index.js.map +1 -1
  13. package/lib/commonjs/lib/dom-exception.js +176 -0
  14. package/lib/commonjs/lib/dom-exception.js.map +1 -0
  15. package/lib/commonjs/lib/event-target.js +140 -0
  16. package/lib/commonjs/lib/event-target.js.map +1 -0
  17. package/lib/commonjs/lib/promise.js +23 -0
  18. package/lib/commonjs/lib/promise.js.map +1 -0
  19. package/lib/commonjs/lib/web-streams.js +42 -0
  20. package/lib/commonjs/lib/web-streams.js.map +1 -0
  21. package/lib/commonjs/testing/device-fixture.js +70 -0
  22. package/lib/commonjs/testing/device-fixture.js.map +1 -0
  23. package/lib/commonjs/testing/expose.js +91 -0
  24. package/lib/commonjs/testing/expose.js.map +1 -0
  25. package/lib/commonjs/testing/harness.js +98 -0
  26. package/lib/commonjs/testing/harness.js.map +1 -0
  27. package/lib/commonjs/testing/in-memory-serial-transport.js +653 -0
  28. package/lib/commonjs/testing/in-memory-serial-transport.js.map +1 -0
  29. package/lib/commonjs/testing/index.js +153 -0
  30. package/lib/commonjs/testing/index.js.map +1 -0
  31. package/lib/commonjs/testing/install-in-memory-serial-transport.js +54 -0
  32. package/lib/commonjs/testing/install-in-memory-serial-transport.js.map +1 -0
  33. package/lib/commonjs/testing/serial-client.js +277 -0
  34. package/lib/commonjs/testing/serial-client.js.map +1 -0
  35. package/lib/commonjs/testing/simulated-device.js +164 -0
  36. package/lib/commonjs/testing/simulated-device.js.map +1 -0
  37. package/lib/commonjs/testing/test-suite.js +142 -0
  38. package/lib/commonjs/testing/test-suite.js.map +1 -0
  39. package/lib/commonjs/transport.js +61 -0
  40. package/lib/commonjs/transport.js.map +1 -0
  41. package/lib/commonjs/websocket/WebSocketSerialTransport.js +659 -0
  42. package/lib/commonjs/websocket/WebSocketSerialTransport.js.map +1 -0
  43. package/lib/commonjs/websocket/bridge.js +234 -0
  44. package/lib/commonjs/websocket/bridge.js.map +1 -0
  45. package/lib/commonjs/websocket/index.js +33 -0
  46. package/lib/commonjs/websocket/index.js.map +1 -0
  47. package/lib/commonjs/websocket/protocol.js +55 -0
  48. package/lib/commonjs/websocket/protocol.js.map +1 -0
  49. package/lib/commonjs/websocket/serial-device-bridge.js +130 -0
  50. package/lib/commonjs/websocket/serial-device-bridge.js.map +1 -0
  51. package/lib/typescript/src/UsbSerial.d.ts +24 -67
  52. package/lib/typescript/src/UsbSerial.d.ts.map +1 -1
  53. package/lib/typescript/src/WebSerial.d.ts +16 -7
  54. package/lib/typescript/src/WebSerial.d.ts.map +1 -1
  55. package/lib/typescript/src/index.d.ts +3 -1
  56. package/lib/typescript/src/index.d.ts.map +1 -1
  57. package/lib/typescript/src/lib/dom-exception.d.ts +100 -0
  58. package/lib/typescript/src/lib/dom-exception.d.ts.map +1 -0
  59. package/lib/typescript/src/lib/event-target.d.ts +55 -0
  60. package/lib/typescript/src/lib/event-target.d.ts.map +1 -0
  61. package/lib/typescript/src/lib/promise.d.ts +11 -0
  62. package/lib/typescript/src/lib/promise.d.ts.map +1 -0
  63. package/lib/typescript/src/lib/web-streams.d.ts +9 -0
  64. package/lib/typescript/src/lib/web-streams.d.ts.map +1 -0
  65. package/lib/typescript/src/testing/device-fixture.d.ts +70 -0
  66. package/lib/typescript/src/testing/device-fixture.d.ts.map +1 -0
  67. package/lib/typescript/src/testing/expose.d.ts +71 -0
  68. package/lib/typescript/src/testing/expose.d.ts.map +1 -0
  69. package/lib/typescript/src/testing/harness.d.ts +34 -0
  70. package/lib/typescript/src/testing/harness.d.ts.map +1 -0
  71. package/lib/typescript/src/testing/in-memory-serial-transport.d.ts +216 -0
  72. package/lib/typescript/src/testing/in-memory-serial-transport.d.ts.map +1 -0
  73. package/lib/typescript/src/testing/index.d.ts +33 -0
  74. package/lib/typescript/src/testing/index.d.ts.map +1 -0
  75. package/lib/typescript/src/testing/install-in-memory-serial-transport.d.ts +25 -0
  76. package/lib/typescript/src/testing/install-in-memory-serial-transport.d.ts.map +1 -0
  77. package/lib/typescript/src/testing/serial-client.d.ts +62 -0
  78. package/lib/typescript/src/testing/serial-client.d.ts.map +1 -0
  79. package/lib/typescript/src/testing/simulated-device.d.ts +127 -0
  80. package/lib/typescript/src/testing/simulated-device.d.ts.map +1 -0
  81. package/lib/typescript/src/testing/test-suite.d.ts +75 -0
  82. package/lib/typescript/src/testing/test-suite.d.ts.map +1 -0
  83. package/lib/typescript/src/transport.d.ts +131 -0
  84. package/lib/typescript/src/transport.d.ts.map +1 -0
  85. package/lib/typescript/src/websocket/WebSocketSerialTransport.d.ts +111 -0
  86. package/lib/typescript/src/websocket/WebSocketSerialTransport.d.ts.map +1 -0
  87. package/lib/typescript/src/websocket/bridge.d.ts +66 -0
  88. package/lib/typescript/src/websocket/bridge.d.ts.map +1 -0
  89. package/lib/typescript/src/websocket/index.d.ts +19 -0
  90. package/lib/typescript/src/websocket/index.d.ts.map +1 -0
  91. package/lib/typescript/src/websocket/protocol.d.ts +64 -0
  92. package/lib/typescript/src/websocket/protocol.d.ts.map +1 -0
  93. package/lib/typescript/src/websocket/serial-device-bridge.d.ts +32 -0
  94. package/lib/typescript/src/websocket/serial-device-bridge.d.ts.map +1 -0
  95. package/package.json +57 -3
  96. package/src/UsbSerial.ts +65 -90
  97. package/src/WebSerial.ts +351 -113
  98. package/src/index.ts +6 -8
  99. package/src/lib/dom-exception.ts +129 -60
  100. package/src/lib/event-target.ts +58 -21
  101. package/src/lib/promise.ts +7 -7
  102. package/src/lib/web-streams.ts +43 -0
  103. package/src/testing/device-fixture.ts +150 -0
  104. package/src/testing/expose.ts +147 -0
  105. package/src/testing/harness.ts +124 -0
  106. package/src/testing/in-memory-serial-transport.ts +840 -0
  107. package/src/testing/index.ts +90 -0
  108. package/src/testing/install-in-memory-serial-transport.ts +65 -0
  109. package/src/testing/serial-client.ts +313 -0
  110. package/src/testing/simulated-device.ts +193 -0
  111. package/src/testing/test-suite.ts +186 -0
  112. package/src/transport.ts +200 -0
  113. package/src/websocket/WebSocketSerialTransport.ts +796 -0
  114. package/src/websocket/bridge.ts +299 -0
  115. package/src/websocket/index.ts +38 -0
  116. package/src/websocket/protocol.ts +101 -0
  117. package/src/websocket/serial-device-bridge.ts +160 -0
  118. package/babel.config.js +0 -3
  119. package/biome.json +0 -35
  120. package/example/.watchmanconfig +0 -1
  121. package/example/App.tsx +0 -71
  122. package/example/__tests__/App.test.tsx +0 -16
  123. package/example/__tests__/connectEvents.test.tsx +0 -81
  124. package/example/__tests__/getPorts.test.tsx +0 -140
  125. package/example/android/app/build.gradle +0 -120
  126. package/example/android/app/debug.keystore +0 -0
  127. package/example/android/app/proguard-rules.pro +0 -10
  128. package/example/android/app/src/debug/AndroidManifest.xml +0 -9
  129. package/example/android/app/src/main/AndroidManifest.xml +0 -38
  130. package/example/android/app/src/main/java/dev/uzlopak/MainActivity.kt +0 -22
  131. package/example/android/app/src/main/java/dev/uzlopak/MainApplication.kt +0 -41
  132. package/example/android/app/src/main/res/drawable/rn_edit_text_material.xml +0 -37
  133. package/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png +0 -0
  134. package/example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png +0 -0
  135. package/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png +0 -0
  136. package/example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png +0 -0
  137. package/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png +0 -0
  138. package/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png +0 -0
  139. package/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png +0 -0
  140. package/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png +0 -0
  141. package/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
  142. package/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png +0 -0
  143. package/example/android/app/src/main/res/values/strings.xml +0 -3
  144. package/example/android/app/src/main/res/values/styles.xml +0 -9
  145. package/example/android/build.gradle +0 -22
  146. package/example/android/gradle/wrapper/gradle-wrapper.jar +0 -0
  147. package/example/android/gradle/wrapper/gradle-wrapper.properties +0 -7
  148. package/example/android/gradle.properties +0 -47
  149. package/example/android/gradlew +0 -252
  150. package/example/android/gradlew.bat +0 -94
  151. package/example/android/settings.gradle +0 -6
  152. package/example/app.json +0 -4
  153. package/example/babel.config.js +0 -21
  154. package/example/biome.json +0 -47
  155. package/example/deploy.sh +0 -11
  156. package/example/index.html +0 -26
  157. package/example/index.js +0 -9
  158. package/example/index.web.js +0 -8
  159. package/example/jest.config.js +0 -12
  160. package/example/metro.config.js +0 -58
  161. package/example/package-lock.json +0 -14510
  162. package/example/package.json +0 -48
  163. package/example/react-native.config.js +0 -17
  164. package/example/src/components/AppBar.tsx +0 -73
  165. package/example/src/components/Menu.tsx +0 -90
  166. package/example/src/components/SingleChoiceDialog.tsx +0 -120
  167. package/example/src/screens/ConnectScreen.tsx +0 -195
  168. package/example/src/screens/DevicesScreen.tsx +0 -252
  169. package/example/src/screens/TerminalScreen.tsx +0 -572
  170. package/example/src/settings.ts +0 -43
  171. package/example/src/theme.ts +0 -19
  172. package/example/src/util/TextUtil.ts +0 -129
  173. package/example/tsconfig.json +0 -10
  174. package/example/vite.config.mjs +0 -55
  175. package/scripts/deploy-release.sh +0 -127
  176. package/tsconfig.build.json +0 -7
  177. package/tsconfig.json +0 -20
@@ -0,0 +1,131 @@
1
+ /**
2
+ * Hardware-transport abstraction for the Web Serial polyfill.
3
+ *
4
+ * `SerialTransport` is the single interface that the `Serial`/`SerialPort`
5
+ * classes depend on to talk to "the device". The production implementation
6
+ * (`UsbSerialModule`, backed by the `NativeUsbSerial` TurboModule — see
7
+ * {@link ./UsbSerial}) and the in-memory test/dev double
8
+ * (`InMemorySerialTransport` — see {@link ./testing/in-memory-serial-transport}) both
9
+ * implement it.
10
+ *
11
+ * This module is intentionally free of any `react-native` import. That is what
12
+ * lets the virtual transport — and therefore the conformance suite built on top
13
+ * of it — run unchanged under Node/Jest, in a browser, and on a device.
14
+ */
15
+ export type ControlLine = 'RTS' | 'CTS' | 'DTR' | 'DSR' | 'CD' | 'RI';
16
+ export type FlowControl = 'NONE' | 'RTS_CTS' | 'DTR_DSR' | 'XON_XOFF' | 'XON_XOFF_INLINE';
17
+ export type PortFilter = {
18
+ usbVendorId?: number;
19
+ usbProductId?: number;
20
+ };
21
+ export type PortPickerLabels = {
22
+ titleSelectPort?: string;
23
+ titleNoPortsAvailable?: string;
24
+ messageNoPortsAvailable?: string;
25
+ };
26
+ export type PortId = {
27
+ deviceId: number;
28
+ portNumber: number;
29
+ usbVendorId: number;
30
+ usbProductId: number;
31
+ /**
32
+ * Whether the app currently holds Android USB permission to access this
33
+ * device (via the system attach dialog or a prior permission request).
34
+ */
35
+ hasPermission: boolean;
36
+ };
37
+ export type DataEvent = {
38
+ deviceId: number;
39
+ portNumber: number;
40
+ data: number[];
41
+ };
42
+ export type ErrorEvent = {
43
+ deviceId: number;
44
+ portNumber: number;
45
+ error: string;
46
+ /**
47
+ * Optional spec error name (e.g. "BreakError", "BufferOverrunError",
48
+ * "FramingError", "ParityError") for a typed read error. When present the
49
+ * polyfill surfaces a DOMException of that name on the readable stream
50
+ * (otherwise it defaults to "NetworkError"); the WPT-derived spec tests in
51
+ * src/__tests__/conformance-suite.ts exercise this (BreakError,
52
+ * BufferOverrunError).
53
+ */
54
+ errorName?: string;
55
+ };
56
+ export type ConnectEvent = {
57
+ deviceId: number;
58
+ usbVendorId: number;
59
+ usbProductId: number;
60
+ };
61
+ export declare const Parity: {
62
+ readonly NONE: 0;
63
+ readonly ODD: 1;
64
+ readonly EVEN: 2;
65
+ readonly MARK: 3;
66
+ readonly SPACE: 4;
67
+ };
68
+ export declare const DataBits: {
69
+ readonly FIVE: 5;
70
+ readonly SIX: 6;
71
+ readonly SEVEN: 7;
72
+ readonly EIGHT: 8;
73
+ };
74
+ export declare const StopBits: {
75
+ readonly ONE: 1;
76
+ readonly ONE_FIVE: 3;
77
+ readonly TWO: 2;
78
+ };
79
+ export type OpenOptions = {
80
+ baudRate: number;
81
+ dataBits?: number;
82
+ stopBits?: number;
83
+ parity?: number;
84
+ };
85
+ export declare const DEFAULT_OPEN_OPTIONS: Required<Omit<OpenOptions, 'baudRate'>>;
86
+ /** Handle returned by the `on*` subscription methods. */
87
+ export type Subscription = {
88
+ remove: () => void;
89
+ };
90
+ /**
91
+ * The contract every serial transport must satisfy. It mirrors the JS-friendly
92
+ * surface of `UsbSerialModule` exactly, so `UsbSerialModule implements
93
+ * SerialTransport` is a faithful 1:1 and any conforming double (e.g.
94
+ * `InMemorySerialTransport`) is a drop-in replacement.
95
+ *
96
+ * Ports are addressed by the pair `(deviceId, portNumber)`. Inbound bytes,
97
+ * read errors and device attach/detach arrive through the `on*` subscriptions.
98
+ */
99
+ export interface SerialTransport {
100
+ findAllDrivers(): Promise<ReadonlyArray<PortId>>;
101
+ showPortPicker(filter: ReadonlyArray<PortFilter>, labels?: PortPickerLabels): Promise<PortId>;
102
+ requestPermission(deviceId: number): Promise<boolean>;
103
+ open(deviceId: number, portNumber: number, options: OpenOptions): Promise<void>;
104
+ close(deviceId: number, portNumber: number): Promise<void>;
105
+ isOpen(deviceId: number, portNumber: number): boolean;
106
+ write(deviceId: number, portNumber: number, data: number[], timeout?: number): Promise<void>;
107
+ startReading(deviceId: number, portNumber: number): Promise<void>;
108
+ stopReading(deviceId: number, portNumber: number): Promise<void>;
109
+ setParameters(deviceId: number, portNumber: number, options: OpenOptions): Promise<void>;
110
+ setDTR(deviceId: number, portNumber: number, value: boolean): Promise<void>;
111
+ setRTS(deviceId: number, portNumber: number, value: boolean): Promise<void>;
112
+ getDTR(deviceId: number, portNumber: number): Promise<boolean>;
113
+ getRTS(deviceId: number, portNumber: number): Promise<boolean>;
114
+ getCD(deviceId: number, portNumber: number): Promise<boolean>;
115
+ getCTS(deviceId: number, portNumber: number): Promise<boolean>;
116
+ getDSR(deviceId: number, portNumber: number): Promise<boolean>;
117
+ getRI(deviceId: number, portNumber: number): Promise<boolean>;
118
+ getControlLines(deviceId: number, portNumber: number): Promise<ControlLine[]>;
119
+ getSupportedControlLines(deviceId: number, portNumber: number): Promise<ControlLine[]>;
120
+ setFlowControl(deviceId: number, portNumber: number, flowControl: FlowControl): Promise<void>;
121
+ getFlowControl(deviceId: number, portNumber: number): Promise<FlowControl>;
122
+ getSupportedFlowControl(deviceId: number, portNumber: number): Promise<FlowControl[]>;
123
+ setBreak(deviceId: number, portNumber: number, value: boolean): Promise<void>;
124
+ purgeHwBuffers(deviceId: number, portNumber: number, purgeWriteBuffers: boolean, purgeReadBuffers: boolean): Promise<void>;
125
+ getSerial(deviceId: number, portNumber: number): Promise<string>;
126
+ onData(listener: (event: DataEvent) => void): Subscription;
127
+ onError(listener: (event: ErrorEvent) => void): Subscription;
128
+ onConnect(listener: (event: ConnectEvent) => void): Subscription;
129
+ onDisconnect(listener: (event: ConnectEvent) => void): Subscription;
130
+ }
131
+ //# sourceMappingURL=transport.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../../../src/transport.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,111 @@
1
+ /**
2
+ * A {@link SerialTransport} that talks to a remote serial port over a WebSocket
3
+ * bridge (run `expose-serial-websocket` on the host — see `bin/expose-serial.js`).
4
+ *
5
+ * Because it implements the same transport contract as the native `UsbSerialModule` and the
6
+ * in-memory `InMemorySerialTransport`, it's a drop-in: the whole `Serial` /
7
+ * `SerialPort` polyfill (streams, signals, reconnect, the conformance suite)
8
+ * works on top of it unchanged.
9
+ *
10
+ * **Resilience.** The socket is supervised: an unexpected drop (server restart,
11
+ * network blip) is handled transparently — the transport reconnects with
12
+ * exponential backoff and *restores the session* (line coding, control signals,
13
+ * and read state) so the app's open `SerialPort` keeps working. A transient
14
+ * drop is therefore invisible to the polyfill (reads pause and resume; writes
15
+ * issued during the gap wait for the reconnection). Only a real remote
16
+ * disconnect (the host's serial port going away) or giving up after
17
+ * `maxReconnectAttempts` surfaces as a serial disconnect.
18
+ *
19
+ * @example
20
+ * import {Serial} from 'react-native-web-serial-api';
21
+ * import {WebSocketSerialTransport} from 'react-native-web-serial-api/websocket';
22
+ *
23
+ * const serial = new Serial(new WebSocketSerialTransport('ws://localhost:8080'));
24
+ * const [port] = await serial.getPorts();
25
+ * await port.open({baudRate: 115200});
26
+ */
27
+ import type { ConnectEvent, ControlLine, DataEvent, ErrorEvent, FlowControl, OpenOptions, PortFilter, PortId, PortPickerLabels, SerialTransport, Subscription } from '../transport';
28
+ /** Minimal structural WebSocket shape (browser & React Native both provide it). */
29
+ export interface WebSocketLike {
30
+ binaryType: string;
31
+ send(data: string | ArrayBufferLike | ArrayBufferView): void;
32
+ close(): void;
33
+ addEventListener(type: string, listener: (event: {
34
+ data?: unknown;
35
+ }) => void): void;
36
+ }
37
+ export type WebSocketCtor = new (url: string) => WebSocketLike;
38
+ export type WebSocketConnectionState = 'connecting' | 'open' | 'reconnecting' | 'suspended' | 'closed';
39
+ export type WebSocketSerialOptions = {
40
+ /** USB identity to report from getInfo() (cosmetic; default 0/0). */
41
+ usbVendorId?: number;
42
+ usbProductId?: number;
43
+ /** Serial number returned by getSerial(). */
44
+ serialNumber?: string;
45
+ /** WebSocket implementation (defaults to the global). Injectable for tests. */
46
+ WebSocket?: WebSocketCtor;
47
+ /** Auto-reconnect when the socket drops unexpectedly. Default `true`. */
48
+ reconnect?: boolean;
49
+ /** Initial reconnect backoff in ms; doubles each attempt. Default 250. */
50
+ reconnectInitialDelayMs?: number;
51
+ /** Cap on the reconnect backoff in ms. Default 10000. */
52
+ reconnectMaxDelayMs?: number;
53
+ /** Give up (surface a disconnect) after this many consecutive failures. Default Infinity. */
54
+ maxReconnectAttempts?: number;
55
+ /** How long write()/commands wait for a (re)connection before failing. Default 10000. */
56
+ connectTimeoutMs?: number;
57
+ /** How long a control command waits for its response before failing. Default 10000. */
58
+ commandTimeoutMs?: number;
59
+ /** Notified before each reconnect attempt (1-based attempt + the chosen delay). */
60
+ onReconnecting?: (attempt: number, delayMs: number) => void;
61
+ /** Notified once (re)connected; `reconnected` is false only for the first connect. */
62
+ onConnected?: (info: {
63
+ reconnected: boolean;
64
+ }) => void;
65
+ /** Notified when the transport is permanently closed (gave up, or `disconnect()`). */
66
+ onClosed?: (reason: string) => void;
67
+ };
68
+ type Listener<E> = (event: E) => void;
69
+ export declare class WebSocketSerialTransport implements SerialTransport {
70
+ #private;
71
+ constructor(url: string, options?: WebSocketSerialOptions);
72
+ /** Current connection state (advisory; the polyfill keeps working across reconnects). */
73
+ get connectionState(): WebSocketConnectionState;
74
+ findAllDrivers(): Promise<ReadonlyArray<PortId>>;
75
+ showPortPicker(_filter: ReadonlyArray<PortFilter>, _labels?: PortPickerLabels): Promise<PortId>;
76
+ requestPermission(_deviceId: number): Promise<boolean>;
77
+ open(_deviceId: number, _portNumber: number, options: OpenOptions): Promise<void>;
78
+ close(_deviceId: number, _portNumber: number): Promise<void>;
79
+ isOpen(_deviceId: number, _portNumber: number): boolean;
80
+ write(_deviceId: number, _portNumber: number, data: number[], _timeout?: number): Promise<void>;
81
+ startReading(_deviceId: number, _portNumber: number): Promise<void>;
82
+ stopReading(_deviceId: number, _portNumber: number): Promise<void>;
83
+ setParameters(_deviceId: number, _portNumber: number, options: OpenOptions): Promise<void>;
84
+ setDTR(_d: number, _p: number, value: boolean): Promise<void>;
85
+ setRTS(_d: number, _p: number, value: boolean): Promise<void>;
86
+ getDTR(_d: number, _p: number): Promise<boolean>;
87
+ getRTS(_d: number, _p: number): Promise<boolean>;
88
+ getCD(_d: number, _p: number): Promise<boolean>;
89
+ getCTS(_d: number, _p: number): Promise<boolean>;
90
+ getDSR(_d: number, _p: number): Promise<boolean>;
91
+ getRI(_d: number, _p: number): Promise<boolean>;
92
+ getControlLines(_d: number, _p: number): Promise<ControlLine[]>;
93
+ getSupportedControlLines(_d: number, _p: number): Promise<ControlLine[]>;
94
+ setFlowControl(_d: number, _p: number, _flowControl: FlowControl): Promise<void>;
95
+ getFlowControl(_d: number, _p: number): Promise<FlowControl>;
96
+ getSupportedFlowControl(_d: number, _p: number): Promise<FlowControl[]>;
97
+ setBreak(_d: number, _p: number, value: boolean): Promise<void>;
98
+ purgeHwBuffers(_d: number, _p: number, _purgeWrite: boolean, _purgeRead: boolean): Promise<void>;
99
+ getSerial(_d: number, _p: number): Promise<string>;
100
+ onData(listener: Listener<DataEvent>): Subscription;
101
+ onError(listener: Listener<ErrorEvent>): Subscription;
102
+ onConnect(listener: Listener<ConnectEvent>): Subscription;
103
+ onDisconnect(listener: Listener<ConnectEvent>): Subscription;
104
+ /**
105
+ * Permanently close the transport (and the remote port session). Cancels any
106
+ * pending reconnect and stops auto-reconnecting.
107
+ */
108
+ disconnect(): void;
109
+ }
110
+ export {};
111
+ //# sourceMappingURL=WebSocketSerialTransport.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WebSocketSerialTransport.d.ts","sourceRoot":"","sources":["../../../../src/websocket/WebSocketSerialTransport.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Server-side core of the WebSocket serial bridge — deliberately free of any
3
+ * `serialport`/`ws`/Node imports so it is unit-testable with fakes. The thin
4
+ * `bin/expose-serial.js` wrapper supplies the real `serialport` port and `ws`
5
+ * socket objects and calls {@link attachBridge} for each connection.
6
+ */
7
+ import { type PortInfo } from './protocol';
8
+ /** The subset of a `serialport` SerialPort that the bridge uses (callback API). */
9
+ export interface SerialLike {
10
+ write(data: Uint8Array, cb?: (err?: Error | null) => void): void;
11
+ set(opts: {
12
+ dtr?: boolean;
13
+ rts?: boolean;
14
+ brk?: boolean;
15
+ }, cb?: (err?: Error | null) => void): void;
16
+ get(cb: (err: Error | null, signals?: {
17
+ cts?: boolean;
18
+ dsr?: boolean;
19
+ dcd?: boolean;
20
+ ri?: boolean;
21
+ }) => void): void;
22
+ update(opts: {
23
+ baudRate: number;
24
+ }, cb?: (err?: Error | null) => void): void;
25
+ flush(cb?: (err?: Error | null) => void): void;
26
+ drain(cb?: (err?: Error | null) => void): void;
27
+ close(cb?: (err?: Error | null) => void): void;
28
+ on(event: 'data', listener: (data: Uint8Array) => void): void;
29
+ on(event: 'error', listener: (err: Error) => void): void;
30
+ on(event: 'open' | 'close', listener: () => void): void;
31
+ removeListener(event: string, listener: (...args: never[]) => void): void;
32
+ }
33
+ /** The subset of a `ws` WebSocket that the bridge uses. */
34
+ export interface WsLike {
35
+ send(data: string | Uint8Array): void;
36
+ on(event: 'message', listener: (data: unknown, isBinary: boolean) => void): void;
37
+ on(event: 'close', listener: () => void): void;
38
+ on(event: 'error', listener: (err: Error) => void): void;
39
+ }
40
+ export type BridgeOptions = {
41
+ /** Whether the port forwards serial→client data before `startReading`. */
42
+ readingByDefault?: boolean;
43
+ /** Optional logger for diagnostics. */
44
+ log?: (message: string) => void;
45
+ /** Optional static metadata or callback exposed via `getPortInfo`. */
46
+ portInfo?: PortInfo | (() => PortInfo | null | undefined);
47
+ };
48
+ /**
49
+ * Wire a single WebSocket connection to a single serial port: binary frames are
50
+ * piped both ways, JSON control frames invoke the corresponding serial method
51
+ * and get a response, and the port's data/error/close are surfaced as frames.
52
+ * Returns a teardown function that detaches the serial listeners.
53
+ */
54
+ export declare function attachBridge(serial: SerialLike, ws: WsLike, options?: BridgeOptions): () => void;
55
+ export type BridgeArgs = {
56
+ port?: string;
57
+ baudRate: number;
58
+ wsPort: number;
59
+ host: string;
60
+ allowRemote: boolean;
61
+ help: boolean;
62
+ };
63
+ /** Parse `expose-serial` CLI args + env into a normalised options object. */
64
+ export declare function parseBridgeArgs(argv: readonly string[], env?: Record<string, string | undefined>): BridgeArgs;
65
+ export declare const USAGE = "expose-serial-websocket \u2014 bridge a serial port to a WebSocket\n\nUsage:\n expose-serial-websocket --port <path> [--baudrate 115200] [--ws-port 8080]\n [--host 127.0.0.1] [--allow-remote]\n\nOptions:\n -p, --port <path> Serial device (e.g. /dev/ttyUSB0, COM3) [env SERIAL_PORT]\n -b, --baudrate <n> Baud rate (default 115200) [env BAUD_RATE]\n -w, --ws-port <n> WebSocket port (default 8080) [env WS_PORT]\n --host <addr> Listen address (default 127.0.0.1) [env HOST]\n --allow-remote Bind 0.0.0.0 (exposes the port to the network!)\n -h, --help Show this help\n\nConnect from the app with:\n new Serial(new WebSocketSerialTransport('ws://localhost:8080'))\n";
66
+ //# sourceMappingURL=bridge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bridge.d.ts","sourceRoot":"","sources":["../../../../src/websocket/bridge.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Remote-serial-over-WebSocket entry point.
3
+ *
4
+ * import {WebSocketSerialTransport}
5
+ * from 'react-native-web-serial-api/websocket';
6
+ *
7
+ * Client side: `WebSocketSerialTransport` connects an app to a remote serial
8
+ * port. Server side: {@link attachBridge} pipes a WebSocket to a serial port,
9
+ * and {@link SimulatedDeviceToSerialLike} lets that "serial port" be an in-memory
10
+ * {@link SimulatedDevice} simulator (used by `testing/exposeSimulatedDevice`). The
11
+ * wire protocol is in {@link ./protocol}; the bridge core in {@link ./bridge}.
12
+ */
13
+ export type { BridgeOptions, SerialLike, WsLike } from './bridge';
14
+ export { attachBridge } from './bridge';
15
+ export type { CommandMessage, CommandName, ControlMessage, EventMessage, InputSignals, LineCoding, Parity, PortInfo, ResponseMessage, } from './protocol';
16
+ export { portInfoFromDevice, SimulatedDeviceToSerialLike, } from './serial-device-bridge';
17
+ export type { WebSocketCtor, WebSocketLike, WebSocketSerialOptions, } from './WebSocketSerialTransport';
18
+ export { WebSocketSerialTransport } from './WebSocketSerialTransport';
19
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/websocket/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,YAAY,EAAC,aAAa,EAAE,UAAU,EAAE,MAAM,EAAC,MAAM,UAAU,CAAC;AAChE,OAAO,EAAC,YAAY,EAAC,MAAM,UAAU,CAAC;AACtC,YAAY,EACV,cAAc,EACd,WAAW,EACX,cAAc,EACd,YAAY,EACZ,YAAY,EACZ,UAAU,EACV,MAAM,EACN,QAAQ,EACR,eAAe,GAChB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,kBAAkB,EAClB,2BAA2B,GAC5B,MAAM,wBAAwB,CAAC;AAChC,YAAY,EACV,aAAa,EACb,aAAa,EACb,sBAAsB,GACvB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAC,wBAAwB,EAAC,MAAM,4BAA4B,CAAC"}
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Wire protocol shared by the WebSocket serial bridge (Node, see
3
+ * {@link ./bridge}) and the client transport ({@link ./WebSocketSerialTransport}).
4
+ *
5
+ * A single WebSocket carries two kinds of frame:
6
+ * - **binary** frames are raw serial bytes (client→bridge = data to write to the
7
+ * port; bridge→client = data read from the port), and
8
+ * - **text** frames are JSON control messages (this module).
9
+ *
10
+ * Control flow is request/response: the client tags each {@link CommandMessage}
11
+ * with an incrementing `id` and the bridge replies with a {@link ResponseMessage}
12
+ * carrying the same `id`. The bridge may also push unsolicited
13
+ * {@link EventMessage}s (port open/close/error).
14
+ */
15
+ import type { Parity } from '../WebSerial';
16
+ export type { Parity } from '../WebSerial';
17
+ /** Connection parameters for `setLineCoding`. */
18
+ export type LineCoding = {
19
+ baudRate: number;
20
+ dataBits?: number;
21
+ stopBits?: number;
22
+ parity?: Parity;
23
+ };
24
+ /** Input control-signal state returned by `getSignals`. */
25
+ export type InputSignals = {
26
+ cts: boolean;
27
+ dsr: boolean;
28
+ dcd: boolean;
29
+ ri: boolean;
30
+ };
31
+ /** Optional serial-port metadata returned by `getPortInfo`. */
32
+ export type PortInfo = {
33
+ usbVendorId?: number;
34
+ usbProductId?: number;
35
+ serialNumber?: string;
36
+ };
37
+ /** Every control command the bridge understands. */
38
+ export type CommandName = 'setLineCoding' | 'setBaudRate' | 'setSignals' | 'getSignals' | 'getPortInfo' | 'startReading' | 'stopReading' | 'flush' | 'drain' | 'break' | 'close';
39
+ /** client → bridge */
40
+ export type CommandMessage = {
41
+ type: 'command';
42
+ id: number;
43
+ command: CommandName;
44
+ args?: Record<string, unknown>;
45
+ };
46
+ /** bridge → client, answering a {@link CommandMessage} with the same `id`. */
47
+ export type ResponseMessage = {
48
+ type: 'response';
49
+ id: number;
50
+ error: string | null;
51
+ result?: unknown;
52
+ };
53
+ /** An unsolicited port lifecycle event the bridge emits to the client. */
54
+ export type BridgeEventName = 'open' | 'close' | 'error';
55
+ /** bridge → client */
56
+ export type EventMessage = {
57
+ type: 'event';
58
+ event: BridgeEventName;
59
+ error?: string;
60
+ };
61
+ export type ControlMessage = CommandMessage | ResponseMessage | EventMessage;
62
+ /** Parse a text frame into a {@link ControlMessage}, or null if it isn't one. */
63
+ export declare function parseControlMessage(text: string): ControlMessage | null;
64
+ //# sourceMappingURL=protocol.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../../../../src/websocket/protocol.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,cAAc,CAAC;AAEzC,YAAY,EAAC,MAAM,EAAC,MAAM,cAAc,CAAC;AAEzC,iDAAiD;AACjD,MAAM,MAAM,UAAU,GAAG;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,2DAA2D;AAC3D,MAAM,MAAM,YAAY,GAAG;IACzB,GAAG,EAAE,OAAO,CAAC;IACb,GAAG,EAAE,OAAO,CAAC;IACb,GAAG,EAAE,OAAO,CAAC;IACb,EAAE,EAAE,OAAO,CAAC;CACb,CAAC;AAEF,+DAA+D;AAC/D,MAAM,MAAM,QAAQ,GAAG;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,oDAAoD;AACpD,MAAM,MAAM,WAAW,GACnB,eAAe,GACf,aAAa,GACb,YAAY,GACZ,YAAY,GACZ,aAAa,GACb,cAAc,GACd,aAAa,GACb,OAAO,GACP,OAAO,GACP,OAAO,GACP,OAAO,CAAC;AAEZ,sBAAsB;AACtB,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,EAAE,SAAS,CAAC;IAChB,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,WAAW,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC,CAAC;AAEF,8EAA8E;AAC9E,MAAM,MAAM,eAAe,GAAG;IAC5B,IAAI,EAAE,UAAU,CAAC;IACjB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB,CAAC;AAEF,0EAA0E;AAC1E,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,CAAC;AAEzD,sBAAsB;AACtB,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,eAAe,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG,cAAc,GAAG,eAAe,GAAG,YAAY,CAAC;AAE7E,iFAAiF;AACjF,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI,CAevE"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Adapt an in-memory {@link DeviceHandle} simulator to the
3
+ * `serialport`-style {@link SerialLike} that {@link attachBridge} consumes, so a
4
+ * test can expose a *simulated* device over a WebSocket and have a real app
5
+ * connect to it with `new WebSocketSerialTransport(url)`. This is what makes the
6
+ * *same* device-driving test run in Jest (in-memory) and on a device/emulator
7
+ * (over the socket).
8
+ *
9
+ * Pure — no `ws`/`serialport`/Node imports — so it unit-tests with the same
10
+ * `FakeWs` + `attachBridge` fakes the bridge core uses. The lazy-`ws` server
11
+ * wrapper lives in `react-native-web-serial-api/testing` (`exposeSimulatedDevice`).
12
+ *
13
+ * Mapping (host app ⇄ simulator):
14
+ * - client binary frame → `write` → `device.onData`,
15
+ * - `setLineCoding`/`setBaudRate` → `update` → opens the sim + starts reading,
16
+ * - `setSignals` → `set` → `device.onHostSignals`,
17
+ * - `getSignals` → `get` → the device's asserted input signals,
18
+ * - `device.send(...)` → `'data'` → client binary frame,
19
+ * - `getPortInfo` → {@link portInfoFromDevice}.
20
+ */
21
+ import type { DeviceHandle, InMemorySerialTransport } from '../testing/in-memory-serial-transport';
22
+ import type { SerialLike } from './bridge';
23
+ import type { PortInfo } from './protocol';
24
+ /** The bridge's `getPortInfo` metadata, taken from the device's USB identity. */
25
+ export declare function portInfoFromDevice(device: DeviceHandle): PortInfo;
26
+ /**
27
+ * Build a {@link SerialLike} backed by `device` (already registered on
28
+ * `transport` via `addDevice`). One adapter per WebSocket connection; its
29
+ * transport subscriptions are released when the bridge tears down its listeners.
30
+ */
31
+ export declare function SimulatedDeviceToSerialLike(transport: InMemorySerialTransport, device: DeviceHandle): SerialLike;
32
+ //# sourceMappingURL=serial-device-bridge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serial-device-bridge.d.ts","sourceRoot":"","sources":["../../../../src/websocket/serial-device-bridge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,EACV,YAAY,EACZ,uBAAuB,EACxB,MAAM,uCAAuC,CAAC;AAE/C,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,UAAU,CAAC;AACzC,OAAO,KAAK,EAAC,QAAQ,EAAC,MAAM,YAAY,CAAC;AAEzC,iFAAiF;AACjF,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,YAAY,GAAG,QAAQ,CAMjE;AAED;;;;GAIG;AACH,wBAAgB,2BAA2B,CACzC,SAAS,EAAE,uBAAuB,EAClC,MAAM,EAAE,YAAY,GACnB,UAAU,CAiHZ"}
package/package.json CHANGED
@@ -1,17 +1,61 @@
1
1
  {
2
2
  "name": "react-native-web-serial-api",
3
- "version": "0.0.3",
3
+ "version": "0.2.0",
4
4
  "description": "W3C Web Serial API (navigator.serial) for React Native on Android, backed by a USB-serial TurboModule (built on mik3y/usb-serial-for-android).",
5
5
  "main": "lib/commonjs/index.js",
6
6
  "react-native": "lib/commonjs/index.js",
7
7
  "source": "src/index.ts",
8
8
  "types": "lib/typescript/src/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "source": "./src/index.ts",
12
+ "types": "./lib/typescript/src/index.d.ts",
13
+ "react-native": "./lib/commonjs/index.js",
14
+ "default": "./lib/commonjs/index.js"
15
+ },
16
+ "./testing": {
17
+ "source": "./src/testing/index.ts",
18
+ "types": "./lib/typescript/src/testing/index.d.ts",
19
+ "react-native": "./lib/commonjs/testing/index.js",
20
+ "default": "./lib/commonjs/testing/index.js"
21
+ },
22
+ "./websocket": {
23
+ "source": "./src/websocket/index.ts",
24
+ "types": "./lib/typescript/src/websocket/index.d.ts",
25
+ "react-native": "./lib/commonjs/websocket/index.js",
26
+ "default": "./lib/commonjs/websocket/index.js"
27
+ },
28
+ "./package.json": "./package.json"
29
+ },
30
+ "bin": {
31
+ "expose-serial-websocket": "bin/expose-serial.js"
32
+ },
33
+ "files": [
34
+ "src",
35
+ "lib",
36
+ "android",
37
+ "bin",
38
+ "react-native.config.js",
39
+ "TESTING.md",
40
+ "!android/build",
41
+ "!android/src/test",
42
+ "!**/__tests__",
43
+ "!**/__fixtures__",
44
+ "!**/__mocks__",
45
+ "!**/.*"
46
+ ],
9
47
  "scripts": {
10
48
  "prepare": "bob build",
11
49
  "clean": "node -e \"require('fs').rm(\\\"./lib\\\", { recursive: true }, () => {console.log(\\\"'lib' folder deleted\\\")})\"",
12
50
  "typecheck": "tsc --noEmit",
13
- "lint": "biome check src",
14
- "format": "biome check --write src"
51
+ "lint": "biome check",
52
+ "format": "biome check --write",
53
+ "test": "jest",
54
+ "test:watch": "jest --watch",
55
+ "test:coverage": "jest --coverage",
56
+ "test:android": "cd example/android && ./gradlew :react-native-web-serial-api:testDebugUnitTest",
57
+ "test:emulator:e2e": "npm --prefix example run e2e",
58
+ "test:host+emulator": "node ./node_modules/jest/bin/jest.js && npm --prefix example run e2e"
15
59
  },
16
60
  "keywords": [
17
61
  "react-native",
@@ -46,6 +90,10 @@
46
90
  "dependencies": {
47
91
  "web-streams-polyfill": "^4.3.0"
48
92
  },
93
+ "optionalDependencies": {
94
+ "serialport": "^12.0.0",
95
+ "ws": "^8.18.0"
96
+ },
49
97
  "peerDependencies": {
50
98
  "react": "*",
51
99
  "react-native": "*"
@@ -53,7 +101,13 @@
53
101
  "devDependencies": {
54
102
  "@biomejs/biome": "2.4.16",
55
103
  "@react-native/babel-preset": "0.85.3",
104
+ "@react-native/jest-preset": "0.85.3",
105
+ "@types/jest": "^29.5.14",
106
+ "@types/node": "^25.9.2",
56
107
  "@types/react": "^19.2.6",
108
+ "@types/ws": "^8.18.1",
109
+ "babel-jest": "29.6.3",
110
+ "jest": "29.6.3",
57
111
  "react": "19.2.3",
58
112
  "react-native": "0.85.3",
59
113
  "react-native-builder-bob": "^0.41.0",