UNPKG

webserial-core

Version:

A strongly-typed, event-driven, abstract TypeScript library for the Web Serial API with custom parsers, command queue, handshake validation, and auto-reconnect.

1,146 lines (1,145 loc) 33.7 kB
//#region src/core/SerialEventEmitter.ts var e = class { listeners = {}; on(e, t) { return this.listeners[e] || (this.listeners[e] = /* @__PURE__ */ new Set()), this.listeners[e].add(t), this; } off(e, t) { return this.listeners[e] && this.listeners[e].delete(t), this; } emit(e, ...t) { let n = this.listeners[e]; if (!n || n.size === 0) return !1; for (let e of n) e(...t); return !0; } }, t = class { static instances = /* @__PURE__ */ new Set(); static portInstanceMap = /* @__PURE__ */ new WeakMap(); static getInstances() { return Array.from(this.instances); } static register(e) { this.instances.add(e); } static unregister(e) { this.instances.delete(e); } static isPortInUse(e, t) { let n = this.portInstanceMap.get(e); return n !== void 0 && n !== t; } static lockPort(e, t) { this.portInstanceMap.set(e, t); } static unlockPort(e) { this.portInstanceMap.delete(e); } }, n = class { queue = []; isProcessing = !1; isPaused = !0; timeoutId = null; commandTimeout; onSend; onTimeout; constructor(e) { this.commandTimeout = e.commandTimeout, this.onSend = e.onSend, this.onTimeout = e.onTimeout; } get queueSize() { return this.queue.length; } enqueue(e) { this.queue.push(e), this.tryProcessNext(); } advance() { this.clearCommandTimeout(), this.isProcessing = !1, this.tryProcessNext(); } pause() { this.isPaused = !0, this.clearCommandTimeout(), this.isProcessing = !1; } resume() { this.isPaused = !1, this.tryProcessNext(); } clear() { this.queue = [], this.clearCommandTimeout(), this.isProcessing = !1; } snapshot() { return [...this.queue]; } restore(e) { this.queue = [...e, ...this.queue]; } tryProcessNext() { if (this.isPaused || this.isProcessing || this.queue.length === 0) return; this.isProcessing = !0; let e = this.queue.shift(); this.commandTimeout > 0 && (this.timeoutId = setTimeout(() => { this.timeoutId = null, this.onTimeout(e), this.advance(); }, this.commandTimeout)), this.onSend(e).catch(() => { this.advance(); }); } clearCommandTimeout() { this.timeoutId !== null && (clearTimeout(this.timeoutId), this.timeoutId = null); } }, r = class e extends Error { constructor(t) { super(t), this.name = "SerialPortConflictError", Object.setPrototypeOf(this, e.prototype); } }, i = class e extends Error { constructor(t) { super(t), this.name = "SerialPermissionError", Object.setPrototypeOf(this, e.prototype); } }, a = class e extends Error { constructor(t) { super(t), this.name = "SerialTimeoutError", Object.setPrototypeOf(this, e.prototype); } }, o = class e extends Error { constructor(t) { super(t), this.name = "SerialReadError", Object.setPrototypeOf(this, e.prototype); } }, s = class e extends Error { constructor(t) { super(t), this.name = "SerialWriteError", Object.setPrototypeOf(this, e.prototype); } }, c = class r extends e { port = null; reader = null; writer = null; queue; options; isConnecting = !1; abortController = null; userInitiatedDisconnect = !1; reconnectTimerId = null; isHandshaking = !1; static customProvider = null; static polyfillOptions; constructor(e) { super(), this.options = { baudRate: e.baudRate, dataBits: e.dataBits ?? 8, stopBits: e.stopBits ?? 1, parity: e.parity ?? "none", bufferSize: e.bufferSize ?? 255, flowControl: e.flowControl ?? "none", filters: e.filters ?? [], commandTimeout: e.commandTimeout ?? 0, parser: e.parser, autoReconnect: e.autoReconnect ?? !1, autoReconnectInterval: e.autoReconnectInterval ?? 1500, handshakeTimeout: e.handshakeTimeout ?? 2e3, provider: e.provider, polyfillOptions: e.polyfillOptions }, this.queue = new n({ commandTimeout: this.options.commandTimeout, onSend: async (e) => { await this.writeToPort(e), this.emit("serial:sent", e, this); }, onTimeout: (e) => { this.emit("serial:timeout", e, this); } }), this.on("serial:data", () => { this.queue.advance(); }), t.register(this); } async handshake() { return !0; } async connect() { if (!this.isConnecting && !this.port) { this.isConnecting = !0, this.emit("serial:connecting", this); try { let e = this.getSerial(); if (!e) throw Error("Web Serial API is not supported in this browser. Use AbstractSerialDevice.setProvider() to set a WebUSB polyfill."); if (this.port = await this.findAndValidatePort(), !this.port) { let t; try { t = await e.requestPort({ filters: this.options.filters }, this.options.polyfillOptions ?? r.polyfillOptions); } catch (e) { throw e instanceof DOMException && (e.name === "NotFoundError" || e.name === "SecurityError" || e.name === "AbortError") ? new i(e instanceof Error ? e.message : String(e)) : e instanceof Error ? e : Error(String(e)); } if (!await this.openAndHandshake(t)) throw Error("Handshake failed: the selected device did not respond correctly."); this.port = t; } this.abortController = new AbortController(), this.queue.resume(), this.emit("serial:connected", this); } catch (e) { if (e instanceof i ? this.emit("serial:need-permission", this) : this.emit("serial:error", e instanceof Error ? e : Error(String(e)), this), this.port) { t.unlockPort(this.port); try { await this.port.close(); } catch {} this.port = null; } throw e; } finally { this.isConnecting = !1; } } } async disconnect() { this.port && (this.userInitiatedDisconnect = !0, this.stopReconnecting(), await this.cleanupPort()); } isConnected() { return !!(this.port && this.port.connected && this.port.readable && this.port.writable); } isDisconnected() { return !this.isConnected(); } async cleanupPort() { if (this.port) { this.queue.pause(), this.abortController?.abort(), this.abortController = null; try { let e = this.reader, t = this.writer; if (this.reader = null, this.writer = null, e) { try { await e.cancel(); } catch {} try { e.releaseLock(); } catch {} } if (t) { try { await t.close(); } catch {} try { t.releaseLock(); } catch {} } try { await this.port.close(); } catch {} } catch (e) { this.emit("serial:error", e instanceof Error ? e : Error(String(e)), this); } finally { this.port && t.unlockPort(this.port), this.port = null, this.options.parser?.reset?.(), this.emit("serial:disconnected", this), !this.userInitiatedDisconnect && this.options.autoReconnect && this.startReconnecting(), this.userInitiatedDisconnect = !1; } } } async forget() { await this.disconnect(), this.port && typeof this.port.forget == "function" && await this.port.forget(), t.unregister(this); } async send(e) { let t; t = typeof e == "string" ? new TextEncoder().encode(e) : e, t.length > 0 && this.queue.enqueue(t); } clearQueue() { this.queue.clear(), this.emit("serial:queue-empty", this); } async writeToPort(e) { if (!this.port || !this.port.writable) throw new s("Port not writable."); this.writer = this.port.writable.getWriter(); try { await this.writer.write(e); } catch (e) { throw new s(e instanceof Error ? e.message : String(e)); } finally { this.writer.releaseLock(), this.writer = null; } } async readLoop() { if (!(!this.port || !this.port.readable) && !this.reader) { this.reader = this.port.readable.getReader(); try { for (;;) { let { value: e, done: t } = await this.reader.read(); if (t) break; e && (this.options.parser ? this.options.parser.parse(e, (e) => { this.emit("serial:data", e, this); }) : this.emit("serial:data", e, this)); } } catch (e) { if (this.port) throw new o(e instanceof Error ? e.message : String(e)); } finally { if (this.reader) { try { this.reader.releaseLock(); } catch {} this.reader = null; } } } } async openAndHandshake(e) { let n = this; if (t.isPortInUse(e, n)) return !1; t.lockPort(e, n); try { await e.open({ baudRate: this.options.baudRate, dataBits: this.options.dataBits, stopBits: this.options.stopBits, parity: this.options.parity, bufferSize: this.options.bufferSize, flowControl: this.options.flowControl }); } catch (n) { throw t.unlockPort(e), n instanceof Error ? n : Error(String(n)); } this.port = e, this.abortController = new AbortController(); let r = this.queue.snapshot(); this.isHandshaking = !0, this.readLoop().catch((e) => { !this.isHandshaking && this.port && (this.emit("serial:error", e, this), this.cleanupPort()); }), this.queue.resume(); try { let t = await this.runHandshakeWithTimeout(); return this.isHandshaking = !1, t ? (this.queue.pause(), this.queue.clear(), this.queue.restore(r), this.options.parser?.reset?.(), !0) : (await this.teardownHandshake(e, r), !1); } catch { return this.isHandshaking = !1, await this.teardownHandshake(e, r), !1; } } async teardownHandshake(e, n) { this.queue.pause(), this.queue.clear(), this.queue.restore(n), await this.stopReader(), this.port = null, this.abortController = null, this.options.parser?.reset?.(); try { await e.close(); } catch {} t.unlockPort(e); } async stopReader() { let e = this.reader; if (this.reader = null, e) { try { await e.cancel(); } catch {} try { e.releaseLock(); } catch {} } } async runHandshakeWithTimeout() { let e = this.options.handshakeTimeout ?? 2e3; return Promise.race([this.handshake(), new Promise((t) => setTimeout(() => t(!1), e))]); } async findAndValidatePort() { let e = this.getSerial(); if (!e) return null; let n = await e.getPorts(this.options.polyfillOptions ?? r.polyfillOptions); if (n.length === 0) return null; let i = this.options.filters ?? [], a = this; for (let e of n) if (!t.isPortInUse(e, a)) { if (i.length > 0) { let t = e.getInfo(); if (!i.some((e) => { let n = e.usbVendorId === void 0 || e.usbVendorId === t.usbVendorId, r = e.usbProductId === void 0 || e.usbProductId === t.usbProductId; return n && r; })) continue; } try { if (await this.openAndHandshake(e)) return e; } catch {} } return null; } startReconnecting() { this.reconnectTimerId ||= (this.emit("serial:reconnecting", this), setInterval(async () => { if (this.port || this.isConnecting) { this.stopReconnecting(); return; } try { let e = await this.findAndValidatePort(); e && (this.stopReconnecting(), await this.reconnect(e)); } catch {} }, this.options.autoReconnectInterval)); } stopReconnecting() { this.reconnectTimerId &&= (clearInterval(this.reconnectTimerId), null); } async reconnect(e) { if (!(this.isConnecting || this.port)) { this.isConnecting = !0, this.emit("serial:connecting", this); try { this.port = e, this.abortController = new AbortController(), this.queue.resume(), this.emit("serial:connected", this); } catch (e) { this.emit("serial:error", e instanceof Error ? e : Error(String(e)), this), this.port &&= (t.unlockPort(this.port), null), this.options.autoReconnect && this.startReconnecting(); } finally { this.isConnecting = !1; } } } static getInstances() { return t.getInstances(); } static async connectAll() { let e = t.getInstances(); for (let t of e) try { await t.connect(); } catch {} } static setProvider(e, t) { r.customProvider = e, r.polyfillOptions = t; } getSerial() { return this.options.provider ? this.options.provider : r.customProvider ? r.customProvider : typeof navigator < "u" && navigator.serial ? navigator.serial : null; } }; //#endregion //#region src/parsers/CCTalkParser.ts function l(e = 50) { let t = new Uint8Array(), n = null; function r(e) { for (; !(t.length < 2);) { let n = t[1] + 5; if (t.length < n) break; e(t.slice(0, n)), t = t.slice(n); } } return { parse(i, a) { n !== null && (clearTimeout(n), n = null); let o = new Uint8Array(t.length + i.length); o.set(t), o.set(i, t.length), t = o, r(a), t.length > 0 && (n = setTimeout(() => { t = new Uint8Array(), n = null; }, e)); }, reset() { n !== null && (clearTimeout(n), n = null), t = new Uint8Array(); } }; } //#endregion //#region src/parsers/DelimiterParser.ts function u(e) { return typeof e == "string" ? new TextEncoder().encode(e) : e instanceof Uint8Array ? e : new Uint8Array(e); } function d(e, t) { if (t.length === 0) return 0; outer: for (let n = 0; n <= e.length - t.length; n++) { for (let r = 0; r < t.length; r++) if (e[n + r] !== t[r]) continue outer; return n; } return -1; } function f(e, t) { let n = t?.includeDelimiter ?? !1, r = u(e); if (typeof e == "string") { let e = new Uint8Array(), t = new TextDecoder(); return { parse(i, a) { let o = new Uint8Array(e.length + i.length); o.set(e), o.set(i, e.length), e = o; let s; for (; (s = d(e, r)) !== -1;) { let i = n ? s + r.length : s; a(t.decode(e.slice(0, i))), t = new TextDecoder(), e = e.slice(s + r.length); } }, reset() { e = new Uint8Array(), t = new TextDecoder(); } }; } let i = new Uint8Array(); return { parse(e, t) { let a = new Uint8Array(i.length + e.length); a.set(i), a.set(e, i.length), i = a; let o; for (; (o = d(i, r)) !== -1;) { let e = n ? o + r.length : o; t(i.slice(0, e)), i = i.slice(o + r.length); } }, reset() { i = new Uint8Array(); } }; } //#endregion //#region src/parsers/FixedLengthParser.ts function p(e) { if (e <= 0) throw Error("FixedLengthParser: length must be greater than 0"); let t = new Uint8Array(); return { parse(n, r) { let i = new Uint8Array(t.length + n.length); for (i.set(t), i.set(n, t.length), t = i; t.length >= e;) r(t.slice(0, e)), t = t.slice(e); }, reset() { t = new Uint8Array(); } }; } //#endregion //#region src/parsers/InterByteTimeoutParser.ts function m(e) { if (e.interval <= 0) throw Error("InterByteTimeoutParser: interval must be greater than 0"); let t = e.maxBufferSize ?? 65536, n = new Uint8Array(), r = null, i = null; function a() { r !== null && (clearTimeout(r), r = null), n.length > 0 && i !== null && (i(n.slice()), n = new Uint8Array()); } return { parse(o, s) { i = s, r !== null && (clearTimeout(r), r = null); let c = new Uint8Array(n.length + o.length); if (c.set(n), c.set(o, n.length), n = c, n.length >= t) { a(); return; } r = setTimeout(() => { r = null, a(); }, e.interval); }, reset() { r !== null && (clearTimeout(r), r = null), n = new Uint8Array(), i = null; } }; } //#endregion //#region src/parsers/PacketLengthParser.ts function h(e) { let t = e?.delimiter ?? 170, n = e?.packetOverhead ?? 2, r = e?.lengthBytes ?? 1, i = e?.lengthOffset ?? 1, a = e?.maxLen ?? 255; if (i + r > n) throw Error("PacketLengthParser: lengthOffset + lengthBytes must not exceed packetOverhead"); let o = new Uint8Array(); return { parse(e, s) { let c = new Uint8Array(o.length + e.length); for (c.set(o), c.set(e, o.length), o = c;;) { let e = o.indexOf(t); if (e === -1) { o = new Uint8Array(); break; } e > 0 && (o = o.slice(e)); let c = i + r; if (o.length < c) break; let l = 0; for (let e = 0; e < r; e++) l = l << 8 | o[i + e]; if (l > a) { o = o.slice(1); continue; } let u = l + n; if (o.length < u) break; s(o.slice(0, u)), o = o.slice(u); } }, reset() { o = new Uint8Array(); } }; } //#endregion //#region src/parsers/RawParser.ts function g() { return { parse(e, t) { t(e); }, reset() {} }; } //#endregion //#region src/parsers/ReadlineParser.ts function _(e, t) { if (t.length === 0) return 0; outer: for (let n = 0; n <= e.length - t.length; n++) { for (let r = 0; r < t.length; r++) if (e[n + r] !== t[r]) continue outer; return n; } return -1; } function v(e) { let t = e?.encoding ?? "utf-8", n = e?.includeDelimiter ?? !1, r = e?.delimiter ?? "\n", i; i = typeof r == "string" ? new TextEncoder().encode(r) : r instanceof Uint8Array ? r : new Uint8Array(r); let a = new Uint8Array(); return { parse(e, r) { let o = new Uint8Array(a.length + e.length); o.set(a), o.set(e, a.length), a = o; let s; for (; (s = _(a, i)) !== -1;) { let e = n ? s + i.length : s; r(new TextDecoder(t).decode(a.slice(0, e))), a = a.slice(s + i.length); } }, reset() { a = new Uint8Array(); } }; } //#endregion //#region src/parsers/ReadyParser.ts function y(e, t) { if (t.length === 0) return 0; outer: for (let n = 0; n <= e.length - t.length; n++) { for (let r = 0; r < t.length; r++) if (e[n + r] !== t[r]) continue outer; return n; } return -1; } function b(e) { let t = e.delimiter, n; n = typeof t == "string" ? new TextEncoder().encode(t) : t instanceof Uint8Array ? t : new Uint8Array(t); let r = !1, i = new Uint8Array(); return { parse(t, a) { if (r) { a(t); return; } let o = new Uint8Array(i.length + t.length); o.set(i), o.set(t, i.length), i = o; let s = y(i, n); if (s === -1) return; r = !0, e.onReady?.(); let c = i.slice(s + n.length); i = new Uint8Array(), c.length > 0 && a(c); }, reset() { r = !1, i = new Uint8Array(); } }; } //#endregion //#region src/parsers/RegexParser.ts function x(e) { let t = e.regex instanceof RegExp ? e.regex : new RegExp(e.regex), n = e.encoding ?? "utf-8", r = ""; return { parse(e, i) { let a = new TextDecoder(n); r += a.decode(e); let o = r.split(t); r = o.pop() ?? ""; for (let e of o) i(e); }, reset() { r = ""; } }; } //#endregion //#region src/parsers/SlipParser.ts var S = { END: 192, ESC: 219, ESC_END: 220, ESC_ESC: 221 }; function C(e) { let t = e?.END ?? S.END, n = e?.ESC ?? S.ESC, r = e?.ESC_END ?? S.ESC_END, i = e?.ESC_ESC ?? S.ESC_ESC, a = e?.START, o = e?.ESC_START ?? n, s = [], c = !1, l = a === void 0; return { parse(e, u) { for (let d = 0; d < e.length; d++) { let f = e[d]; if (!l) { f === a && (l = !0); continue; } if (f === t) { s.length > 0 && (u(new Uint8Array(s)), s = []), a !== void 0 && (l = !1), c = !1; continue; } if (c) { c = !1, f === r ? s.push(t) : f === i ? s.push(n) : a !== void 0 && f === o ? s.push(a) : s.push(f); continue; } if (f === n) { c = !0; continue; } s.push(f); } }, reset() { s = [], c = !1, l = a === void 0; } }; } function ee(e, t) { let n = t?.END ?? S.END, r = t?.ESC ?? S.ESC, i = t?.ESC_END ?? S.ESC_END, a = t?.ESC_ESC ?? S.ESC_ESC, o = t?.START, s = t?.ESC_START ?? r, c = t?.bluetoothQuirk ?? !1, l = []; c && l.push(n); for (let t = 0; t < e.length; t++) { let c = e[t]; c === n ? l.push(r, i) : c === r ? l.push(r, a) : o !== void 0 && c === o ? l.push(r, s) : l.push(c); } return l.push(n), new Uint8Array(l); } //#endregion //#region src/parsers/SpacePacketParser.ts function te(e) { let t = e?.timeCodeFieldLength ?? 0, n = e?.ancillaryDataFieldLength ?? 0, r = new Uint8Array(); function i(e) { let r = e[0], i = e[1], a = e[2], o = e[3], s = e[4], c = e[5], l = r >> 5 & 7, u = r >> 4 & 1, d = r >> 3 & 1, f = (r & 7) << 8 | i, p = a >> 6 & 3, m = (a & 63) << 8 | o, h = s << 8 | c, g = { versionNumber: l, identification: { apid: f, secondaryHeader: d, type: u }, sequenceControl: { packetName: m, sequenceFlags: p }, dataLength: h }, _, v = 6; if (d === 1) { _ = {}; let r = new TextDecoder("latin1"); t > 0 && (_.timeCode = r.decode(e.slice(v, v + t)), v += t), n > 0 && (_.ancillaryData = r.decode(e.slice(v, v + n)), v += n); } let y = new TextDecoder("latin1").decode(e.slice(v)); return { header: g, secondaryHeader: _, data: y }; } return { parse(e, t) { let n = new Uint8Array(r.length + e.length); for (n.set(r), n.set(e, r.length), r = n; r.length >= 6;) { let e = 6 + (r[4] << 8 | r[5]) + 1; if (r.length < e) break; t(i(r.slice(0, e))), r = r.slice(e); } }, reset() { r = new Uint8Array(); } }; } //#endregion //#region src/adapters/web-usb/WebUsbProvider.ts var w = 32, T = 34, E = 0, D = 30, O = 3, k = 7, A = 1, ne = 0, re = 771, ie = 768, j = 255, M = 8, N = "none", P = 1, F = [ 16, 8, 7, 6, 5 ], I = [1, 2], L = [ "none", "even", "odd" ], R = [ "none", "odd", "even" ], z = [ 1, 1.5, 2 ], B = { usbControlInterfaceClass: 2, usbTransferInterfaceClass: 10, protocol: void 0 }; function V(e, t) { let n = e.configurations[0]; if (!n) return null; for (let e of n.interfaces) if (e.alternates[0]?.interfaceClass === t) return e; return null; } function H(e, t) { let n = e.configurations[0]; if (!n) return null; for (let e of n.interfaces) { let n = e.alternates[0]; if (!n || n.interfaceClass !== t) continue; let r = n.endpoints.some((e) => e.direction === "in"), i = n.endpoints.some((e) => e.direction === "out"); if (r && i) return e; } return null; } function U(e, t) { let n = e.alternates[0]; if (n) { for (let e of n.endpoints) if (e.direction === t) return e; } throw TypeError(`Interface ${e.interfaceNumber} does not have an ${t} endpoint.`); } function W(e, t) { return t === 2 ? "cdc_acm" : e.vendorId === 4292 ? "cp210x" : "none"; } var G = class { device_; endpoint_; onError_; constructor(e, t, n) { this.device_ = e, this.endpoint_ = t, this.onError_ = n; } pull(e) { (async () => { let t = this.endpoint_.packetSize; try { let n = await this.device_.transferIn(this.endpoint_.endpointNumber, t); if (n.status !== "ok") { e.error(`USB error: ${n.status}`), this.onError_(); return; } if (n.data?.buffer && n.data.byteLength > 0) { let t = new Uint8Array(n.data.buffer, n.data.byteOffset, n.data.byteLength); t.length > 0 && e.enqueue(t); } } catch (t) { e.error(String(t)), this.onError_(); } })(); } }, K = class { device_; endpoint_; onError_; constructor(e, t, n) { this.device_ = e, this.endpoint_ = t, this.onError_ = n; } async write(e, t) { try { let n = await this.device_.transferOut(this.endpoint_.endpointNumber, e.buffer); n.status !== "ok" && (t.error(n.status), this.onError_()); } catch (e) { t.error(String(e)), this.onError_(); } } }, q = class { device_; protocol_; controlInterface_; transferInterface_; inEndpoint_; outEndpoint_; serialOptions_; readable_ = null; writable_ = null; cdcOutputSignals_ = { dataTerminalReady: !1, requestToSend: !1, break: !1 }; constructor(e, t) { this.device_ = e; let n = { ...B, ...t }; this.protocol_ = n.protocol ?? W(e, n.usbControlInterfaceClass); let r = n.usbControlInterfaceClass, i = n.usbTransferInterfaceClass; if (r === i) { let t = H(e, i); if (!t) throw TypeError(`Unable to find interface with class ${i} that has both IN and OUT endpoints.`); this.controlInterface_ = t, this.transferInterface_ = t; } else { let t = V(e, r); if (!t) throw TypeError(`Unable to find control interface with class ${r}.`); let n = H(e, i) ?? V(e, i); if (!n) throw TypeError(`Unable to find transfer interface with class ${i}.`); this.controlInterface_ = t, this.transferInterface_ = n; } this.inEndpoint_ = U(this.transferInterface_, "in"), this.outEndpoint_ = U(this.transferInterface_, "out"); } get readable() { return !this.readable_ && this.device_.opened && (this.readable_ = new ReadableStream(new G(this.device_, this.inEndpoint_, () => { this.readable_ = null; }), { highWaterMark: this.serialOptions_?.bufferSize ?? j })), this.readable_; } get writable() { return !this.writable_ && this.device_.opened && (this.writable_ = new WritableStream(new K(this.device_, this.outEndpoint_, () => { this.writable_ = null; }), new ByteLengthQueuingStrategy({ highWaterMark: this.serialOptions_?.bufferSize ?? j }))), this.writable_; } async open(e) { this.serialOptions_ = e, this.validateOptions(); try { switch (await this.device_.open(), this.device_.configuration === null && await this.device_.selectConfiguration(1), await this.device_.claimInterface(this.controlInterface_.interfaceNumber), this.controlInterface_ !== this.transferInterface_ && await this.device_.claimInterface(this.transferInterface_.interfaceNumber), this.protocol_) { case "cdc_acm": await this.cdcInit(); break; case "cp210x": await this.cp210xInit(); break; case "none": break; } } catch (e) { throw this.device_.opened && await this.device_.close(), Error("Error setting up device: " + (e instanceof Error ? e.message : String(e)), { cause: e }); } } async close() { let e = []; if (this.readable_ && e.push(this.readable_.cancel()), this.writable_ && e.push(this.writable_.abort()), await Promise.all(e), this.readable_ = null, this.writable_ = null, this.device_.opened) { switch (this.protocol_) { case "cdc_acm": await this.cdcSetSignals({ dataTerminalReady: !1, requestToSend: !1 }); break; case "cp210x": await this.cp210xDeinit(); break; } await this.device_.close(); } } async forget() { return this.device_.forget(); } getInfo() { return { usbVendorId: this.device_.vendorId, usbProductId: this.device_.productId }; } async cdcInit() { await this.cdcSetLineCoding(), await this.cdcSetSignals({ dataTerminalReady: !0 }); } async cdcSetSignals(e) { if (this.cdcOutputSignals_ = { ...this.cdcOutputSignals_, ...e }, e.dataTerminalReady !== void 0 || e.requestToSend !== void 0) { let e = !!this.cdcOutputSignals_.dataTerminalReady | (this.cdcOutputSignals_.requestToSend ? 2 : 0); await this.device_.controlTransferOut({ requestType: "class", recipient: "interface", request: T, value: e, index: this.controlInterface_.interfaceNumber }); } } async cdcSetLineCoding() { let e = /* @__PURE__ */ new ArrayBuffer(7), t = new DataView(e); if (t.setUint32(0, this.serialOptions_.baudRate, !0), t.setUint8(4, z.indexOf(this.serialOptions_.stopBits ?? P)), t.setUint8(5, R.indexOf(this.serialOptions_.parity ?? N)), t.setUint8(6, this.serialOptions_.dataBits ?? M), (await this.device_.controlTransferOut({ requestType: "class", recipient: "interface", request: w, value: 0, index: this.controlInterface_.interfaceNumber }, e)).status !== "ok") throw new DOMException("Failed to set line coding.", "NetworkError"); } async cp210xInit() { let e = this.controlInterface_.interfaceNumber; await this.device_.controlTransferOut({ requestType: "vendor", recipient: "interface", request: E, value: A, index: e }); let t = /* @__PURE__ */ new ArrayBuffer(4); new DataView(t).setUint32(0, this.serialOptions_.baudRate, !0), await this.device_.controlTransferOut({ requestType: "vendor", recipient: "interface", request: D, value: 0, index: e }, t); let n = this.serialOptions_.dataBits ?? M, r = { none: 0, odd: 16, even: 32 }[this.serialOptions_.parity ?? N] ?? 0, i = ({ 1: 0, 2: 2 }[this.serialOptions_.stopBits ?? P] ?? 0) << 8 | r | n; await this.device_.controlTransferOut({ requestType: "vendor", recipient: "interface", request: O, value: i, index: e }), await this.device_.controlTransferOut({ requestType: "vendor", recipient: "interface", request: k, value: re, index: e }); } async cp210xDeinit() { let e = this.controlInterface_.interfaceNumber; await this.device_.controlTransferOut({ requestType: "vendor", recipient: "interface", request: k, value: ie, index: e }), await this.device_.controlTransferOut({ requestType: "vendor", recipient: "interface", request: E, value: ne, index: e }); } validateOptions() { if (this.serialOptions_.baudRate % 1 != 0) throw RangeError(`Invalid baud rate: ${this.serialOptions_.baudRate}`); if (this.serialOptions_.dataBits !== void 0 && !F.includes(this.serialOptions_.dataBits)) throw RangeError(`Invalid dataBits: ${this.serialOptions_.dataBits}`); if (this.serialOptions_.stopBits !== void 0 && !I.includes(this.serialOptions_.stopBits)) throw RangeError(`Invalid stopBits: ${this.serialOptions_.stopBits}`); if (this.serialOptions_.parity !== void 0 && !L.includes(this.serialOptions_.parity)) throw RangeError(`Invalid parity: ${this.serialOptions_.parity}`); } }, J = class { options_; constructor(e) { this.options_ = { ...B, ...e }; } async requestPort(e, t) { let n = { ...this.options_, ...t }, r = []; if (e?.filters && e.filters.length > 0) for (let t of e.filters) { let e = {}; t.usbVendorId !== void 0 && (e.vendorId = t.usbVendorId), t.usbProductId !== void 0 && (e.productId = t.usbProductId), n.usbControlInterfaceClass !== void 0 && n.usbControlInterfaceClass !== 255 ? e.classCode = n.usbControlInterfaceClass : e.vendorId === void 0 && e.productId === void 0 && (e.classCode = n.usbControlInterfaceClass ?? 2), r.push(e); } else r.push({ classCode: n.usbControlInterfaceClass ?? 2 }); return new q(await navigator.usb.requestDevice({ filters: r }), n); } async getPorts(e) { let t = { ...this.options_, ...e }, n = await navigator.usb.getDevices(), r = []; for (let e of n) try { let n = new q(e, t); r.push(n); } catch {} return r; } }, Y = "6e400001-b5a3-f393-e0a9-e50e24dcca9e", ae = "6e400003-b5a3-f393-e0a9-e50e24dcca9e", oe = "6e400002-b5a3-f393-e0a9-e50e24dcca9e", X = 20, se = 10; function ce(e) { let t = null, n = null, r = null; return { get readable() { return t; }, get writable() { return n; }, getInfo() { return {}; }, async open() { if (!e.gatt) throw Error("GATT not available on this Bluetooth device."); r = await e.gatt.connect(); let i = await r.getPrimaryService(Y), a = await i.getCharacteristic(ae), o = await i.getCharacteristic(oe); await a.startNotifications(), t = new ReadableStream({ start(e) { a.addEventListener("characteristicvaluechanged", (t) => { let n = t.target.value.buffer; e.enqueue(new Uint8Array(n)); }); } }), n = new WritableStream({ async write(e) { for (let t = 0; t < e.length; t += X) { let n = e.slice(t, t + X); await o.writeValueWithoutResponse(n), t + X < e.length && await new Promise((e) => setTimeout(e, se)); } } }); }, async close() { r?.connected && r.disconnect(), t = null, n = null; } }; } function le() { return { async requestPort() { if (!navigator.bluetooth) throw Error("Web Bluetooth API is not supported in this browser. Use Chrome on Android, macOS, or ChromeOS."); return ce(await navigator.bluetooth.requestDevice({ filters: [{ services: [Y] }] })); }, async getPorts() { return []; } }; } //#endregion //#region src/adapters/websocket/WebSocketProvider.ts function Z(e) { return new Promise((t, n) => { e.addEventListener("open", () => t(), { once: !0 }), e.addEventListener("error", (e) => n(e), { once: !0 }); }); } function Q(e, t) { return new Promise((n) => { let r = (i) => { let a = JSON.parse(i.data); a.type === t && (e.removeEventListener("message", r), n(a.payload)); }; e.addEventListener("message", r); }); } function $(e, t) { let n = null, r = null; return { get readable() { return n; }, get writable() { return r; }, getInfo() { return { usbVendorId: t.vendorId, usbProductId: t.productId }; }, async open(i) { e.send(JSON.stringify({ type: "open", path: t.path, baudRate: i.baudRate, dataBits: i.dataBits, stopBits: i.stopBits, parity: i.parity, parser: { type: "delimiter", value: "\\n" } })), await Q(e, "opened"); let a = [], o = null, s = !1; function c(e) { let t = JSON.parse(e.data); if (t.type === "data" && t.bytes) { let e = new Uint8Array(t.bytes); o ? o.enqueue(e) : a.push(e); } t.type === "closed" && (s = !0, o && o.close()); } e.addEventListener("message", c), n = new ReadableStream({ start(e) { o = e; for (let t of a) e.enqueue(t); a.length = 0, s && e.close(); }, cancel() { e.removeEventListener("message", c), o = null; } }), r = new WritableStream({ write(t) { e.send(JSON.stringify({ type: "write", bytes: Array.from(t) })); } }); }, async close() { e.send(JSON.stringify({ type: "close" })), n = null, r = null, e.close(); } }; } function ue(e) { return { async requestPort(t) { let n = new WebSocket(e); await Z(n), n.send(JSON.stringify({ type: "list-ports", filters: t?.filters ?? [] })); let r = (await Q(n, "port-list"))[0]; if (!r) throw Error("No ports available on the bridge server. Make sure the Node.js server is running and a device is connected."); return $(n, r); }, async getPorts() { let t = new WebSocket(e); return await Z(t), t.send(JSON.stringify({ type: "list-ports", filters: [] })), (await Q(t, "port-list")).map((e) => $(t, e)); } }; } //#endregion export { c as AbstractSerialDevice, n as CommandQueue, e as SerialEventEmitter, i as SerialPermissionError, r as SerialPortConflictError, o as SerialReadError, t as SerialRegistry, a as SerialTimeoutError, s as SerialWriteError, J as WebUsbProvider, l as ccTalk, le as createBluetoothProvider, ue as createWebSocketProvider, f as delimiter, p as fixedLength, m as interByteTimeout, h as packetLength, g as raw, v as readline, b as readyParser, x as regexParser, C as slipDecoder, ee as slipEncode, te as spacePacket };