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
@@ -1,252 +0,0 @@
1
- import React from 'react';
2
- import {
3
- AppState,
4
- FlatList,
5
- StyleSheet,
6
- Text,
7
- TouchableOpacity,
8
- View,
9
- } from 'react-native';
10
- import type {SerialPort} from 'react-native-web-serial-api';
11
- import {serial, UsbSerial} from 'react-native-web-serial-api';
12
- import {AppBar} from '../components/AppBar';
13
- import {colors} from '../theme';
14
-
15
- type Props = {
16
- onSelect: (port: SerialPort) => void;
17
- };
18
-
19
- // One row in the device list: a probed USB-serial port, which may or may not be
20
- // accessible yet (Android USB permission).
21
- type DeviceRow = {
22
- deviceId: number;
23
- portNumber: number;
24
- usbVendorId: number;
25
- usbProductId: number;
26
- hasPermission: boolean;
27
- };
28
-
29
- function hex4(n: number | undefined): string {
30
- return (n ?? 0).toString(16).toUpperCase().padStart(4, '0');
31
- }
32
-
33
- // The Web Serial API only exposes VID/PID (not the driver/chip class), so we
34
- // derive a best-effort label from known USB-serial vendor IDs.
35
- function chipLabel(vendorId: number | undefined): string {
36
- switch (vendorId) {
37
- case 0x0403:
38
- return 'FTDI';
39
- case 0x10c4:
40
- return 'CP210x';
41
- case 0x1a86:
42
- return 'CH34x';
43
- case 0x067b:
44
- return 'Prolific';
45
- default:
46
- return 'USB serial';
47
- }
48
- }
49
-
50
- // Native (Android) gives us every probed port plus its permission state, so we
51
- // can show plugged-in-but-unpermitted devices too. On web that lower-level
52
- // module is unavailable; fall back to serial.getPorts() (already permitted).
53
- function nativeUsb() {
54
- try {
55
- return UsbSerial.getUsbSerial();
56
- } catch {
57
- return null;
58
- }
59
- }
60
-
61
- export function DevicesScreen({onSelect}: Props) {
62
- const [rows, setRows] = React.useState<DeviceRow[]>([]);
63
- const [error, setError] = React.useState<string | null>(null);
64
-
65
- const refresh = React.useCallback(async () => {
66
- setError(null);
67
- try {
68
- const usb = nativeUsb();
69
- if (usb) {
70
- // Full enumeration incl. unpermitted devices.
71
- setRows((await usb.findAllDrivers()) as DeviceRow[]);
72
- return;
73
- }
74
- if (!serial) {
75
- setError('Web Serial API is not available on this platform.');
76
- return;
77
- }
78
- // Web: only already-granted ports are visible; all are permitted.
79
- const ports = await serial.getPorts();
80
- setRows(
81
- ports.map(p => {
82
- const info = p.getInfo();
83
- return {
84
- deviceId: -1,
85
- portNumber: 0,
86
- usbVendorId: info.usbVendorId ?? 0,
87
- usbProductId: info.usbProductId ?? 0,
88
- hasPermission: true,
89
- };
90
- }),
91
- );
92
- } catch (e: any) {
93
- setError(e?.message ?? String(e));
94
- }
95
- }, []);
96
-
97
- React.useEffect(() => {
98
- refresh();
99
- if (!serial) {
100
- return;
101
- }
102
- // Auto-refresh on attach, detach, AND permission-grant (the library emits
103
- // "connect" for all three), so the list stays current without manual taps.
104
- serial.addEventListener('connect', refresh);
105
- serial.addEventListener('disconnect', refresh);
106
- return () => {
107
- serial.removeEventListener('connect', refresh);
108
- serial.removeEventListener('disconnect', refresh);
109
- };
110
- }, [refresh]);
111
-
112
- // Resolve the SerialPort for an already-permitted row and proceed.
113
- const openPermitted = React.useCallback(
114
- async (row: DeviceRow) => {
115
- setError(null);
116
- try {
117
- const ports = await serial.getPorts();
118
- const match =
119
- ports.find(p => {
120
- const info = p.getInfo();
121
- return (
122
- info.usbVendorId === row.usbVendorId &&
123
- info.usbProductId === row.usbProductId
124
- );
125
- }) ?? ports[0];
126
- if (match) {
127
- onSelect(match);
128
- } else {
129
- setError('Device is no longer available.');
130
- }
131
- } catch (e: any) {
132
- setError(e?.message ?? String(e));
133
- }
134
- },
135
- [onSelect],
136
- );
137
-
138
- // Tap on an unpermitted row: request Android USB permission. On grant,
139
- // refresh immediately.
140
- const grantPermission = React.useCallback(
141
- async (row: DeviceRow) => {
142
- setError(null);
143
- const usb = nativeUsb();
144
- if (!usb) {
145
- return;
146
- }
147
- try {
148
- await usb.requestPermission(row.deviceId);
149
- } catch (e: any) {
150
- setError(e?.message ?? String(e));
151
- }
152
- refresh();
153
- },
154
- [refresh],
155
- );
156
-
157
- const requestNew = React.useCallback(async () => {
158
- setError(null);
159
- try {
160
- const port = await serial.requestPort();
161
- onSelect(port);
162
- } catch (e: any) {
163
- // user cancelled the picker, or no device
164
- setError(e?.message ?? String(e));
165
- }
166
- }, [onSelect]);
167
-
168
- // Refresh device list when app returns to foreground (covers system dialog grants)
169
- React.useEffect(() => {
170
- const handleAppStateChange = (state: string) => {
171
- if (state === 'active') {
172
- refresh();
173
- }
174
- };
175
- const subscription = AppState.addEventListener(
176
- 'change',
177
- handleAppStateChange,
178
- );
179
- return () => {
180
- subscription.remove();
181
- };
182
- }, [refresh]);
183
-
184
- return (
185
- <View style={styles.container}>
186
- <AppBar
187
- title="Simple USB Terminal"
188
- menu={[
189
- {key: 'refresh', title: 'Refresh Devices', onPress: refresh},
190
- {key: 'request', title: 'Connect new device…', onPress: requestNew},
191
- ]}
192
- />
193
-
194
- <View style={styles.header}>
195
- <Text style={styles.headerText}>USB Devices</Text>
196
- </View>
197
-
198
- {error ? <Text style={styles.error}>{error}</Text> : null}
199
-
200
- <FlatList
201
- data={rows}
202
- keyExtractor={(r, i) => `${r.deviceId}:${r.portNumber}:${i}`}
203
- ListEmptyComponent={
204
- <Text style={styles.empty}>{'<no USB devices found>'}</Text>
205
- }
206
- renderItem={({item}) => (
207
- <TouchableOpacity
208
- style={styles.item}
209
- onPress={() =>
210
- item.hasPermission ? openPermitted(item) : grantPermission(item)
211
- }>
212
- <Text style={[styles.text1, !item.hasPermission && styles.dimmed]}>
213
- {chipLabel(item.usbVendorId)}
214
- {item.hasPermission ? '' : ' 🔒 tap to allow'}
215
- </Text>
216
- <Text style={[styles.text2, !item.hasPermission && styles.dimmed]}>
217
- {`Vendor ${hex4(item.usbVendorId)}, Product ${hex4(
218
- item.usbProductId,
219
- )}`}
220
- </Text>
221
- </TouchableOpacity>
222
- )}
223
- />
224
- </View>
225
- );
226
- }
227
-
228
- const styles = StyleSheet.create({
229
- container: {flex: 1, backgroundColor: colors.background},
230
- header: {
231
- backgroundColor: colors.divider,
232
- paddingVertical: 12,
233
- alignItems: 'center',
234
- },
235
- headerText: {fontSize: 16, color: colors.text},
236
- error: {color: '#c62828', padding: 12},
237
- empty: {
238
- fontSize: 18,
239
- textAlign: 'center',
240
- color: colors.textSecondary,
241
- marginTop: 24,
242
- },
243
- item: {paddingVertical: 8, paddingHorizontal: 12},
244
- text1: {fontSize: 16, color: colors.text, marginTop: 4, marginHorizontal: 12},
245
- text2: {
246
- fontSize: 13,
247
- color: colors.textSecondary,
248
- marginHorizontal: 20,
249
- marginBottom: 4,
250
- },
251
- dimmed: {opacity: 0.5},
252
- });