@gurezo/web-serial-rxjs 0.2.0 → 2.0.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.ja.md +82 -15
- package/README.md +82 -15
- package/dist/errors/serial-error-code.d.ts +7 -0
- package/dist/errors/serial-error-code.d.ts.map +1 -1
- package/dist/index.d.ts +31 -32
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +459 -673
- package/dist/index.mjs +459 -673
- package/dist/index.mjs.map +4 -4
- package/dist/session/create-serial-session.d.ts +49 -0
- package/dist/session/create-serial-session.d.ts.map +1 -0
- package/dist/session/index.d.ts +5 -0
- package/dist/session/index.d.ts.map +1 -0
- package/dist/session/internal/build-request-options.d.ts +18 -0
- package/dist/session/internal/build-request-options.d.ts.map +1 -0
- package/dist/session/internal/has-web-serial-support.d.ts +12 -0
- package/dist/session/internal/has-web-serial-support.d.ts.map +1 -0
- package/dist/session/internal/line-buffer.d.ts +14 -0
- package/dist/session/internal/line-buffer.d.ts.map +1 -0
- package/dist/session/normalize-serial-error.d.ts +55 -0
- package/dist/session/normalize-serial-error.d.ts.map +1 -0
- package/dist/session/read-pump.d.ts +74 -0
- package/dist/session/read-pump.d.ts.map +1 -0
- package/dist/session/send-queue.d.ts +58 -0
- package/dist/session/send-queue.d.ts.map +1 -0
- package/dist/session/serial-session-options.d.ts +80 -0
- package/dist/session/serial-session-options.d.ts.map +1 -0
- package/dist/session/serial-session-state.d.ts +35 -0
- package/dist/session/serial-session-state.d.ts.map +1 -0
- package/dist/session/serial-session.d.ts +143 -0
- package/dist/session/serial-session.d.ts.map +1 -0
- package/dist/session/session-state-machine.d.ts +39 -0
- package/dist/session/session-state-machine.d.ts.map +1 -0
- package/package.json +1 -5
- package/dist/browser/browser-detection.d.ts +0 -104
- package/dist/browser/browser-detection.d.ts.map +0 -1
- package/dist/browser/browser-support.d.ts +0 -57
- package/dist/browser/browser-support.d.ts.map +0 -1
- package/dist/client/index.d.ts +0 -283
- package/dist/client/index.d.ts.map +0 -1
- package/dist/client/serial-client.d.ts +0 -137
- package/dist/client/serial-client.d.ts.map +0 -1
- package/dist/filters/build-request-options.d.ts +0 -42
- package/dist/filters/build-request-options.d.ts.map +0 -1
- package/dist/io/observable-to-writable.d.ts +0 -65
- package/dist/io/observable-to-writable.d.ts.map +0 -1
- package/dist/io/readable-to-observable.d.ts +0 -44
- package/dist/io/readable-to-observable.d.ts.map +0 -1
- package/dist/lib/web-serial-rxjs.d.ts +0 -7
- package/dist/lib/web-serial-rxjs.d.ts.map +0 -1
- package/dist/shell/create-shell-client.d.ts +0 -17
- package/dist/shell/create-shell-client.d.ts.map +0 -1
- package/dist/shell/index.d.ts +0 -2
- package/dist/shell/index.d.ts.map +0 -1
- package/dist/types/options.d.ts +0 -107
- package/dist/types/options.d.ts.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,13 +1,5 @@
|
|
|
1
|
-
// packages/web-serial-rxjs/src/
|
|
2
|
-
import {
|
|
3
|
-
BehaviorSubject,
|
|
4
|
-
Observable as Observable2,
|
|
5
|
-
Subject,
|
|
6
|
-
defer,
|
|
7
|
-
map,
|
|
8
|
-
share,
|
|
9
|
-
switchMap
|
|
10
|
-
} from "rxjs";
|
|
1
|
+
// packages/web-serial-rxjs/src/session/create-serial-session.ts
|
|
2
|
+
import { distinctUntilChanged, map, Observable as Observable3, Subject } from "rxjs";
|
|
11
3
|
|
|
12
4
|
// packages/web-serial-rxjs/src/errors/serial-error-code.ts
|
|
13
5
|
var SerialErrorCode = /* @__PURE__ */ ((SerialErrorCode2) => {
|
|
@@ -21,6 +13,7 @@ var SerialErrorCode = /* @__PURE__ */ ((SerialErrorCode2) => {
|
|
|
21
13
|
SerialErrorCode2["CONNECTION_LOST"] = "CONNECTION_LOST";
|
|
22
14
|
SerialErrorCode2["INVALID_FILTER_OPTIONS"] = "INVALID_FILTER_OPTIONS";
|
|
23
15
|
SerialErrorCode2["OPERATION_CANCELLED"] = "OPERATION_CANCELLED";
|
|
16
|
+
SerialErrorCode2["OPERATION_TIMEOUT"] = "OPERATION_TIMEOUT";
|
|
24
17
|
SerialErrorCode2["UNKNOWN"] = "UNKNOWN";
|
|
25
18
|
return SerialErrorCode2;
|
|
26
19
|
})(SerialErrorCode || {});
|
|
@@ -64,54 +57,7 @@ var SerialError = class _SerialError extends Error {
|
|
|
64
57
|
}
|
|
65
58
|
};
|
|
66
59
|
|
|
67
|
-
// packages/web-serial-rxjs/src/
|
|
68
|
-
var BrowserType = /* @__PURE__ */ ((BrowserType2) => {
|
|
69
|
-
BrowserType2["CHROME"] = "chrome";
|
|
70
|
-
BrowserType2["EDGE"] = "edge";
|
|
71
|
-
BrowserType2["OPERA"] = "opera";
|
|
72
|
-
BrowserType2["UNKNOWN"] = "unknown";
|
|
73
|
-
return BrowserType2;
|
|
74
|
-
})(BrowserType || {});
|
|
75
|
-
function hasWebSerialSupport() {
|
|
76
|
-
return typeof navigator !== "undefined" && "serial" in navigator && navigator.serial !== void 0 && navigator.serial !== null;
|
|
77
|
-
}
|
|
78
|
-
function detectBrowserType() {
|
|
79
|
-
if (typeof navigator === "undefined" || !navigator.userAgent) {
|
|
80
|
-
return "unknown" /* UNKNOWN */;
|
|
81
|
-
}
|
|
82
|
-
const ua = navigator.userAgent.toLowerCase();
|
|
83
|
-
if (ua.includes("edg/")) {
|
|
84
|
-
return "edge" /* EDGE */;
|
|
85
|
-
}
|
|
86
|
-
if (ua.includes("opr/") || ua.includes("opera/")) {
|
|
87
|
-
return "opera" /* OPERA */;
|
|
88
|
-
}
|
|
89
|
-
if (ua.includes("chrome/")) {
|
|
90
|
-
return "chrome" /* CHROME */;
|
|
91
|
-
}
|
|
92
|
-
return "unknown" /* UNKNOWN */;
|
|
93
|
-
}
|
|
94
|
-
function isChromiumBased() {
|
|
95
|
-
const browserType = detectBrowserType();
|
|
96
|
-
return browserType === "chrome" /* CHROME */ || browserType === "edge" /* EDGE */ || browserType === "opera" /* OPERA */;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// packages/web-serial-rxjs/src/browser/browser-support.ts
|
|
100
|
-
function checkBrowserSupport() {
|
|
101
|
-
if (!hasWebSerialSupport()) {
|
|
102
|
-
const browserType = detectBrowserType();
|
|
103
|
-
const browserName = browserType === "unknown" /* UNKNOWN */ ? "your browser" : browserType.toUpperCase();
|
|
104
|
-
throw new SerialError(
|
|
105
|
-
"BROWSER_NOT_SUPPORTED" /* BROWSER_NOT_SUPPORTED */,
|
|
106
|
-
`Web Serial API is not supported in ${browserName}. Please use a Chromium-based browser (Chrome, Edge, or Opera).`
|
|
107
|
-
);
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
function isBrowserSupported() {
|
|
111
|
-
return hasWebSerialSupport();
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
// packages/web-serial-rxjs/src/filters/build-request-options.ts
|
|
60
|
+
// packages/web-serial-rxjs/src/session/internal/build-request-options.ts
|
|
115
61
|
function buildRequestOptions(options) {
|
|
116
62
|
if (!options || !options.filters || options.filters.length === 0) {
|
|
117
63
|
return void 0;
|
|
@@ -145,165 +91,204 @@ function buildRequestOptions(options) {
|
|
|
145
91
|
};
|
|
146
92
|
}
|
|
147
93
|
|
|
148
|
-
// packages/web-serial-rxjs/src/
|
|
149
|
-
function
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
94
|
+
// packages/web-serial-rxjs/src/session/internal/has-web-serial-support.ts
|
|
95
|
+
function hasWebSerialSupport() {
|
|
96
|
+
return typeof navigator !== "undefined" && "serial" in navigator && navigator.serial !== void 0 && navigator.serial !== null;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// packages/web-serial-rxjs/src/session/internal/line-buffer.ts
|
|
100
|
+
function createLineBuffer() {
|
|
101
|
+
let buffer = "";
|
|
102
|
+
const clear = () => {
|
|
103
|
+
buffer = "";
|
|
104
|
+
};
|
|
105
|
+
const feed = (chunk) => {
|
|
106
|
+
buffer += chunk;
|
|
107
|
+
const out = [];
|
|
108
|
+
for (; ; ) {
|
|
109
|
+
const crlf = buffer.indexOf("\r\n");
|
|
110
|
+
if (crlf >= 0) {
|
|
111
|
+
out.push(buffer.slice(0, crlf));
|
|
112
|
+
buffer = buffer.slice(crlf + 2);
|
|
113
|
+
continue;
|
|
157
114
|
}
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
await writer.write(chunk);
|
|
164
|
-
} catch (error) {
|
|
165
|
-
subscription?.unsubscribe();
|
|
166
|
-
if (writer) {
|
|
167
|
-
writer.releaseLock();
|
|
168
|
-
}
|
|
169
|
-
throw error;
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
},
|
|
173
|
-
error: async (error) => {
|
|
174
|
-
if (writer) {
|
|
175
|
-
try {
|
|
176
|
-
await writer.abort(error);
|
|
177
|
-
} catch {
|
|
178
|
-
} finally {
|
|
179
|
-
writer.releaseLock();
|
|
180
|
-
writer = null;
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
},
|
|
184
|
-
complete: async () => {
|
|
185
|
-
if (writer) {
|
|
186
|
-
try {
|
|
187
|
-
await writer.close();
|
|
188
|
-
} catch {
|
|
189
|
-
} finally {
|
|
190
|
-
writer.releaseLock();
|
|
191
|
-
writer = null;
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
});
|
|
196
|
-
},
|
|
197
|
-
abort(reason) {
|
|
198
|
-
if (subscription) {
|
|
199
|
-
subscription.unsubscribe();
|
|
200
|
-
subscription = null;
|
|
115
|
+
const cr = buffer.indexOf("\r");
|
|
116
|
+
if (cr >= 0 && cr + 1 < buffer.length && buffer[cr + 1] !== "\n") {
|
|
117
|
+
out.push(buffer.slice(0, cr));
|
|
118
|
+
buffer = buffer.slice(cr + 1);
|
|
119
|
+
continue;
|
|
201
120
|
}
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
121
|
+
const nl = buffer.indexOf("\n");
|
|
122
|
+
if (nl >= 0) {
|
|
123
|
+
out.push(buffer.slice(0, nl));
|
|
124
|
+
buffer = buffer.slice(nl + 1);
|
|
125
|
+
continue;
|
|
207
126
|
}
|
|
127
|
+
if (cr >= 0 && cr + 1 === buffer.length) {
|
|
128
|
+
break;
|
|
129
|
+
}
|
|
130
|
+
break;
|
|
208
131
|
}
|
|
209
|
-
|
|
210
|
-
|
|
132
|
+
return out;
|
|
133
|
+
};
|
|
134
|
+
return { feed, clear };
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// packages/web-serial-rxjs/src/session/normalize-serial-error.ts
|
|
138
|
+
var DEFAULT_MESSAGE_PREFIX = "Serial operation failed";
|
|
139
|
+
var isDomExceptionWithName = (error, name) => typeof DOMException !== "undefined" && error instanceof DOMException && error.name === name;
|
|
140
|
+
function normalizeSerialError(error, options) {
|
|
141
|
+
if (error instanceof SerialError) {
|
|
142
|
+
return error;
|
|
143
|
+
}
|
|
144
|
+
const prefix = options.messagePrefix ?? DEFAULT_MESSAGE_PREFIX;
|
|
145
|
+
if (isDomExceptionWithName(error, "NotFoundError")) {
|
|
146
|
+
return new SerialError(
|
|
147
|
+
"OPERATION_CANCELLED" /* OPERATION_CANCELLED */,
|
|
148
|
+
"Port selection was cancelled by the user",
|
|
149
|
+
error
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
const cause = error instanceof Error ? error : new Error(String(error));
|
|
153
|
+
return new SerialError(
|
|
154
|
+
options.fallbackCode,
|
|
155
|
+
`${prefix}: ${cause.message}`,
|
|
156
|
+
cause
|
|
157
|
+
);
|
|
211
158
|
}
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
159
|
+
|
|
160
|
+
// packages/web-serial-rxjs/src/session/read-pump.ts
|
|
161
|
+
function createReadPump(port, { onChunk, onError }) {
|
|
162
|
+
let reader = null;
|
|
163
|
+
let running = false;
|
|
164
|
+
let stopped = false;
|
|
165
|
+
const decoder = new TextDecoder(void 0, { fatal: false });
|
|
166
|
+
const releaseReader = () => {
|
|
167
|
+
if (!reader) {
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
215
170
|
try {
|
|
216
|
-
|
|
171
|
+
reader.releaseLock();
|
|
217
172
|
} catch {
|
|
173
|
+
}
|
|
174
|
+
reader = null;
|
|
175
|
+
};
|
|
176
|
+
const pump = async (stream) => {
|
|
177
|
+
reader = stream.getReader();
|
|
178
|
+
running = true;
|
|
179
|
+
try {
|
|
180
|
+
while (!stopped) {
|
|
181
|
+
const { done, value } = await reader.read();
|
|
182
|
+
if (done) {
|
|
183
|
+
break;
|
|
184
|
+
}
|
|
185
|
+
if (value && value.byteLength > 0) {
|
|
186
|
+
const text = decoder.decode(value, { stream: true });
|
|
187
|
+
if (text.length > 0) {
|
|
188
|
+
onChunk(text);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
if (!stopped) {
|
|
193
|
+
const tail = decoder.decode();
|
|
194
|
+
if (tail.length > 0) {
|
|
195
|
+
onChunk(tail);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
} catch (error) {
|
|
199
|
+
if (!stopped) {
|
|
200
|
+
onError(
|
|
201
|
+
normalizeSerialError(error, {
|
|
202
|
+
fallbackCode: "READ_FAILED" /* READ_FAILED */,
|
|
203
|
+
messagePrefix: "Read pump failed"
|
|
204
|
+
})
|
|
205
|
+
);
|
|
206
|
+
}
|
|
218
207
|
} finally {
|
|
219
|
-
|
|
208
|
+
running = false;
|
|
209
|
+
releaseReader();
|
|
220
210
|
}
|
|
221
211
|
};
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
212
|
+
return {
|
|
213
|
+
start() {
|
|
214
|
+
if (running || stopped) {
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
const stream = port.readable;
|
|
218
|
+
if (!stream) {
|
|
219
|
+
stopped = true;
|
|
220
|
+
onError(
|
|
221
|
+
new SerialError(
|
|
222
|
+
"CONNECTION_LOST" /* CONNECTION_LOST */,
|
|
223
|
+
"Read pump failed: port.readable is not available"
|
|
224
|
+
)
|
|
233
225
|
);
|
|
234
|
-
|
|
226
|
+
return;
|
|
235
227
|
}
|
|
228
|
+
void pump(stream);
|
|
236
229
|
},
|
|
237
|
-
|
|
238
|
-
|
|
230
|
+
async stop() {
|
|
231
|
+
if (stopped) {
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
stopped = true;
|
|
235
|
+
if (!reader) {
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
239
238
|
try {
|
|
240
|
-
await
|
|
239
|
+
await reader.cancel();
|
|
241
240
|
} catch {
|
|
242
241
|
} finally {
|
|
243
|
-
|
|
242
|
+
releaseReader();
|
|
244
243
|
}
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
unsubscribe: () => {
|
|
249
|
-
subscription.unsubscribe();
|
|
250
|
-
writer.releaseLock();
|
|
244
|
+
},
|
|
245
|
+
get isRunning() {
|
|
246
|
+
return running;
|
|
251
247
|
}
|
|
252
248
|
};
|
|
253
249
|
}
|
|
254
250
|
|
|
255
|
-
// packages/web-serial-rxjs/src/
|
|
256
|
-
import { Observable } from "rxjs";
|
|
257
|
-
function
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
)
|
|
280
|
-
|
|
281
|
-
} else {
|
|
282
|
-
subscriber.error(
|
|
283
|
-
new SerialError(
|
|
284
|
-
"READ_FAILED" /* READ_FAILED */,
|
|
285
|
-
"Failed to read from stream: Unknown error",
|
|
286
|
-
error
|
|
287
|
-
)
|
|
251
|
+
// packages/web-serial-rxjs/src/session/send-queue.ts
|
|
252
|
+
import { Observable, defer } from "rxjs";
|
|
253
|
+
function createSendQueue() {
|
|
254
|
+
let chain = Promise.resolve();
|
|
255
|
+
return {
|
|
256
|
+
enqueue(operation) {
|
|
257
|
+
return defer(
|
|
258
|
+
() => new Observable((subscriber) => {
|
|
259
|
+
let cancelled = false;
|
|
260
|
+
const run = async () => {
|
|
261
|
+
try {
|
|
262
|
+
const value = await operation();
|
|
263
|
+
if (!cancelled) {
|
|
264
|
+
subscriber.next(value);
|
|
265
|
+
subscriber.complete();
|
|
266
|
+
}
|
|
267
|
+
} catch (error) {
|
|
268
|
+
if (!cancelled) {
|
|
269
|
+
subscriber.error(error);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
};
|
|
273
|
+
const scheduled = chain.then(run, run);
|
|
274
|
+
chain = scheduled.then(
|
|
275
|
+
() => void 0,
|
|
276
|
+
() => void 0
|
|
288
277
|
);
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
return () => {
|
|
300
|
-
reader.releaseLock();
|
|
301
|
-
};
|
|
302
|
-
});
|
|
278
|
+
return () => {
|
|
279
|
+
cancelled = true;
|
|
280
|
+
};
|
|
281
|
+
})
|
|
282
|
+
);
|
|
283
|
+
},
|
|
284
|
+
clear() {
|
|
285
|
+
chain = Promise.resolve();
|
|
286
|
+
}
|
|
287
|
+
};
|
|
303
288
|
}
|
|
304
289
|
|
|
305
|
-
// packages/web-serial-rxjs/src/
|
|
306
|
-
var
|
|
290
|
+
// packages/web-serial-rxjs/src/session/serial-session-options.ts
|
|
291
|
+
var DEFAULT_SERIAL_SESSION_OPTIONS = {
|
|
307
292
|
baudRate: 9600,
|
|
308
293
|
dataBits: 8,
|
|
309
294
|
stopBits: 1,
|
|
@@ -313,517 +298,318 @@ var DEFAULT_SERIAL_CLIENT_OPTIONS = {
|
|
|
313
298
|
filters: void 0
|
|
314
299
|
};
|
|
315
300
|
|
|
316
|
-
// packages/web-serial-rxjs/src/
|
|
317
|
-
var
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
this.
|
|
340
|
-
/** @internal */
|
|
341
|
-
this.connectedState$ = new BehaviorSubject(false);
|
|
342
|
-
/** @internal */
|
|
343
|
-
this.connectionEventsSubject$ = new Subject();
|
|
344
|
-
checkBrowserSupport();
|
|
345
|
-
this.options = {
|
|
346
|
-
...DEFAULT_SERIAL_CLIENT_OPTIONS,
|
|
347
|
-
...options,
|
|
348
|
-
filters: options?.filters
|
|
349
|
-
};
|
|
301
|
+
// packages/web-serial-rxjs/src/session/serial-session-state.ts
|
|
302
|
+
var SerialSessionState = {
|
|
303
|
+
Idle: "idle",
|
|
304
|
+
Connecting: "connecting",
|
|
305
|
+
Connected: "connected",
|
|
306
|
+
Disconnecting: "disconnecting",
|
|
307
|
+
Unsupported: "unsupported",
|
|
308
|
+
Error: "error"
|
|
309
|
+
};
|
|
310
|
+
|
|
311
|
+
// packages/web-serial-rxjs/src/session/session-state-machine.ts
|
|
312
|
+
import { BehaviorSubject } from "rxjs";
|
|
313
|
+
var S = SerialSessionState;
|
|
314
|
+
var ALLOWED_TRANSITIONS = {
|
|
315
|
+
[S.Idle]: [S.Connecting, S.Error],
|
|
316
|
+
[S.Connecting]: [S.Connected, S.Error, S.Idle],
|
|
317
|
+
[S.Connected]: [S.Disconnecting, S.Error],
|
|
318
|
+
[S.Disconnecting]: [S.Idle, S.Error],
|
|
319
|
+
[S.Error]: [S.Idle, S.Connecting],
|
|
320
|
+
[S.Unsupported]: []
|
|
321
|
+
};
|
|
322
|
+
var SessionStateMachine = class {
|
|
323
|
+
constructor(initial = SerialSessionState.Idle) {
|
|
324
|
+
this.subject = new BehaviorSubject(initial);
|
|
350
325
|
}
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
*
|
|
354
|
-
* @returns Observable that emits the selected SerialPort
|
|
355
|
-
* @internal
|
|
356
|
-
*/
|
|
357
|
-
requestPort() {
|
|
358
|
-
return defer(() => {
|
|
359
|
-
checkBrowserSupport();
|
|
360
|
-
return navigator.serial.requestPort(buildRequestOptions(this.options)).catch((error) => {
|
|
361
|
-
if (error instanceof DOMException && error.name === "NotFoundError") {
|
|
362
|
-
throw new SerialError(
|
|
363
|
-
"OPERATION_CANCELLED" /* OPERATION_CANCELLED */,
|
|
364
|
-
"Port selection was cancelled by the user",
|
|
365
|
-
error
|
|
366
|
-
);
|
|
367
|
-
}
|
|
368
|
-
throw new SerialError(
|
|
369
|
-
"PORT_NOT_AVAILABLE" /* PORT_NOT_AVAILABLE */,
|
|
370
|
-
`Failed to request port: ${error instanceof Error ? error.message : String(error)}`,
|
|
371
|
-
error instanceof Error ? error : new Error(String(error))
|
|
372
|
-
);
|
|
373
|
-
});
|
|
374
|
-
});
|
|
326
|
+
get current() {
|
|
327
|
+
return this.subject.getValue();
|
|
375
328
|
}
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
*
|
|
379
|
-
* @returns Observable that emits an array of available SerialPorts
|
|
380
|
-
* @internal
|
|
381
|
-
*/
|
|
382
|
-
getPorts() {
|
|
383
|
-
return defer(() => {
|
|
384
|
-
checkBrowserSupport();
|
|
385
|
-
return navigator.serial.getPorts().catch((error) => {
|
|
386
|
-
throw new SerialError(
|
|
387
|
-
"PORT_NOT_AVAILABLE" /* PORT_NOT_AVAILABLE */,
|
|
388
|
-
`Failed to get ports: ${error instanceof Error ? error.message : String(error)}`,
|
|
389
|
-
error instanceof Error ? error : new Error(String(error))
|
|
390
|
-
);
|
|
391
|
-
});
|
|
392
|
-
});
|
|
329
|
+
get state$() {
|
|
330
|
+
return this.subject.asObservable();
|
|
393
331
|
}
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
*
|
|
397
|
-
* @param port - Optional SerialPort to connect to. If not provided, will request one.
|
|
398
|
-
* @returns Observable that completes when the port is opened
|
|
399
|
-
* @internal
|
|
400
|
-
*/
|
|
401
|
-
connect(port) {
|
|
402
|
-
checkBrowserSupport();
|
|
403
|
-
if (this.isOpen) {
|
|
404
|
-
return new Observable2((subscriber) => {
|
|
405
|
-
subscriber.error(
|
|
406
|
-
new SerialError(
|
|
407
|
-
"PORT_ALREADY_OPEN" /* PORT_ALREADY_OPEN */,
|
|
408
|
-
"Port is already open"
|
|
409
|
-
)
|
|
410
|
-
);
|
|
411
|
-
});
|
|
412
|
-
}
|
|
413
|
-
const port$ = port ? new Observable2((subscriber) => {
|
|
414
|
-
subscriber.next(port);
|
|
415
|
-
subscriber.complete();
|
|
416
|
-
}) : this.requestPort();
|
|
417
|
-
return port$.pipe(
|
|
418
|
-
switchMap((selectedPort) => {
|
|
419
|
-
return defer(() => {
|
|
420
|
-
this.port = selectedPort;
|
|
421
|
-
return this.port.open({
|
|
422
|
-
baudRate: this.options.baudRate,
|
|
423
|
-
dataBits: this.options.dataBits,
|
|
424
|
-
stopBits: this.options.stopBits,
|
|
425
|
-
parity: this.options.parity,
|
|
426
|
-
bufferSize: this.options.bufferSize,
|
|
427
|
-
flowControl: this.options.flowControl
|
|
428
|
-
}).then(() => {
|
|
429
|
-
this.isOpen = true;
|
|
430
|
-
this.connectedState$.next(true);
|
|
431
|
-
this.connectionEventsSubject$.next("connected");
|
|
432
|
-
}).catch((error) => {
|
|
433
|
-
this.port = null;
|
|
434
|
-
this.isOpen = false;
|
|
435
|
-
if (error instanceof SerialError) {
|
|
436
|
-
throw error;
|
|
437
|
-
}
|
|
438
|
-
throw new SerialError(
|
|
439
|
-
"PORT_OPEN_FAILED" /* PORT_OPEN_FAILED */,
|
|
440
|
-
`Failed to open port: ${error instanceof Error ? error.message : String(error)}`,
|
|
441
|
-
error instanceof Error ? error : new Error(String(error))
|
|
442
|
-
);
|
|
443
|
-
});
|
|
444
|
-
});
|
|
445
|
-
})
|
|
446
|
-
);
|
|
332
|
+
toConnecting() {
|
|
333
|
+
return this.transition(S.Connecting);
|
|
447
334
|
}
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
*
|
|
451
|
-
* @returns Observable that completes when the port is closed
|
|
452
|
-
* @internal
|
|
453
|
-
*/
|
|
454
|
-
disconnect() {
|
|
455
|
-
return defer(() => {
|
|
456
|
-
if (!this.isOpen || !this.port) {
|
|
457
|
-
return Promise.resolve();
|
|
458
|
-
}
|
|
459
|
-
if (this.readSubscription) {
|
|
460
|
-
this.readSubscription.unsubscribe();
|
|
461
|
-
this.readSubscription = null;
|
|
462
|
-
}
|
|
463
|
-
this.sharedReadStream$ = null;
|
|
464
|
-
this.textDecoder = new TextDecoder();
|
|
465
|
-
if (this.writeSubscription) {
|
|
466
|
-
this.writeSubscription.unsubscribe();
|
|
467
|
-
this.writeSubscription = null;
|
|
468
|
-
}
|
|
469
|
-
return this.port.close().then(() => {
|
|
470
|
-
this.port = null;
|
|
471
|
-
this.isOpen = false;
|
|
472
|
-
this.connectedState$.next(false);
|
|
473
|
-
this.connectionEventsSubject$.next("disconnected");
|
|
474
|
-
}).catch((error) => {
|
|
475
|
-
this.port = null;
|
|
476
|
-
this.isOpen = false;
|
|
477
|
-
this.connectedState$.next(false);
|
|
478
|
-
this.connectionEventsSubject$.next("disconnected");
|
|
479
|
-
throw new SerialError(
|
|
480
|
-
"CONNECTION_LOST" /* CONNECTION_LOST */,
|
|
481
|
-
`Failed to close port: ${error instanceof Error ? error.message : String(error)}`,
|
|
482
|
-
error instanceof Error ? error : new Error(String(error))
|
|
483
|
-
);
|
|
484
|
-
});
|
|
485
|
-
});
|
|
335
|
+
toConnected() {
|
|
336
|
+
return this.transition(S.Connected);
|
|
486
337
|
}
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
338
|
+
toDisconnecting() {
|
|
339
|
+
return this.transition(S.Disconnecting);
|
|
340
|
+
}
|
|
341
|
+
toIdle() {
|
|
342
|
+
return this.transition(S.Idle);
|
|
343
|
+
}
|
|
344
|
+
toError() {
|
|
345
|
+
return this.transition(S.Error);
|
|
346
|
+
}
|
|
347
|
+
toUnsupported() {
|
|
348
|
+
return this.transition(S.Unsupported);
|
|
349
|
+
}
|
|
350
|
+
complete() {
|
|
351
|
+
this.subject.complete();
|
|
352
|
+
}
|
|
353
|
+
transition(next) {
|
|
354
|
+
const current = this.subject.getValue();
|
|
355
|
+
if (current === next) {
|
|
356
|
+
return false;
|
|
499
357
|
}
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
358
|
+
const allowed = ALLOWED_TRANSITIONS[current];
|
|
359
|
+
if (!allowed.includes(next)) {
|
|
360
|
+
if (typeof console !== "undefined" && console.warn) {
|
|
361
|
+
console.warn(
|
|
362
|
+
`[web-serial-rxjs] Ignoring invalid SerialSession transition ${current} -> ${next}`
|
|
363
|
+
);
|
|
364
|
+
}
|
|
365
|
+
return false;
|
|
508
366
|
}
|
|
509
|
-
|
|
367
|
+
this.subject.next(next);
|
|
368
|
+
return true;
|
|
510
369
|
}
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
370
|
+
};
|
|
371
|
+
|
|
372
|
+
// packages/web-serial-rxjs/src/session/create-serial-session.ts
|
|
373
|
+
function createSerialSession(options) {
|
|
374
|
+
const resolvedOptions = {
|
|
375
|
+
...DEFAULT_SERIAL_SESSION_OPTIONS,
|
|
376
|
+
...options,
|
|
377
|
+
filters: options?.filters
|
|
378
|
+
};
|
|
379
|
+
const supported = hasWebSerialSupport();
|
|
380
|
+
const machine = new SessionStateMachine(
|
|
381
|
+
supported ? SerialSessionState.Idle : SerialSessionState.Unsupported
|
|
382
|
+
);
|
|
383
|
+
const errorsSubject = new Subject();
|
|
384
|
+
const receiveSubject = new Subject();
|
|
385
|
+
const linesSubject = new Subject();
|
|
386
|
+
const sendQueue = createSendQueue();
|
|
387
|
+
const textEncoder = new TextEncoder();
|
|
388
|
+
const lineBuffer = createLineBuffer();
|
|
389
|
+
const errors$ = errorsSubject.asObservable();
|
|
390
|
+
const receive$ = receiveSubject.asObservable();
|
|
391
|
+
const lines$ = linesSubject.asObservable();
|
|
392
|
+
const isConnected$ = machine.state$.pipe(
|
|
393
|
+
map((state) => state === SerialSessionState.Connected),
|
|
394
|
+
distinctUntilChanged()
|
|
395
|
+
);
|
|
396
|
+
let activePort = null;
|
|
397
|
+
let activePump = null;
|
|
398
|
+
const teardownPump = async () => {
|
|
399
|
+
const pump = activePump;
|
|
400
|
+
activePump = null;
|
|
401
|
+
lineBuffer.clear();
|
|
402
|
+
if (pump) {
|
|
403
|
+
await pump.stop();
|
|
404
|
+
}
|
|
405
|
+
};
|
|
406
|
+
const closePortSafely = async (port) => {
|
|
407
|
+
if (!port) {
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
410
|
+
try {
|
|
411
|
+
await port.close();
|
|
412
|
+
} catch {
|
|
413
|
+
}
|
|
414
|
+
};
|
|
415
|
+
const reportError = (error, severity, options2) => {
|
|
416
|
+
const serialError = normalizeSerialError(error, options2);
|
|
417
|
+
errorsSubject.next(serialError);
|
|
418
|
+
if (severity === "fatal") {
|
|
419
|
+
machine.toError();
|
|
420
|
+
sendQueue.clear();
|
|
421
|
+
const portToClose = activePort;
|
|
422
|
+
activePort = null;
|
|
423
|
+
void teardownPump().then(() => closePortSafely(portToClose));
|
|
424
|
+
}
|
|
425
|
+
return serialError;
|
|
426
|
+
};
|
|
427
|
+
const writeToPort = async (payload) => {
|
|
428
|
+
const port = activePort;
|
|
429
|
+
if (machine.current !== SerialSessionState.Connected || !port || !port.writable) {
|
|
531
430
|
throw new SerialError(
|
|
532
431
|
"PORT_NOT_OPEN" /* PORT_NOT_OPEN */,
|
|
533
|
-
"
|
|
432
|
+
"Cannot send data while session is not connected"
|
|
534
433
|
);
|
|
535
434
|
}
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
new SerialError(
|
|
544
|
-
"WRITE_FAILED" /* WRITE_FAILED */,
|
|
545
|
-
"Write subscription is not available"
|
|
546
|
-
)
|
|
547
|
-
);
|
|
548
|
-
return;
|
|
435
|
+
const writer = port.writable.getWriter();
|
|
436
|
+
try {
|
|
437
|
+
await writer.write(payload);
|
|
438
|
+
} finally {
|
|
439
|
+
try {
|
|
440
|
+
writer.releaseLock();
|
|
441
|
+
} catch {
|
|
549
442
|
}
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
this.writeSubscription.unsubscribe();
|
|
568
|
-
this.writeSubscription = null;
|
|
569
|
-
}
|
|
443
|
+
}
|
|
444
|
+
};
|
|
445
|
+
return {
|
|
446
|
+
isBrowserSupported() {
|
|
447
|
+
return hasWebSerialSupport();
|
|
448
|
+
},
|
|
449
|
+
connect$() {
|
|
450
|
+
return new Observable3((subscriber) => {
|
|
451
|
+
if (!hasWebSerialSupport()) {
|
|
452
|
+
const error = reportError(
|
|
453
|
+
new SerialError(
|
|
454
|
+
"BROWSER_NOT_SUPPORTED" /* BROWSER_NOT_SUPPORTED */,
|
|
455
|
+
"Web Serial API is not supported in this environment"
|
|
456
|
+
),
|
|
457
|
+
"non-fatal",
|
|
458
|
+
{ fallbackCode: "BROWSER_NOT_SUPPORTED" /* BROWSER_NOT_SUPPORTED */ }
|
|
459
|
+
);
|
|
570
460
|
subscriber.error(error);
|
|
461
|
+
return;
|
|
571
462
|
}
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
if (!this.isOpen || !this.port || !this.port.writable) {
|
|
585
|
-
throw new SerialError(
|
|
586
|
-
"PORT_NOT_OPEN" /* PORT_NOT_OPEN */,
|
|
587
|
-
"Port is not open or writable stream is not available"
|
|
588
|
-
);
|
|
589
|
-
}
|
|
590
|
-
const writer = this.port.writable.getWriter();
|
|
591
|
-
return writer.write(data).then(() => {
|
|
592
|
-
writer.releaseLock();
|
|
593
|
-
}).catch((error) => {
|
|
594
|
-
writer.releaseLock();
|
|
595
|
-
throw new SerialError(
|
|
596
|
-
"WRITE_FAILED" /* WRITE_FAILED */,
|
|
597
|
-
`Failed to write data: ${error instanceof Error ? error.message : String(error)}`,
|
|
598
|
-
error instanceof Error ? error : new Error(String(error))
|
|
599
|
-
);
|
|
600
|
-
});
|
|
601
|
-
});
|
|
602
|
-
}
|
|
603
|
-
/**
|
|
604
|
-
* Write text data to the serial port.
|
|
605
|
-
*
|
|
606
|
-
* @param data - Text data to write
|
|
607
|
-
* @returns Observable that completes when the data is written
|
|
608
|
-
* @internal
|
|
609
|
-
*/
|
|
610
|
-
writeText(data) {
|
|
611
|
-
return this.write(this.textEncoder.encode(data));
|
|
612
|
-
}
|
|
613
|
-
/**
|
|
614
|
-
* Check if the port is currently open.
|
|
615
|
-
*
|
|
616
|
-
* @returns `true` if a port is currently open, `false` otherwise
|
|
617
|
-
* @internal
|
|
618
|
-
*/
|
|
619
|
-
get connected() {
|
|
620
|
-
return this.isOpen;
|
|
621
|
-
}
|
|
622
|
-
/**
|
|
623
|
-
* Get an Observable that emits connection state changes.
|
|
624
|
-
*
|
|
625
|
-
* @returns Observable that emits `true` when connected and `false` when disconnected
|
|
626
|
-
* @internal
|
|
627
|
-
*/
|
|
628
|
-
get connected$() {
|
|
629
|
-
return this.connectedState$.asObservable();
|
|
630
|
-
}
|
|
631
|
-
/**
|
|
632
|
-
* Get an Observable that emits connection lifecycle events.
|
|
633
|
-
*
|
|
634
|
-
* @returns Observable that emits 'connected' or 'disconnected'
|
|
635
|
-
* @internal
|
|
636
|
-
*/
|
|
637
|
-
get connectionEvents$() {
|
|
638
|
-
return this.connectionEventsSubject$.asObservable();
|
|
639
|
-
}
|
|
640
|
-
/**
|
|
641
|
-
* Get the current SerialPort instance.
|
|
642
|
-
*
|
|
643
|
-
* @returns The current SerialPort instance, or `null` if no port is open
|
|
644
|
-
* @internal
|
|
645
|
-
*/
|
|
646
|
-
get currentPort() {
|
|
647
|
-
return this.port;
|
|
648
|
-
}
|
|
649
|
-
};
|
|
650
|
-
|
|
651
|
-
// packages/web-serial-rxjs/src/client/index.ts
|
|
652
|
-
function createSerialClient(options) {
|
|
653
|
-
return new SerialClientImpl(options);
|
|
654
|
-
}
|
|
655
|
-
|
|
656
|
-
// packages/web-serial-rxjs/src/shell/create-shell-client.ts
|
|
657
|
-
import {
|
|
658
|
-
Observable as Observable3,
|
|
659
|
-
Subject as Subject2,
|
|
660
|
-
defaultIfEmpty,
|
|
661
|
-
defer as defer2,
|
|
662
|
-
firstValueFrom,
|
|
663
|
-
shareReplay
|
|
664
|
-
} from "rxjs";
|
|
665
|
-
var DEFAULT_TIMEOUT = 1e4;
|
|
666
|
-
var DEFAULT_RETRY = 0;
|
|
667
|
-
var DEFAULT_LINE_ENDING = "\r\n";
|
|
668
|
-
var ShellClientImpl = class {
|
|
669
|
-
constructor(client, options) {
|
|
670
|
-
this.client = client;
|
|
671
|
-
this.bufferTick$ = new Subject2();
|
|
672
|
-
this.queueChain = Promise.resolve();
|
|
673
|
-
this.readBuffer = "";
|
|
674
|
-
this.prompt = options.prompt;
|
|
675
|
-
this.timeout = options.timeout ?? DEFAULT_TIMEOUT;
|
|
676
|
-
this.retry = options.retry ?? DEFAULT_RETRY;
|
|
677
|
-
this.lineEnding = options.lineEnding ?? DEFAULT_LINE_ENDING;
|
|
678
|
-
this.promptRegex = this.prompt instanceof RegExp ? this.createAnchoredRegex(this.prompt) : null;
|
|
679
|
-
this.read$ = this.client.getReadStreamAsText().pipe(shareReplay(1));
|
|
680
|
-
this.read$.subscribe({
|
|
681
|
-
next: (chunk) => {
|
|
682
|
-
this.readBuffer += chunk;
|
|
683
|
-
this.bufferTick$.next();
|
|
684
|
-
},
|
|
685
|
-
error: (error) => this.bufferTick$.error(error),
|
|
686
|
-
complete: () => this.bufferTick$.complete()
|
|
687
|
-
});
|
|
688
|
-
}
|
|
689
|
-
exec$(command) {
|
|
690
|
-
return this.enqueue(async () => {
|
|
691
|
-
for (let attempt = 0; attempt <= this.retry; attempt += 1) {
|
|
692
|
-
try {
|
|
693
|
-
await this.writeText(command + this.lineEnding);
|
|
694
|
-
const stdout = await this.waitUntilPrompt(this.timeout);
|
|
695
|
-
return { stdout };
|
|
696
|
-
} catch (error) {
|
|
697
|
-
if (!this.isTimeoutError(error) || attempt >= this.retry) {
|
|
698
|
-
throw error;
|
|
699
|
-
}
|
|
700
|
-
}
|
|
701
|
-
}
|
|
702
|
-
throw new Error("Unexpected command execution state");
|
|
703
|
-
});
|
|
704
|
-
}
|
|
705
|
-
readUntilPrompt$() {
|
|
706
|
-
return this.enqueue(async () => {
|
|
707
|
-
for (let attempt = 0; attempt <= this.retry; attempt += 1) {
|
|
708
|
-
try {
|
|
709
|
-
return await this.waitUntilPrompt(this.timeout);
|
|
710
|
-
} catch (error) {
|
|
711
|
-
if (!this.isTimeoutError(error) || attempt >= this.retry) {
|
|
712
|
-
throw error;
|
|
713
|
-
}
|
|
463
|
+
const current = machine.current;
|
|
464
|
+
if (current !== SerialSessionState.Idle && current !== SerialSessionState.Error) {
|
|
465
|
+
const error = reportError(
|
|
466
|
+
new SerialError(
|
|
467
|
+
"PORT_ALREADY_OPEN" /* PORT_ALREADY_OPEN */,
|
|
468
|
+
`Cannot connect while session state is '${current}'`
|
|
469
|
+
),
|
|
470
|
+
"non-fatal",
|
|
471
|
+
{ fallbackCode: "PORT_ALREADY_OPEN" /* PORT_ALREADY_OPEN */ }
|
|
472
|
+
);
|
|
473
|
+
subscriber.error(error);
|
|
474
|
+
return;
|
|
714
475
|
}
|
|
715
|
-
}
|
|
716
|
-
throw new Error("Unexpected prompt waiting state");
|
|
717
|
-
});
|
|
718
|
-
}
|
|
719
|
-
enqueue(operation) {
|
|
720
|
-
return defer2(
|
|
721
|
-
() => new Observable3((subscriber) => {
|
|
722
476
|
let cancelled = false;
|
|
477
|
+
machine.toConnecting();
|
|
723
478
|
const run = async () => {
|
|
479
|
+
let selectedPort = null;
|
|
724
480
|
try {
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
481
|
+
selectedPort = await navigator.serial.requestPort(
|
|
482
|
+
buildRequestOptions(resolvedOptions)
|
|
483
|
+
);
|
|
484
|
+
await selectedPort.open({
|
|
485
|
+
baudRate: resolvedOptions.baudRate,
|
|
486
|
+
dataBits: resolvedOptions.dataBits,
|
|
487
|
+
stopBits: resolvedOptions.stopBits,
|
|
488
|
+
parity: resolvedOptions.parity,
|
|
489
|
+
bufferSize: resolvedOptions.bufferSize,
|
|
490
|
+
flowControl: resolvedOptions.flowControl
|
|
491
|
+
});
|
|
730
492
|
} catch (error) {
|
|
493
|
+
if (selectedPort) {
|
|
494
|
+
await closePortSafely(selectedPort);
|
|
495
|
+
}
|
|
496
|
+
activePort = null;
|
|
497
|
+
const serialError = reportError(error, "fatal", {
|
|
498
|
+
fallbackCode: "PORT_OPEN_FAILED" /* PORT_OPEN_FAILED */,
|
|
499
|
+
messagePrefix: "Failed to open port"
|
|
500
|
+
});
|
|
731
501
|
if (!cancelled) {
|
|
732
|
-
subscriber.error(
|
|
502
|
+
subscriber.error(serialError);
|
|
733
503
|
}
|
|
504
|
+
return;
|
|
505
|
+
}
|
|
506
|
+
if (cancelled) {
|
|
507
|
+
await closePortSafely(selectedPort);
|
|
508
|
+
return;
|
|
734
509
|
}
|
|
510
|
+
activePort = selectedPort;
|
|
511
|
+
lineBuffer.clear();
|
|
512
|
+
activePump = createReadPump(selectedPort, {
|
|
513
|
+
onChunk: (text) => {
|
|
514
|
+
receiveSubject.next(text);
|
|
515
|
+
for (const line of lineBuffer.feed(text)) {
|
|
516
|
+
linesSubject.next(line);
|
|
517
|
+
}
|
|
518
|
+
},
|
|
519
|
+
onError: (pumpError) => reportError(pumpError, "fatal", {
|
|
520
|
+
fallbackCode: "READ_FAILED" /* READ_FAILED */,
|
|
521
|
+
messagePrefix: "Read pump failed"
|
|
522
|
+
})
|
|
523
|
+
});
|
|
524
|
+
activePump.start();
|
|
525
|
+
sendQueue.clear();
|
|
526
|
+
machine.toConnected();
|
|
527
|
+
subscriber.next();
|
|
528
|
+
subscriber.complete();
|
|
735
529
|
};
|
|
736
|
-
|
|
737
|
-
this.queueChain = scheduled.then(
|
|
738
|
-
() => void 0,
|
|
739
|
-
() => void 0
|
|
740
|
-
);
|
|
530
|
+
void run();
|
|
741
531
|
return () => {
|
|
742
532
|
cancelled = true;
|
|
743
533
|
};
|
|
744
|
-
})
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
534
|
+
});
|
|
535
|
+
},
|
|
536
|
+
disconnect$() {
|
|
537
|
+
return new Observable3((subscriber) => {
|
|
538
|
+
const current = machine.current;
|
|
539
|
+
if (current === SerialSessionState.Idle || current === SerialSessionState.Unsupported) {
|
|
540
|
+
subscriber.next();
|
|
541
|
+
subscriber.complete();
|
|
542
|
+
return;
|
|
543
|
+
}
|
|
544
|
+
if (current !== SerialSessionState.Connected && current !== SerialSessionState.Error) {
|
|
545
|
+
const error = reportError(
|
|
546
|
+
new SerialError(
|
|
547
|
+
"PORT_NOT_OPEN" /* PORT_NOT_OPEN */,
|
|
548
|
+
`Cannot disconnect while session state is '${current}'`
|
|
549
|
+
),
|
|
550
|
+
"non-fatal",
|
|
551
|
+
{ fallbackCode: "PORT_NOT_OPEN" /* PORT_NOT_OPEN */ }
|
|
552
|
+
);
|
|
553
|
+
subscriber.error(error);
|
|
554
|
+
return;
|
|
555
|
+
}
|
|
556
|
+
machine.toDisconnecting();
|
|
557
|
+
sendQueue.clear();
|
|
558
|
+
const portToClose = activePort;
|
|
559
|
+
const run = async () => {
|
|
560
|
+
try {
|
|
561
|
+
await teardownPump();
|
|
562
|
+
if (portToClose) {
|
|
563
|
+
try {
|
|
564
|
+
await portToClose.close();
|
|
565
|
+
} catch (error) {
|
|
566
|
+
activePort = null;
|
|
567
|
+
const serialError = reportError(error, "fatal", {
|
|
568
|
+
fallbackCode: "CONNECTION_LOST" /* CONNECTION_LOST */,
|
|
569
|
+
messagePrefix: "Failed to close port"
|
|
570
|
+
});
|
|
571
|
+
subscriber.error(serialError);
|
|
572
|
+
return;
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
activePort = null;
|
|
576
|
+
machine.toIdle();
|
|
577
|
+
subscriber.next();
|
|
578
|
+
subscriber.complete();
|
|
579
|
+
} catch (error) {
|
|
580
|
+
const serialError = reportError(error, "fatal", {
|
|
581
|
+
fallbackCode: "UNKNOWN" /* UNKNOWN */,
|
|
582
|
+
messagePrefix: "Unexpected disconnect failure"
|
|
583
|
+
});
|
|
584
|
+
subscriber.error(serialError);
|
|
585
|
+
}
|
|
586
|
+
};
|
|
587
|
+
void run();
|
|
588
|
+
});
|
|
589
|
+
},
|
|
590
|
+
send$(data) {
|
|
591
|
+
return sendQueue.enqueue(async () => {
|
|
592
|
+
const payload = typeof data === "string" ? textEncoder.encode(data) : data;
|
|
593
|
+
try {
|
|
594
|
+
await writeToPort(payload);
|
|
595
|
+
} catch (error) {
|
|
596
|
+
throw reportError(error, "non-fatal", {
|
|
597
|
+
fallbackCode: "WRITE_FAILED" /* WRITE_FAILED */,
|
|
598
|
+
messagePrefix: "Failed to write data"
|
|
599
|
+
});
|
|
774
600
|
}
|
|
775
|
-
};
|
|
776
|
-
const subscription = this.bufferTick$.subscribe({
|
|
777
|
-
next: () => tryResolve(),
|
|
778
|
-
error: (error) => fail(error),
|
|
779
|
-
complete: () => fail(new Error("Read stream completed before prompt was found"))
|
|
780
601
|
});
|
|
781
|
-
}
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
return body2.trimEnd();
|
|
789
|
-
}
|
|
790
|
-
return null;
|
|
791
|
-
}
|
|
792
|
-
if (!this.promptRegex) {
|
|
793
|
-
return null;
|
|
794
|
-
}
|
|
795
|
-
const match = this.promptRegex.exec(this.readBuffer);
|
|
796
|
-
if (!match || match.index == null) {
|
|
797
|
-
return null;
|
|
798
|
-
}
|
|
799
|
-
const body = this.readBuffer.slice(0, match.index);
|
|
800
|
-
this.readBuffer = "";
|
|
801
|
-
return body.trimEnd();
|
|
802
|
-
}
|
|
803
|
-
createAnchoredRegex(source) {
|
|
804
|
-
const flags = source.flags.replace(/g/g, "");
|
|
805
|
-
return new RegExp(`(?:${source.source})$`, flags);
|
|
806
|
-
}
|
|
807
|
-
isTimeoutError(error) {
|
|
808
|
-
return error instanceof Error && error.message.includes("Timed out waiting for prompt");
|
|
809
|
-
}
|
|
810
|
-
};
|
|
811
|
-
function createShellClient(client, options) {
|
|
812
|
-
return new ShellClientImpl(client, options);
|
|
602
|
+
},
|
|
603
|
+
state$: machine.state$,
|
|
604
|
+
isConnected$,
|
|
605
|
+
errors$,
|
|
606
|
+
receive$,
|
|
607
|
+
lines$
|
|
608
|
+
};
|
|
813
609
|
}
|
|
814
610
|
export {
|
|
815
|
-
BrowserType,
|
|
816
611
|
SerialError,
|
|
817
612
|
SerialErrorCode,
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
createSerialClient,
|
|
821
|
-
createShellClient,
|
|
822
|
-
detectBrowserType,
|
|
823
|
-
hasWebSerialSupport,
|
|
824
|
-
isBrowserSupported,
|
|
825
|
-
isChromiumBased,
|
|
826
|
-
observableToWritable,
|
|
827
|
-
readableToObservable,
|
|
828
|
-
subscribeToWritable
|
|
613
|
+
SerialSessionState,
|
|
614
|
+
createSerialSession
|
|
829
615
|
};
|