react-native-web-serial-api 0.0.1

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 (114) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +147 -0
  3. package/android/build.gradle +62 -0
  4. package/android/gradle.properties +6 -0
  5. package/android/src/main/AndroidManifest.xml +21 -0
  6. package/android/src/main/java/dev/webserialapi/NativeUsbSerialModule.java +704 -0
  7. package/android/src/main/java/dev/webserialapi/NativeUsbSerialPackage.java +46 -0
  8. package/android/src/main/java/dev/webserialapi/PortPickerActivity.java +235 -0
  9. package/android/src/main/java/dev/webserialapi/UsbDetachReceiver.java +37 -0
  10. package/android/src/main/res/xml/device_filter.xml +13 -0
  11. package/babel.config.js +3 -0
  12. package/biome.json +35 -0
  13. package/example/.watchmanconfig +1 -0
  14. package/example/App.tsx +71 -0
  15. package/example/__tests__/App.test.tsx +16 -0
  16. package/example/android/app/build.gradle +120 -0
  17. package/example/android/app/debug.keystore +0 -0
  18. package/example/android/app/proguard-rules.pro +10 -0
  19. package/example/android/app/src/debug/AndroidManifest.xml +9 -0
  20. package/example/android/app/src/main/AndroidManifest.xml +38 -0
  21. package/example/android/app/src/main/java/dev/uzlopak/MainActivity.kt +22 -0
  22. package/example/android/app/src/main/java/dev/uzlopak/MainApplication.kt +41 -0
  23. package/example/android/app/src/main/res/drawable/rn_edit_text_material.xml +37 -0
  24. package/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png +0 -0
  25. package/example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png +0 -0
  26. package/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png +0 -0
  27. package/example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png +0 -0
  28. package/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png +0 -0
  29. package/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png +0 -0
  30. package/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png +0 -0
  31. package/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png +0 -0
  32. package/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
  33. package/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png +0 -0
  34. package/example/android/app/src/main/res/values/strings.xml +3 -0
  35. package/example/android/app/src/main/res/values/styles.xml +9 -0
  36. package/example/android/build.gradle +22 -0
  37. package/example/android/gradle/wrapper/gradle-wrapper.jar +0 -0
  38. package/example/android/gradle/wrapper/gradle-wrapper.properties +7 -0
  39. package/example/android/gradle.properties +47 -0
  40. package/example/android/gradlew +252 -0
  41. package/example/android/gradlew.bat +94 -0
  42. package/example/android/settings.gradle +6 -0
  43. package/example/app.json +4 -0
  44. package/example/babel.config.js +21 -0
  45. package/example/biome.json +47 -0
  46. package/example/deploy.sh +11 -0
  47. package/example/index.html +26 -0
  48. package/example/index.js +9 -0
  49. package/example/index.web.js +8 -0
  50. package/example/jest.config.js +12 -0
  51. package/example/metro.config.js +58 -0
  52. package/example/package-lock.json +14510 -0
  53. package/example/package.json +48 -0
  54. package/example/react-native.config.js +17 -0
  55. package/example/src/components/AppBar.tsx +73 -0
  56. package/example/src/components/Menu.tsx +90 -0
  57. package/example/src/components/SingleChoiceDialog.tsx +120 -0
  58. package/example/src/screens/ConnectScreen.tsx +195 -0
  59. package/example/src/screens/DevicesScreen.tsx +141 -0
  60. package/example/src/screens/TerminalScreen.tsx +564 -0
  61. package/example/src/settings.ts +43 -0
  62. package/example/src/theme.ts +19 -0
  63. package/example/src/util/TextUtil.ts +129 -0
  64. package/example/tsconfig.json +10 -0
  65. package/example/vite.config.mjs +55 -0
  66. package/lib/commonjs/NativeUsbSerial.js +11 -0
  67. package/lib/commonjs/NativeUsbSerial.js.map +1 -0
  68. package/lib/commonjs/NativeUsbSerial.web.js +12 -0
  69. package/lib/commonjs/NativeUsbSerial.web.js.map +1 -0
  70. package/lib/commonjs/UsbSerial.js +149 -0
  71. package/lib/commonjs/UsbSerial.js.map +1 -0
  72. package/lib/commonjs/WebSerial.js +852 -0
  73. package/lib/commonjs/WebSerial.js.map +1 -0
  74. package/lib/commonjs/index.js +44 -0
  75. package/lib/commonjs/index.js.map +1 -0
  76. package/lib/commonjs/package.json +1 -0
  77. package/lib/commonjs/serial.android.js +13 -0
  78. package/lib/commonjs/serial.android.js.map +1 -0
  79. package/lib/commonjs/serial.js +13 -0
  80. package/lib/commonjs/serial.js.map +1 -0
  81. package/lib/commonjs/serial.web.js +11 -0
  82. package/lib/commonjs/serial.web.js.map +1 -0
  83. package/lib/typescript/src/NativeUsbSerial.d.ts +51 -0
  84. package/lib/typescript/src/NativeUsbSerial.d.ts.map +1 -0
  85. package/lib/typescript/src/NativeUsbSerial.web.d.ts +3 -0
  86. package/lib/typescript/src/NativeUsbSerial.web.d.ts.map +1 -0
  87. package/lib/typescript/src/UsbSerial.d.ts +97 -0
  88. package/lib/typescript/src/UsbSerial.d.ts.map +1 -0
  89. package/lib/typescript/src/WebSerial.d.ts +236 -0
  90. package/lib/typescript/src/WebSerial.d.ts.map +1 -0
  91. package/lib/typescript/src/index.d.ts +7 -0
  92. package/lib/typescript/src/index.d.ts.map +1 -0
  93. package/lib/typescript/src/serial.android.d.ts +2 -0
  94. package/lib/typescript/src/serial.android.d.ts.map +1 -0
  95. package/lib/typescript/src/serial.d.ts +2 -0
  96. package/lib/typescript/src/serial.d.ts.map +1 -0
  97. package/lib/typescript/src/serial.web.d.ts +4 -0
  98. package/lib/typescript/src/serial.web.d.ts.map +1 -0
  99. package/package.json +78 -0
  100. package/react-native.config.js +9 -0
  101. package/scripts/deploy-release.sh +127 -0
  102. package/src/NativeUsbSerial.ts +124 -0
  103. package/src/NativeUsbSerial.web.ts +5 -0
  104. package/src/UsbSerial.ts +305 -0
  105. package/src/WebSerial.ts +1084 -0
  106. package/src/index.ts +23 -0
  107. package/src/lib/dom-exception.ts +161 -0
  108. package/src/lib/event-target.ts +170 -0
  109. package/src/lib/promise.ts +19 -0
  110. package/src/serial.android.ts +1 -0
  111. package/src/serial.ts +1 -0
  112. package/src/serial.web.ts +6 -0
  113. package/tsconfig.build.json +7 -0
  114. package/tsconfig.json +20 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Aras Abbasi
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,147 @@
1
+ # react-native-web-serial-api
2
+
3
+ > The [W3C Web Serial API](https://wicg.github.io/serial/) (`navigator.serial`) for **React Native on Android**, backed by a USB-serial TurboModule built on top of [`mik3y/usb-serial-for-android`](https://github.com/mik3y/usb-serial-for-android).
4
+
5
+ Talk to USB serial devices (FTDI, CP210x, CH340/CH341, PL2303, CDC-ACM …) from React Native using the **exact same API you already know from the browser** — `serial.requestPort()`, `port.open()`, `port.readable`, `port.writable`, and so on.
6
+
7
+ - Spec-compliant `Serial` / `SerialPort` implementation (`getPorts`, `requestPort`, `open`, `close`, `readable`, `writable`, `setSignals`, `getSignals`, `forget`, `connect`/`disconnect` events)
8
+ - New Architecture **TurboModule**
9
+ - Native port-picker dialog + USB permission handling
10
+ - Backed by Web Streams (`ReadableStream` / `WritableStream`)
11
+ - Drop-in for code written against the browser Web Serial API (on web it transparently uses the native `navigator.serial`)
12
+
13
+ > **Platform support:** Android only. On web (`react-native-web`) the package delegates to the browser's native Web Serial API. There is no iOS implementation (iOS does not allow generic USB-serial access), so iOS autolinking is disabled.
14
+
15
+ ## Installation
16
+
17
+ ```sh
18
+ npm install react-native-web-serial-api
19
+ # or
20
+ yarn add react-native-web-serial-api
21
+ ```
22
+
23
+ This is a New Architecture library, so make sure your app has the New Architecture enabled (default in recent React Native). No manual linking is required — the module is autolinked.
24
+
25
+ ### Android setup
26
+
27
+ The library ships its own `AndroidManifest.xml` that declares the port-picker activity, the detach receiver, and the `android.hardware.usb.host` feature, so usually **no extra configuration is needed**.
28
+
29
+ If you want your app to be **launched automatically when a matching device is plugged in**, add an intent filter to your launcher activity in `android/app/src/main/AndroidManifest.xml`:
30
+
31
+ ```xml
32
+ <activity android:name=".MainActivity" ...>
33
+ <intent-filter>
34
+ <action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
35
+ </intent-filter>
36
+ <!-- The device_filter resource is provided by the library -->
37
+ <meta-data
38
+ android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
39
+ android:resource="@xml/device_filter" />
40
+ </activity>
41
+ ```
42
+
43
+ The bundled `@xml/device_filter` matches the common USB-serial chips (CDC-ACM, FTDI `0x0403`, CP210x `0x10C4`, CH34x `0x1A86`, PL2303 `0x067B`). Provide your own `res/xml/device_filter.xml` to override it.
44
+
45
+ ## Usage
46
+
47
+ ```ts
48
+ import {serial} from 'react-native-web-serial-api';
49
+
50
+ async function run() {
51
+ // Shows a native dialog to pick a port, then requests USB permission.
52
+ const port = await serial.requestPort({
53
+ filters: [{usbVendorId: 0x0403}], // optional — e.g. only FTDI devices
54
+ });
55
+
56
+ await port.open({baudRate: 115200, dataBits: 8, stopBits: 1, parity: 'none'});
57
+
58
+ // Write
59
+ const writer = port.writable.getWriter();
60
+ await writer.write(new TextEncoder().encode('Hello\n'));
61
+ writer.releaseLock();
62
+
63
+ // Read
64
+ const reader = port.readable.getReader();
65
+ const {value} = await reader.read(); // value is a Uint8Array
66
+ console.log(value);
67
+ reader.releaseLock();
68
+
69
+ await port.close();
70
+ }
71
+ ```
72
+
73
+ ### Control & status signals
74
+
75
+ ```ts
76
+ await port.setSignals({dataTerminalReady: true, requestToSend: false});
77
+ const {clearToSend, dataCarrierDetect, ringIndicator, dataSetReady} =
78
+ await port.getSignals();
79
+ ```
80
+
81
+ ### Connect / disconnect events
82
+
83
+ ```ts
84
+ serial.addEventListener('connect', () => console.log('device attached'));
85
+ serial.addEventListener('disconnect', () => console.log('device detached'));
86
+
87
+ port.addEventListener('disconnect', () => console.log('this port went away'));
88
+ ```
89
+
90
+ ### Listing already-permitted ports
91
+
92
+ ```ts
93
+ const ports = await serial.getPorts();
94
+ ```
95
+
96
+ ## API
97
+
98
+ The package exposes:
99
+
100
+ | Export | Description |
101
+ | --- | --- |
102
+ | `serial` | A ready-to-use `Serial` instance (`navigator.serial` equivalent). |
103
+ | `Serial`, `SerialPort` | The Web Serial API classes. |
104
+ | `UsbSerial` | Lower-level access to the raw USB-serial TurboModule (Android only). |
105
+ | `Event`, `EventTarget` | The event primitives used by the polyfill. |
106
+ | Types | `SerialOptions`, `SerialOutputSignals`, `SerialInputSignals`, `SerialPortInfo`, `SerialPortFilter`, `SerialPortRequestOptions`. |
107
+
108
+ ## Example app
109
+
110
+ The [`example/`](./example) app is a React Native port of [SimpleUsbTerminal](https://github.com/kai-morich/SimpleUsbTerminal) built entirely on this package's Web Serial API — a **Devices** list (with baud-rate selection) and a **Terminal** (colored receive/send log, HEX mode, newline selection, clear, control-lines row with RTS/DTR toggles, flow control, and Send BREAK).
111
+
112
+ ```sh
113
+ # install the library's build tooling
114
+ npm install
115
+
116
+ # install and run the example on Android
117
+ cd example
118
+ npm install
119
+ npm run android
120
+ ```
121
+
122
+ Because it uses the Web Serial API, a few SimpleUsbTerminal details map differently:
123
+
124
+ - **Driver/chip name** isn't exposed by the Web Serial API (`getInfo()` returns only VID/PID), so rows show `Vendor/Product` plus a best-effort chip label from known vendor IDs.
125
+ - **Flow control** is limited to *None* / *Hardware (RTS-CTS)* — XON/XOFF and DTR/DSR aren't in the Web Serial spec. Changing it reconnects the port.
126
+ - The Android background **foreground-service notification** is omitted (it's service plumbing unrelated to serial I/O).
127
+
128
+ ### Run the example in the browser
129
+
130
+ The example also runs as a web app via [`react-native-web`](https://necolas.github.io/react-native-web/) + [Vite](https://vite.dev/). On web, the package delegates to the browser's native `navigator.serial`, so the exact same `App.tsx` talks to real serial hardware over WebUSB-style permissions.
131
+
132
+ ```sh
133
+ cd example
134
+ npm install
135
+ npm run web # dev server at http://localhost:5173
136
+ # npm run web:build # production build into example/dist
137
+ ```
138
+
139
+ > Web Serial only works in **Chromium-based browsers** (Chrome / Edge / Opera) over a **secure context** (`http://localhost` counts), and `requestPort()` must be called from a user gesture — the demo's "Request port" button handles that.
140
+
141
+ ## How it works
142
+
143
+ The JavaScript layer (`src/`) implements the Web Serial API on top of a thin TurboModule (`NativeUsbSerial`) whose native Android implementation (`android/src/main/java/dev/webserialapi/`) wraps `usb-serial-for-android`. Reads/writes are bridged to `ReadableStream`/`WritableStream` via [`web-streams-polyfill`](https://github.com/MattiasBuelens/web-streams-polyfill).
144
+
145
+ ## License
146
+
147
+ MIT © Aras Abbasi
@@ -0,0 +1,62 @@
1
+ buildscript {
2
+ // Allow the library to be built either standalone (using the *_ defaults from
3
+ // gradle.properties) or as part of a host app (using the host's rootProject.ext).
4
+ ext.getExtOrDefault = { name ->
5
+ return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties["WebSerialApi_" + name]
6
+ }
7
+
8
+ // When autolinked into a host app, the Android Gradle Plugin is already on the
9
+ // root project's buildscript classpath, so we must not redeclare it here. Only
10
+ // pull it in (with an explicit version) when building this library standalone.
11
+ def agpVersion = rootProject.ext.has("agpVersion") ? rootProject.ext.get("agpVersion") : "8.7.2"
12
+
13
+ repositories {
14
+ google()
15
+ mavenCentral()
16
+ }
17
+
18
+ dependencies {
19
+ if (project == rootProject) {
20
+ classpath "com.android.tools.build:gradle:$agpVersion"
21
+ }
22
+ }
23
+ }
24
+
25
+ apply plugin: "com.android.library"
26
+ // Drives React Native Codegen for the TurboModule spec declared in package.json -> codegenConfig
27
+ apply plugin: "com.facebook.react"
28
+
29
+ def getExtOrIntegerDefault(name) {
30
+ return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties["WebSerialApi_" + name]).toInteger()
31
+ }
32
+
33
+ android {
34
+ namespace "dev.webserialapi"
35
+
36
+ compileSdkVersion getExtOrIntegerDefault("compileSdkVersion")
37
+
38
+ defaultConfig {
39
+ minSdkVersion getExtOrIntegerDefault("minSdkVersion")
40
+ targetSdkVersion getExtOrIntegerDefault("targetSdkVersion")
41
+ }
42
+
43
+ compileOptions {
44
+ sourceCompatibility JavaVersion.VERSION_17
45
+ targetCompatibility JavaVersion.VERSION_17
46
+ }
47
+ }
48
+
49
+ repositories {
50
+ mavenCentral()
51
+ google()
52
+ // usb-serial-for-android is published on JitPack
53
+ maven { url "https://jitpack.io" }
54
+ }
55
+
56
+ dependencies {
57
+ // The version of react-native is set by the host app's React Native Gradle Plugin
58
+ implementation "com.facebook.react:react-android"
59
+ implementation "com.github.mik3y:usb-serial-for-android:3.10.0"
60
+ // PortPickerActivity extends AppCompatActivity and uses the AppCompat dialog theme
61
+ implementation "androidx.appcompat:appcompat:1.7.0"
62
+ }
@@ -0,0 +1,6 @@
1
+ # Fallback values used when the library is built standalone (i.e. when the host
2
+ # app's rootProject.ext does not provide these). Inside a host app the app's
3
+ # values take precedence (see getExtOrDefault in build.gradle).
4
+ WebSerialApi_compileSdkVersion=35
5
+ WebSerialApi_targetSdkVersion=34
6
+ WebSerialApi_minSdkVersion=24
@@ -0,0 +1,21 @@
1
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android">
2
+
3
+ <uses-feature android:name="android.hardware.usb.host" />
4
+
5
+ <application>
6
+ <!-- Native dialog shown by serial.requestPort() to let the user pick a port -->
7
+ <activity
8
+ android:name="dev.webserialapi.PortPickerActivity"
9
+ android:exported="false"
10
+ android:theme="@style/Theme.AppCompat.Dialog" />
11
+
12
+ <!-- Emits a "disconnect" JS event when a USB serial device is unplugged -->
13
+ <receiver
14
+ android:name="dev.webserialapi.UsbDetachReceiver"
15
+ android:exported="true">
16
+ <intent-filter>
17
+ <action android:name="android.hardware.usb.action.USB_DEVICE_DETACHED" />
18
+ </intent-filter>
19
+ </receiver>
20
+ </application>
21
+ </manifest>