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
JavaScript
//#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 };