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.
- package/README.md +198 -104
- package/TESTING.md +542 -0
- package/android/build.gradle +16 -2
- package/android/src/main/java/dev/webserialapi/NativeUsbSerialModule.java +74 -11
- package/android/src/main/java/dev/webserialapi/PortPickerActivity.java +61 -59
- package/bin/expose-serial.js +205 -0
- package/lib/commonjs/UsbSerial.js +58 -26
- package/lib/commonjs/UsbSerial.js.map +1 -1
- package/lib/commonjs/WebSerial.js +273 -77
- package/lib/commonjs/WebSerial.js.map +1 -1
- package/lib/commonjs/index.js +15 -3
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/lib/dom-exception.js +176 -0
- package/lib/commonjs/lib/dom-exception.js.map +1 -0
- package/lib/commonjs/lib/event-target.js +140 -0
- package/lib/commonjs/lib/event-target.js.map +1 -0
- package/lib/commonjs/lib/promise.js +23 -0
- package/lib/commonjs/lib/promise.js.map +1 -0
- package/lib/commonjs/lib/web-streams.js +42 -0
- package/lib/commonjs/lib/web-streams.js.map +1 -0
- package/lib/commonjs/testing/device-fixture.js +70 -0
- package/lib/commonjs/testing/device-fixture.js.map +1 -0
- package/lib/commonjs/testing/expose.js +91 -0
- package/lib/commonjs/testing/expose.js.map +1 -0
- package/lib/commonjs/testing/harness.js +98 -0
- package/lib/commonjs/testing/harness.js.map +1 -0
- package/lib/commonjs/testing/in-memory-serial-transport.js +653 -0
- package/lib/commonjs/testing/in-memory-serial-transport.js.map +1 -0
- package/lib/commonjs/testing/index.js +153 -0
- package/lib/commonjs/testing/index.js.map +1 -0
- package/lib/commonjs/testing/install-in-memory-serial-transport.js +54 -0
- package/lib/commonjs/testing/install-in-memory-serial-transport.js.map +1 -0
- package/lib/commonjs/testing/serial-client.js +277 -0
- package/lib/commonjs/testing/serial-client.js.map +1 -0
- package/lib/commonjs/testing/simulated-device.js +164 -0
- package/lib/commonjs/testing/simulated-device.js.map +1 -0
- package/lib/commonjs/testing/test-suite.js +142 -0
- package/lib/commonjs/testing/test-suite.js.map +1 -0
- package/lib/commonjs/transport.js +61 -0
- package/lib/commonjs/transport.js.map +1 -0
- package/lib/commonjs/websocket/WebSocketSerialTransport.js +659 -0
- package/lib/commonjs/websocket/WebSocketSerialTransport.js.map +1 -0
- package/lib/commonjs/websocket/bridge.js +234 -0
- package/lib/commonjs/websocket/bridge.js.map +1 -0
- package/lib/commonjs/websocket/index.js +33 -0
- package/lib/commonjs/websocket/index.js.map +1 -0
- package/lib/commonjs/websocket/protocol.js +55 -0
- package/lib/commonjs/websocket/protocol.js.map +1 -0
- package/lib/commonjs/websocket/serial-device-bridge.js +130 -0
- package/lib/commonjs/websocket/serial-device-bridge.js.map +1 -0
- package/lib/typescript/src/UsbSerial.d.ts +24 -67
- package/lib/typescript/src/UsbSerial.d.ts.map +1 -1
- package/lib/typescript/src/WebSerial.d.ts +16 -7
- package/lib/typescript/src/WebSerial.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +3 -1
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/lib/dom-exception.d.ts +100 -0
- package/lib/typescript/src/lib/dom-exception.d.ts.map +1 -0
- package/lib/typescript/src/lib/event-target.d.ts +55 -0
- package/lib/typescript/src/lib/event-target.d.ts.map +1 -0
- package/lib/typescript/src/lib/promise.d.ts +11 -0
- package/lib/typescript/src/lib/promise.d.ts.map +1 -0
- package/lib/typescript/src/lib/web-streams.d.ts +9 -0
- package/lib/typescript/src/lib/web-streams.d.ts.map +1 -0
- package/lib/typescript/src/testing/device-fixture.d.ts +70 -0
- package/lib/typescript/src/testing/device-fixture.d.ts.map +1 -0
- package/lib/typescript/src/testing/expose.d.ts +71 -0
- package/lib/typescript/src/testing/expose.d.ts.map +1 -0
- package/lib/typescript/src/testing/harness.d.ts +34 -0
- package/lib/typescript/src/testing/harness.d.ts.map +1 -0
- package/lib/typescript/src/testing/in-memory-serial-transport.d.ts +216 -0
- package/lib/typescript/src/testing/in-memory-serial-transport.d.ts.map +1 -0
- package/lib/typescript/src/testing/index.d.ts +33 -0
- package/lib/typescript/src/testing/index.d.ts.map +1 -0
- package/lib/typescript/src/testing/install-in-memory-serial-transport.d.ts +25 -0
- package/lib/typescript/src/testing/install-in-memory-serial-transport.d.ts.map +1 -0
- package/lib/typescript/src/testing/serial-client.d.ts +62 -0
- package/lib/typescript/src/testing/serial-client.d.ts.map +1 -0
- package/lib/typescript/src/testing/simulated-device.d.ts +127 -0
- package/lib/typescript/src/testing/simulated-device.d.ts.map +1 -0
- package/lib/typescript/src/testing/test-suite.d.ts +75 -0
- package/lib/typescript/src/testing/test-suite.d.ts.map +1 -0
- package/lib/typescript/src/transport.d.ts +131 -0
- package/lib/typescript/src/transport.d.ts.map +1 -0
- package/lib/typescript/src/websocket/WebSocketSerialTransport.d.ts +111 -0
- package/lib/typescript/src/websocket/WebSocketSerialTransport.d.ts.map +1 -0
- package/lib/typescript/src/websocket/bridge.d.ts +66 -0
- package/lib/typescript/src/websocket/bridge.d.ts.map +1 -0
- package/lib/typescript/src/websocket/index.d.ts +19 -0
- package/lib/typescript/src/websocket/index.d.ts.map +1 -0
- package/lib/typescript/src/websocket/protocol.d.ts +64 -0
- package/lib/typescript/src/websocket/protocol.d.ts.map +1 -0
- package/lib/typescript/src/websocket/serial-device-bridge.d.ts +32 -0
- package/lib/typescript/src/websocket/serial-device-bridge.d.ts.map +1 -0
- package/package.json +57 -3
- package/src/UsbSerial.ts +65 -90
- package/src/WebSerial.ts +351 -113
- package/src/index.ts +6 -8
- package/src/lib/dom-exception.ts +129 -60
- package/src/lib/event-target.ts +58 -21
- package/src/lib/promise.ts +7 -7
- package/src/lib/web-streams.ts +43 -0
- package/src/testing/device-fixture.ts +150 -0
- package/src/testing/expose.ts +147 -0
- package/src/testing/harness.ts +124 -0
- package/src/testing/in-memory-serial-transport.ts +840 -0
- package/src/testing/index.ts +90 -0
- package/src/testing/install-in-memory-serial-transport.ts +65 -0
- package/src/testing/serial-client.ts +313 -0
- package/src/testing/simulated-device.ts +193 -0
- package/src/testing/test-suite.ts +186 -0
- package/src/transport.ts +200 -0
- package/src/websocket/WebSocketSerialTransport.ts +796 -0
- package/src/websocket/bridge.ts +299 -0
- package/src/websocket/index.ts +38 -0
- package/src/websocket/protocol.ts +101 -0
- package/src/websocket/serial-device-bridge.ts +160 -0
- package/babel.config.js +0 -3
- package/biome.json +0 -35
- package/example/.watchmanconfig +0 -1
- package/example/App.tsx +0 -71
- package/example/__tests__/App.test.tsx +0 -16
- package/example/__tests__/connectEvents.test.tsx +0 -81
- package/example/__tests__/getPorts.test.tsx +0 -140
- package/example/android/app/build.gradle +0 -120
- package/example/android/app/debug.keystore +0 -0
- package/example/android/app/proguard-rules.pro +0 -10
- package/example/android/app/src/debug/AndroidManifest.xml +0 -9
- package/example/android/app/src/main/AndroidManifest.xml +0 -38
- package/example/android/app/src/main/java/dev/uzlopak/MainActivity.kt +0 -22
- package/example/android/app/src/main/java/dev/uzlopak/MainApplication.kt +0 -41
- package/example/android/app/src/main/res/drawable/rn_edit_text_material.xml +0 -37
- package/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png +0 -0
- package/example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png +0 -0
- package/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png +0 -0
- package/example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png +0 -0
- package/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png +0 -0
- package/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png +0 -0
- package/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png +0 -0
- package/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png +0 -0
- package/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
- package/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png +0 -0
- package/example/android/app/src/main/res/values/strings.xml +0 -3
- package/example/android/app/src/main/res/values/styles.xml +0 -9
- package/example/android/build.gradle +0 -22
- package/example/android/gradle/wrapper/gradle-wrapper.jar +0 -0
- package/example/android/gradle/wrapper/gradle-wrapper.properties +0 -7
- package/example/android/gradle.properties +0 -47
- package/example/android/gradlew +0 -252
- package/example/android/gradlew.bat +0 -94
- package/example/android/settings.gradle +0 -6
- package/example/app.json +0 -4
- package/example/babel.config.js +0 -21
- package/example/biome.json +0 -47
- package/example/deploy.sh +0 -11
- package/example/index.html +0 -26
- package/example/index.js +0 -9
- package/example/index.web.js +0 -8
- package/example/jest.config.js +0 -12
- package/example/metro.config.js +0 -58
- package/example/package-lock.json +0 -14510
- package/example/package.json +0 -48
- package/example/react-native.config.js +0 -17
- package/example/src/components/AppBar.tsx +0 -73
- package/example/src/components/Menu.tsx +0 -90
- package/example/src/components/SingleChoiceDialog.tsx +0 -120
- package/example/src/screens/ConnectScreen.tsx +0 -195
- package/example/src/screens/DevicesScreen.tsx +0 -252
- package/example/src/screens/TerminalScreen.tsx +0 -572
- package/example/src/settings.ts +0 -43
- package/example/src/theme.ts +0 -19
- package/example/src/util/TextUtil.ts +0 -129
- package/example/tsconfig.json +0 -10
- package/example/vite.config.mjs +0 -55
- package/scripts/deploy-release.sh +0 -127
- package/tsconfig.build.json +0 -7
- package/tsconfig.json +0 -20
package/src/index.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
// Platform-resolved Web Serial API instance.
|
|
2
|
-
// On React Native (Android) this is the USB-serial-backed polyfill; on web it
|
|
3
|
-
// is the browser's native navigator.serial. (See serial.android.ts / serial.web.ts)
|
|
4
1
|
export {default as serial} from './serial';
|
|
2
|
+
export type {SerialTransport} from './transport';
|
|
3
|
+
export {resetUsbSerial, setUsbSerial} from './UsbSerial';
|
|
5
4
|
export type {
|
|
6
5
|
SerialInputSignals,
|
|
7
6
|
SerialOptions,
|
|
@@ -13,11 +12,10 @@ export type {
|
|
|
13
12
|
// W3C Web Serial API classes
|
|
14
13
|
export {Serial, SerialPort} from './WebSerial';
|
|
15
14
|
|
|
16
|
-
// Lower-level access to the raw USB-serial TurboModule (Android only).
|
|
17
|
-
// (Imported + re-exported rather than `export * as` so older Babel presets
|
|
18
|
-
// without @babel/plugin-transform-export-namespace-from can consume the source.)
|
|
19
15
|
import * as UsbSerial from './UsbSerial';
|
|
20
16
|
|
|
21
|
-
|
|
22
|
-
|
|
17
|
+
export {
|
|
18
|
+
EventImpl as Event,
|
|
19
|
+
EventTargetImpl as EventTarget,
|
|
20
|
+
} from './lib/event-target';
|
|
23
21
|
export {UsbSerial};
|
package/src/lib/dom-exception.ts
CHANGED
|
@@ -33,7 +33,7 @@ const DOM_EXCEPTION_CODES: Record<string, number> = {
|
|
|
33
33
|
};
|
|
34
34
|
|
|
35
35
|
const DOM_EXCEPTION_NAMES: Record<number, string> = Object.fromEntries(
|
|
36
|
-
Object.entries(DOM_EXCEPTION_CODES).map(([name, code]) => [code, name])
|
|
36
|
+
Object.entries(DOM_EXCEPTION_CODES).map(([name, code]) => [code, name]),
|
|
37
37
|
);
|
|
38
38
|
|
|
39
39
|
export interface DOMExceptionConstructor {
|
|
@@ -78,74 +78,134 @@ class DOMExceptionPolyfill extends Error {
|
|
|
78
78
|
readonly name: string;
|
|
79
79
|
|
|
80
80
|
// --- Static legacy-code constants ---
|
|
81
|
-
static readonly INDEX_SIZE_ERR
|
|
82
|
-
static readonly DOMSTRING_SIZE_ERR
|
|
83
|
-
static readonly HIERARCHY_REQUEST_ERR
|
|
84
|
-
static readonly WRONG_DOCUMENT_ERR
|
|
85
|
-
static readonly INVALID_CHARACTER_ERR
|
|
86
|
-
static readonly NO_DATA_ALLOWED_ERR
|
|
81
|
+
static readonly INDEX_SIZE_ERR = 1 as const;
|
|
82
|
+
static readonly DOMSTRING_SIZE_ERR = 2 as const;
|
|
83
|
+
static readonly HIERARCHY_REQUEST_ERR = 3 as const;
|
|
84
|
+
static readonly WRONG_DOCUMENT_ERR = 4 as const;
|
|
85
|
+
static readonly INVALID_CHARACTER_ERR = 5 as const;
|
|
86
|
+
static readonly NO_DATA_ALLOWED_ERR = 6 as const;
|
|
87
87
|
static readonly NO_MODIFICATION_ALLOWED_ERR = 7 as const;
|
|
88
|
-
static readonly NOT_FOUND_ERR
|
|
89
|
-
static readonly NOT_SUPPORTED_ERR
|
|
90
|
-
static readonly INUSE_ATTRIBUTE_ERR
|
|
91
|
-
static readonly INVALID_STATE_ERR
|
|
92
|
-
static readonly SYNTAX_ERR
|
|
93
|
-
static readonly INVALID_MODIFICATION_ERR
|
|
94
|
-
static readonly NAMESPACE_ERR
|
|
95
|
-
static readonly INVALID_ACCESS_ERR
|
|
96
|
-
static readonly VALIDATION_ERR
|
|
97
|
-
static readonly TYPE_MISMATCH_ERR
|
|
98
|
-
static readonly SECURITY_ERR
|
|
99
|
-
static readonly NETWORK_ERR
|
|
100
|
-
static readonly ABORT_ERR
|
|
101
|
-
static readonly URL_MISMATCH_ERR
|
|
102
|
-
static readonly QUOTA_EXCEEDED_ERR
|
|
103
|
-
static readonly TIMEOUT_ERR
|
|
104
|
-
static readonly INVALID_NODE_TYPE_ERR
|
|
105
|
-
static readonly DATA_CLONE_ERR
|
|
88
|
+
static readonly NOT_FOUND_ERR = 8 as const;
|
|
89
|
+
static readonly NOT_SUPPORTED_ERR = 9 as const;
|
|
90
|
+
static readonly INUSE_ATTRIBUTE_ERR = 10 as const;
|
|
91
|
+
static readonly INVALID_STATE_ERR = 11 as const;
|
|
92
|
+
static readonly SYNTAX_ERR = 12 as const;
|
|
93
|
+
static readonly INVALID_MODIFICATION_ERR = 13 as const;
|
|
94
|
+
static readonly NAMESPACE_ERR = 14 as const;
|
|
95
|
+
static readonly INVALID_ACCESS_ERR = 15 as const;
|
|
96
|
+
static readonly VALIDATION_ERR = 16 as const;
|
|
97
|
+
static readonly TYPE_MISMATCH_ERR = 17 as const;
|
|
98
|
+
static readonly SECURITY_ERR = 18 as const;
|
|
99
|
+
static readonly NETWORK_ERR = 19 as const;
|
|
100
|
+
static readonly ABORT_ERR = 20 as const;
|
|
101
|
+
static readonly URL_MISMATCH_ERR = 21 as const;
|
|
102
|
+
static readonly QUOTA_EXCEEDED_ERR = 22 as const;
|
|
103
|
+
static readonly TIMEOUT_ERR = 23 as const;
|
|
104
|
+
static readonly INVALID_NODE_TYPE_ERR = 24 as const;
|
|
105
|
+
static readonly DATA_CLONE_ERR = 25 as const;
|
|
106
106
|
|
|
107
|
-
constructor(message =
|
|
107
|
+
constructor(message = '', name = 'Error') {
|
|
108
108
|
super(message);
|
|
109
109
|
|
|
110
110
|
// Restore the correct prototype chain when transpiled to ES5
|
|
111
111
|
Object.setPrototypeOf(this, new.target.prototype);
|
|
112
112
|
|
|
113
|
-
this.name
|
|
113
|
+
this.name = name;
|
|
114
114
|
this.message = message;
|
|
115
|
-
this.code
|
|
115
|
+
this.code = DOM_EXCEPTION_CODES[name] ?? 0;
|
|
116
116
|
|
|
117
117
|
// Provide a useful stack trace in V8 / SpiderMonkey
|
|
118
|
-
|
|
119
|
-
|
|
118
|
+
const ErrorWithStack = Error as ErrorConstructor & {
|
|
119
|
+
captureStackTrace?: (
|
|
120
|
+
target: object,
|
|
121
|
+
constructorOpt?:
|
|
122
|
+
| ((...args: never[]) => unknown)
|
|
123
|
+
| (abstract new (
|
|
124
|
+
...args: never[]
|
|
125
|
+
) => unknown),
|
|
126
|
+
) => void;
|
|
127
|
+
};
|
|
128
|
+
if (typeof ErrorWithStack.captureStackTrace === 'function') {
|
|
129
|
+
ErrorWithStack.captureStackTrace(this, new.target);
|
|
120
130
|
}
|
|
121
131
|
}
|
|
122
132
|
|
|
123
133
|
/** Mirror static constants on the prototype (spec §3.1.2) */
|
|
124
|
-
get INDEX_SIZE_ERR()
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
get
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
get
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
get
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
get
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
get
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
get
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
get
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
get
|
|
134
|
+
get INDEX_SIZE_ERR() {
|
|
135
|
+
return 1;
|
|
136
|
+
}
|
|
137
|
+
get DOMSTRING_SIZE_ERR() {
|
|
138
|
+
return 2;
|
|
139
|
+
}
|
|
140
|
+
get HIERARCHY_REQUEST_ERR() {
|
|
141
|
+
return 3;
|
|
142
|
+
}
|
|
143
|
+
get WRONG_DOCUMENT_ERR() {
|
|
144
|
+
return 4;
|
|
145
|
+
}
|
|
146
|
+
get INVALID_CHARACTER_ERR() {
|
|
147
|
+
return 5;
|
|
148
|
+
}
|
|
149
|
+
get NO_DATA_ALLOWED_ERR() {
|
|
150
|
+
return 6;
|
|
151
|
+
}
|
|
152
|
+
get NO_MODIFICATION_ALLOWED_ERR() {
|
|
153
|
+
return 7;
|
|
154
|
+
}
|
|
155
|
+
get NOT_FOUND_ERR() {
|
|
156
|
+
return 8;
|
|
157
|
+
}
|
|
158
|
+
get NOT_SUPPORTED_ERR() {
|
|
159
|
+
return 9;
|
|
160
|
+
}
|
|
161
|
+
get INUSE_ATTRIBUTE_ERR() {
|
|
162
|
+
return 10;
|
|
163
|
+
}
|
|
164
|
+
get INVALID_STATE_ERR() {
|
|
165
|
+
return 11;
|
|
166
|
+
}
|
|
167
|
+
get SYNTAX_ERR() {
|
|
168
|
+
return 12;
|
|
169
|
+
}
|
|
170
|
+
get INVALID_MODIFICATION_ERR() {
|
|
171
|
+
return 13;
|
|
172
|
+
}
|
|
173
|
+
get NAMESPACE_ERR() {
|
|
174
|
+
return 14;
|
|
175
|
+
}
|
|
176
|
+
get INVALID_ACCESS_ERR() {
|
|
177
|
+
return 15;
|
|
178
|
+
}
|
|
179
|
+
get VALIDATION_ERR() {
|
|
180
|
+
return 16;
|
|
181
|
+
}
|
|
182
|
+
get TYPE_MISMATCH_ERR() {
|
|
183
|
+
return 17;
|
|
184
|
+
}
|
|
185
|
+
get SECURITY_ERR() {
|
|
186
|
+
return 18;
|
|
187
|
+
}
|
|
188
|
+
get NETWORK_ERR() {
|
|
189
|
+
return 19;
|
|
190
|
+
}
|
|
191
|
+
get ABORT_ERR() {
|
|
192
|
+
return 20;
|
|
193
|
+
}
|
|
194
|
+
get URL_MISMATCH_ERR() {
|
|
195
|
+
return 21;
|
|
196
|
+
}
|
|
197
|
+
get QUOTA_EXCEEDED_ERR() {
|
|
198
|
+
return 22;
|
|
199
|
+
}
|
|
200
|
+
get TIMEOUT_ERR() {
|
|
201
|
+
return 23;
|
|
202
|
+
}
|
|
203
|
+
get INVALID_NODE_TYPE_ERR() {
|
|
204
|
+
return 24;
|
|
205
|
+
}
|
|
206
|
+
get DATA_CLONE_ERR() {
|
|
207
|
+
return 25;
|
|
208
|
+
}
|
|
149
209
|
|
|
150
210
|
/** Canonical string representation */
|
|
151
211
|
toString(): string {
|
|
@@ -153,9 +213,18 @@ class DOMExceptionPolyfill extends Error {
|
|
|
153
213
|
}
|
|
154
214
|
}
|
|
155
215
|
|
|
156
|
-
export const DOMExceptionImpl = (globalThis as
|
|
157
|
-
|
|
158
|
-
|
|
216
|
+
export const DOMExceptionImpl = (globalThis as Record<string, unknown>)
|
|
217
|
+
.DOMException
|
|
218
|
+
? ((globalThis as Record<string, unknown>)
|
|
219
|
+
.DOMException as DOMExceptionConstructor)
|
|
220
|
+
: (DOMExceptionPolyfill as DOMExceptionConstructor);
|
|
159
221
|
|
|
160
|
-
export {
|
|
161
|
-
|
|
222
|
+
export {
|
|
223
|
+
DOM_EXCEPTION_CODES,
|
|
224
|
+
DOM_EXCEPTION_NAMES,
|
|
225
|
+
DOMExceptionImpl as DOMException,
|
|
226
|
+
// Exported for testing: in environments that already provide a global
|
|
227
|
+
// DOMException (Node, modern browsers) the export above is the native one, so
|
|
228
|
+
// the polyfill class would otherwise never run.
|
|
229
|
+
DOMExceptionPolyfill,
|
|
230
|
+
};
|
package/src/lib/event-target.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
|
|
2
1
|
export interface EventInit {
|
|
3
2
|
bubbles?: boolean;
|
|
4
3
|
cancelable?: boolean;
|
|
@@ -66,7 +65,7 @@ export class Event {
|
|
|
66
65
|
}
|
|
67
66
|
}
|
|
68
67
|
|
|
69
|
-
type Mutable<T> = {
|
|
68
|
+
type Mutable<T> = {-readonly [P in keyof T]: T[P]};
|
|
70
69
|
|
|
71
70
|
type EventListener = (event: Event) => void;
|
|
72
71
|
|
|
@@ -77,7 +76,7 @@ interface EventListenerObject {
|
|
|
77
76
|
type EventListenerOrEventListenerObject = EventListener | EventListenerObject;
|
|
78
77
|
|
|
79
78
|
type ListenerOptions =
|
|
80
|
-
| {
|
|
79
|
+
| {once?: boolean; capture?: boolean; passive?: boolean}
|
|
81
80
|
| boolean
|
|
82
81
|
| undefined;
|
|
83
82
|
|
|
@@ -91,7 +90,23 @@ type SecretMap = Record<string, ListenerInfo[]>;
|
|
|
91
90
|
|
|
92
91
|
const wm = new WeakMap<object, SecretMap>();
|
|
93
92
|
|
|
94
|
-
|
|
93
|
+
// Optional event-propagation parents. An event dispatched on a child also runs
|
|
94
|
+
// its parent's listeners, while event.target stays the original child. This
|
|
95
|
+
// models the W3C "SerialPort's parent is the Serial" relationship, so a
|
|
96
|
+
// connect/disconnect observed on `serial` reports event.target === the
|
|
97
|
+
// SerialPort (per the Web Serial spec and WPT).
|
|
98
|
+
const eventParents = new WeakMap<EventTarget, EventTarget>();
|
|
99
|
+
|
|
100
|
+
/** Make events dispatched on `child` also bubble to `parent`'s listeners. */
|
|
101
|
+
export function setEventParent(child: EventTarget, parent: EventTarget): void {
|
|
102
|
+
eventParents.set(child, parent);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function define<T extends object>(
|
|
106
|
+
target: T,
|
|
107
|
+
name: string,
|
|
108
|
+
value: unknown,
|
|
109
|
+
): void {
|
|
95
110
|
Object.defineProperty(target, name, {
|
|
96
111
|
configurable: true,
|
|
97
112
|
writable: true,
|
|
@@ -102,13 +117,13 @@ function define<T extends object>(target: T, name: string, value: unknown): void
|
|
|
102
117
|
function dispatch(this: Event, info: ListenerInfo): boolean {
|
|
103
118
|
const options = info.options;
|
|
104
119
|
const once =
|
|
105
|
-
typeof options ===
|
|
120
|
+
typeof options === 'object' && options !== null ? options.once : false;
|
|
106
121
|
|
|
107
122
|
if (once) {
|
|
108
123
|
info.target.removeEventListener(this.type, info.listener);
|
|
109
124
|
}
|
|
110
125
|
|
|
111
|
-
if (typeof info.listener ===
|
|
126
|
+
if (typeof info.listener === 'function') {
|
|
112
127
|
info.listener.call(info.target, this);
|
|
113
128
|
} else {
|
|
114
129
|
info.listener.handleEvent(this);
|
|
@@ -125,40 +140,50 @@ export class EventTarget {
|
|
|
125
140
|
addEventListener(
|
|
126
141
|
type: string,
|
|
127
142
|
listener: EventListenerOrEventListenerObject,
|
|
128
|
-
options?: ListenerOptions
|
|
143
|
+
options?: ListenerOptions,
|
|
129
144
|
): void {
|
|
130
145
|
const secret = wm.get(this)!;
|
|
131
|
-
|
|
146
|
+
let listeners = secret[type];
|
|
147
|
+
if (!listeners) {
|
|
148
|
+
listeners = [];
|
|
149
|
+
secret[type] = listeners;
|
|
150
|
+
}
|
|
132
151
|
|
|
133
152
|
for (let i = 0; i < listeners.length; i++) {
|
|
134
153
|
if (listeners[i].listener === listener) return;
|
|
135
154
|
}
|
|
136
155
|
|
|
137
|
-
listeners.push({
|
|
156
|
+
listeners.push({target: this, listener, options});
|
|
138
157
|
}
|
|
139
158
|
|
|
140
159
|
dispatchEvent(event: Event): boolean {
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
listeners
|
|
148
|
-
|
|
149
|
-
|
|
160
|
+
// The event's target is the node it was dispatched on; it then bubbles up
|
|
161
|
+
// the eventParents chain (currentTarget changes, target does not).
|
|
162
|
+
define(event, 'target', this);
|
|
163
|
+
let node: EventTarget | null = this;
|
|
164
|
+
while (node) {
|
|
165
|
+
const listeners = wm.get(node)?.[event.type];
|
|
166
|
+
if (listeners?.length) {
|
|
167
|
+
define(event, 'currentTarget', node);
|
|
168
|
+
listeners.slice(0).some(dispatch, event);
|
|
169
|
+
}
|
|
170
|
+
if (event.cancelBubble) break;
|
|
171
|
+
node = eventParents.get(node) ?? null;
|
|
150
172
|
}
|
|
151
|
-
|
|
173
|
+
// Per the DOM, target persists after dispatch (a re-dispatch overwrites it
|
|
174
|
+
// at the top of this method); only currentTarget is cleared.
|
|
175
|
+
define(event, 'currentTarget', null);
|
|
152
176
|
return true;
|
|
153
177
|
}
|
|
154
178
|
|
|
155
179
|
removeEventListener(
|
|
156
180
|
type: string,
|
|
157
181
|
listener: EventListenerOrEventListenerObject,
|
|
158
|
-
_options?: ListenerOptions
|
|
182
|
+
_options?: ListenerOptions,
|
|
159
183
|
): void {
|
|
160
184
|
const secret = wm.get(this)!;
|
|
161
|
-
const listeners
|
|
185
|
+
const listeners = secret[type];
|
|
186
|
+
if (!listeners) return;
|
|
162
187
|
|
|
163
188
|
for (let i = 0; i < listeners.length; i++) {
|
|
164
189
|
if (listeners[i].listener === listener) {
|
|
@@ -168,3 +193,15 @@ export class EventTarget {
|
|
|
168
193
|
}
|
|
169
194
|
}
|
|
170
195
|
}
|
|
196
|
+
|
|
197
|
+
type MaybeGlobal = Record<string, unknown>;
|
|
198
|
+
|
|
199
|
+
export const EventImpl =
|
|
200
|
+
typeof (globalThis as MaybeGlobal).Event === 'function'
|
|
201
|
+
? ((globalThis as MaybeGlobal).Event as typeof Event)
|
|
202
|
+
: Event;
|
|
203
|
+
|
|
204
|
+
export const EventTargetImpl =
|
|
205
|
+
typeof (globalThis as MaybeGlobal).EventTarget === 'function'
|
|
206
|
+
? ((globalThis as MaybeGlobal).EventTarget as typeof EventTarget)
|
|
207
|
+
: EventTarget;
|
package/src/lib/promise.ts
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
type DeferredPromise<T> = {
|
|
2
2
|
promise: Promise<T>;
|
|
3
3
|
resolve: (value?: T) => void;
|
|
4
|
-
reject: (reason?:
|
|
4
|
+
reject: (reason?: unknown) => void;
|
|
5
5
|
};
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* @returns An object containing a promise and its resolve/reject methods.
|
|
9
9
|
*/
|
|
10
10
|
export function createDeferredPromise<T>(): DeferredPromise<T> {
|
|
11
|
-
let res:
|
|
12
|
-
let rej:
|
|
11
|
+
let res: unknown;
|
|
12
|
+
let rej: unknown;
|
|
13
13
|
const promise = new Promise<T>((resolve, reject) => {
|
|
14
|
-
res = resolve
|
|
15
|
-
rej = reject
|
|
16
|
-
})
|
|
14
|
+
res = resolve;
|
|
15
|
+
rej = reject;
|
|
16
|
+
});
|
|
17
17
|
|
|
18
|
-
return {
|
|
18
|
+
return {promise, resolve: res, reject: rej} as DeferredPromise<T>;
|
|
19
19
|
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ByteLengthQueuingStrategy as ByteLengthQueuingStrategyPolyfill,
|
|
3
|
+
ReadableStream as ReadableStreamPolyfill,
|
|
4
|
+
WritableStream as WritableStreamPolyfill,
|
|
5
|
+
} from 'web-streams-polyfill';
|
|
6
|
+
|
|
7
|
+
type MaybeGlobal = Record<string, unknown>;
|
|
8
|
+
|
|
9
|
+
function getCtor<T>(name: string, fallback: T): T {
|
|
10
|
+
const value = (globalThis as MaybeGlobal)[name];
|
|
11
|
+
return typeof value === 'function' ? (value as T) : fallback;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export {
|
|
15
|
+
ByteLengthQueuingStrategyPolyfill,
|
|
16
|
+
ReadableStreamPolyfill,
|
|
17
|
+
WritableStreamPolyfill,
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export const ReadableStreamImpl = getCtor(
|
|
21
|
+
'ReadableStream',
|
|
22
|
+
ReadableStreamPolyfill,
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
export const WritableStreamImpl = getCtor(
|
|
26
|
+
'WritableStream',
|
|
27
|
+
WritableStreamPolyfill,
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
export const ByteLengthQueuingStrategyImpl = getCtor(
|
|
31
|
+
'ByteLengthQueuingStrategy',
|
|
32
|
+
ByteLengthQueuingStrategyPolyfill,
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
// Companion TYPE aliases so that `Impl as X` imports carry a type too. Without
|
|
36
|
+
// these, an annotation like `ReadableStream<Uint8Array>` (where `ReadableStream`
|
|
37
|
+
// is the imported value) falls back to the ambient *global* ReadableStream and
|
|
38
|
+
// no longer matches the polyfill instances actually constructed — the runtime
|
|
39
|
+
// pick stays native-or-polyfill; only the static type is anchored to the
|
|
40
|
+
// (structurally faithful) polyfill instance type.
|
|
41
|
+
export type ReadableStreamImpl<R = unknown> = ReadableStreamPolyfill<R>;
|
|
42
|
+
export type WritableStreamImpl<W = unknown> = WritableStreamPolyfill<W>;
|
|
43
|
+
export type ByteLengthQueuingStrategyImpl = ByteLengthQueuingStrategyPolyfill;
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `createDeviceFixture` — the one-call fixture for testing serial code. It wires a
|
|
3
|
+
* {@link SimulatedDevice} simulator to an {@link InMemorySerialTransport} + `Serial`,
|
|
4
|
+
* and hands back everything a test needs to drive *both* sides:
|
|
5
|
+
*
|
|
6
|
+
* - `serial`/`port` — what the app-under-test consumes,
|
|
7
|
+
* - `simulatedDevice` (typed) + `device` handle — drive the device (inject data,
|
|
8
|
+
* move the GPS, inject faults),
|
|
9
|
+
* - `client` — a host-side {@link SerialClient} (for protocol tests), and
|
|
10
|
+
* - `whenOpened()/whenClosed()` — `await` the app connecting.
|
|
11
|
+
*
|
|
12
|
+
* @example Test how your app reacts to a device event
|
|
13
|
+
* const {simulatedDevice, device, whenOpened} = await createDeviceFixture(new MyGps());
|
|
14
|
+
* renderMyApp(); // opens the port itself
|
|
15
|
+
* await whenOpened();
|
|
16
|
+
* simulatedDevice.update({latitude: 51.48, longitude: 0});
|
|
17
|
+
* // …assert your app's UI updated
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import {setUsbSerial} from '../UsbSerial';
|
|
21
|
+
import type {SerialPort} from '../WebSerial';
|
|
22
|
+
import {Serial} from '../WebSerial';
|
|
23
|
+
import type {
|
|
24
|
+
DeviceOptions,
|
|
25
|
+
InMemorySerialTransportOptions,
|
|
26
|
+
} from './in-memory-serial-transport';
|
|
27
|
+
import {
|
|
28
|
+
type DeviceHandle,
|
|
29
|
+
InMemorySerialTransport,
|
|
30
|
+
} from './in-memory-serial-transport';
|
|
31
|
+
import {SerialClient} from './serial-client';
|
|
32
|
+
import type {
|
|
33
|
+
SimulatedDevice,
|
|
34
|
+
SimulatedDeviceOpenOptions,
|
|
35
|
+
} from './simulated-device';
|
|
36
|
+
|
|
37
|
+
export type DeviceFixtureOptions = {
|
|
38
|
+
/** Per-device transport knobs (single-device form). */
|
|
39
|
+
device?: DeviceOptions;
|
|
40
|
+
/** Per-device transport knobs, by index (multi-device form). */
|
|
41
|
+
devices?: DeviceOptions[];
|
|
42
|
+
/** Transport-level options (latencyMs, chunkSize, autoGrantPermission). */
|
|
43
|
+
transport?: InMemorySerialTransportOptions;
|
|
44
|
+
/** Whether each device is already permitted. Defaults to true (so it lists). */
|
|
45
|
+
hasPermission?: boolean;
|
|
46
|
+
/** Also `setUsbSerial(transport)` so a running app's `serial` sees it. Default false. */
|
|
47
|
+
installGlobally?: boolean;
|
|
48
|
+
/** Options for the host-side {@link SerialClient}. */
|
|
49
|
+
client?: {defaultTimeoutMs?: number};
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export type MountedDeviceFixture<D extends SimulatedDevice = SimulatedDevice> =
|
|
53
|
+
{
|
|
54
|
+
transport: InMemorySerialTransport;
|
|
55
|
+
serial: Serial;
|
|
56
|
+
port: SerialPort;
|
|
57
|
+
/** The transport-side handle: push/emitError/failNext/written/attach/detach/… */
|
|
58
|
+
device: DeviceHandle;
|
|
59
|
+
/** The concrete device simulator, typed (e.g. call `gps.update(...)`). */
|
|
60
|
+
simulatedDevice: D;
|
|
61
|
+
/** A host-side client bound to `port` — NOT opened (call `client.open()`). */
|
|
62
|
+
client: SerialClient;
|
|
63
|
+
/** Resolve when the app opens the port (now if already open). */
|
|
64
|
+
whenOpened(): Promise<SimulatedDeviceOpenOptions>;
|
|
65
|
+
/** Resolve when the app closes the port (now if not open). */
|
|
66
|
+
whenClosed(): Promise<void>;
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
export type MountedDeviceFixtures = {
|
|
70
|
+
transport: InMemorySerialTransport;
|
|
71
|
+
serial: Serial;
|
|
72
|
+
ports: SerialPort[];
|
|
73
|
+
devices: DeviceHandle[];
|
|
74
|
+
simulatedDevices: SimulatedDevice[];
|
|
75
|
+
clients: SerialClient[];
|
|
76
|
+
/** Resolve when device `index` opens, or any device when `index` is omitted. */
|
|
77
|
+
whenOpened(index?: number): Promise<SimulatedDeviceOpenOptions>;
|
|
78
|
+
/** Resolve when device `index` closes, or any device when `index` is omitted. */
|
|
79
|
+
whenClosed(index?: number): Promise<void>;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
export function createDeviceFixture<D extends SimulatedDevice>(
|
|
83
|
+
device: D,
|
|
84
|
+
options?: DeviceFixtureOptions,
|
|
85
|
+
): Promise<MountedDeviceFixture<D>>;
|
|
86
|
+
export function createDeviceFixture(
|
|
87
|
+
devices: SimulatedDevice[],
|
|
88
|
+
options?: DeviceFixtureOptions,
|
|
89
|
+
): Promise<MountedDeviceFixtures>;
|
|
90
|
+
export async function createDeviceFixture(
|
|
91
|
+
deviceOrDevices: SimulatedDevice | SimulatedDevice[],
|
|
92
|
+
options: DeviceFixtureOptions = {},
|
|
93
|
+
): Promise<MountedDeviceFixture | MountedDeviceFixtures> {
|
|
94
|
+
const list = Array.isArray(deviceOrDevices)
|
|
95
|
+
? deviceOrDevices
|
|
96
|
+
: [deviceOrDevices];
|
|
97
|
+
const hasPermission = options.hasPermission ?? true;
|
|
98
|
+
const transport = new InMemorySerialTransport(options.transport);
|
|
99
|
+
const handles = list.map((dev, i) =>
|
|
100
|
+
transport.addDevice(dev, {
|
|
101
|
+
hasPermission,
|
|
102
|
+
...(Array.isArray(deviceOrDevices)
|
|
103
|
+
? options.devices?.[i]
|
|
104
|
+
: options.device),
|
|
105
|
+
}),
|
|
106
|
+
);
|
|
107
|
+
const serial = new Serial(transport);
|
|
108
|
+
if (options.installGlobally) setUsbSerial(transport);
|
|
109
|
+
|
|
110
|
+
const ports = await serial.getPorts();
|
|
111
|
+
const clients = ports.map(
|
|
112
|
+
p =>
|
|
113
|
+
new SerialClient(p, {
|
|
114
|
+
defaultTimeoutMs: options.client?.defaultTimeoutMs,
|
|
115
|
+
}),
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
if (!Array.isArray(deviceOrDevices)) {
|
|
119
|
+
const handle = handles[0];
|
|
120
|
+
const port = ports[0];
|
|
121
|
+
if (!port) throw new Error('createDeviceFixture: device did not enumerate');
|
|
122
|
+
return {
|
|
123
|
+
transport,
|
|
124
|
+
serial,
|
|
125
|
+
port,
|
|
126
|
+
device: handle,
|
|
127
|
+
simulatedDevice: deviceOrDevices,
|
|
128
|
+
client: clients[0],
|
|
129
|
+
whenOpened: () => handle.whenOpened(),
|
|
130
|
+
whenClosed: () => handle.whenClosed(),
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return {
|
|
135
|
+
transport,
|
|
136
|
+
serial,
|
|
137
|
+
ports,
|
|
138
|
+
devices: handles,
|
|
139
|
+
simulatedDevices: list,
|
|
140
|
+
clients,
|
|
141
|
+
whenOpened: (index?: number) =>
|
|
142
|
+
index === undefined
|
|
143
|
+
? Promise.race(handles.map(h => h.whenOpened()))
|
|
144
|
+
: handles[index].whenOpened(),
|
|
145
|
+
whenClosed: (index?: number) =>
|
|
146
|
+
index === undefined
|
|
147
|
+
? Promise.race(handles.map(h => h.whenClosed()))
|
|
148
|
+
: handles[index].whenClosed(),
|
|
149
|
+
};
|
|
150
|
+
}
|