webserial-core
Version:
Webserial Core to easy connections with serial devices
6 lines (5 loc) • 34.6 kB
JavaScript
(function(l,d){typeof exports=="object"&&typeof module<"u"?d(exports,require("socket.io-client")):typeof define=="function"&&define.amd?define(["exports","socket.io-client"],d):(l=typeof globalThis<"u"?globalThis:l||self,d(l.WebSerialCore={},l.io))})(this,(function(l,d){"use strict";class w extends CustomEvent{constructor(e,t){super(e,t)}}class p extends EventTarget{__listeners__={debug:!1};__debug__=!1;__listenersCallbacks__=[];dispatch(e,t=null){const n=new w(e,{detail:t});this.dispatchEvent(n),this.__debug__&&this.dispatchEvent(new w("debug",{detail:{type:e,data:t}}))}dispatchAsync(e,t=null,n=100){const s=this;setTimeout(()=>{s.dispatch(e,t)},n)}on(e,t){typeof this.__listeners__[e]<"u"&&!this.__listeners__[e]&&(this.__listeners__[e]=!0),this.__listenersCallbacks__.push({key:e,callback:t}),this.addEventListener(e,t)}off(e,t){this.__listenersCallbacks__=this.__listenersCallbacks__.filter(n=>!(n.key===e&&n.callback===t)),this.removeEventListener(e,t)}serialRegisterAvailableListener(e){this.__listeners__[e]||(this.__listeners__[e]=!1)}get availableListeners(){return Object.keys(this.__listeners__).sort().map(t=>({type:t,listening:this.__listeners__[t]}))}removeAllListeners(){for(const e of this.__listenersCallbacks__)["internal:queue"].includes(e.key)||(this.__listenersCallbacks__=this.__listenersCallbacks__.filter(t=>!(t.key===e.key&&t.callback===e.callback)),this.removeEventListener(e.key,e.callback));for(const e of Object.keys(this.__listeners__))this.__listeners__[e]=!1}}class i extends p{static instance;static devices={};constructor(){super(),["change"].forEach(t=>{this.serialRegisterAvailableListener(t)})}static $dispatchChange(e=null){e&&e.$checkAndDispatchConnection(),i.instance.dispatch("change",{devices:i.devices,dispatcher:e})}static typeError(e){const t=new Error;throw t.message=`Type ${e} is not supported`,t.name="DeviceTypeError",t}static registerType(e){typeof i.devices[e]>"u"&&(i.devices={...i.devices,[e]:{}})}static add(e){const t=e.typeDevice;typeof i.devices[t]>"u"&&i.registerType(t);const n=e.uuid;if(typeof i.devices[t]>"u"&&i.typeError(t),i.devices[t][n])throw new Error(`Device with id ${n} already exists`);return i.devices[t][n]=e,i.$dispatchChange(e),Object.keys(i.devices[t]).indexOf(n)}static get(e,t){return typeof i.devices[e]>"u"&&i.registerType(e),typeof i.devices[e]>"u"&&i.typeError(e),i.devices[e][t]}static getAll(e=null){return e===null?i.devices:(typeof i.devices[e]>"u"&&i.typeError(e),i.devices[e])}static getList(){return Object.values(i.devices).map(t=>Object.values(t)).flat()}static getByNumber(e,t){return typeof i.devices[e]>"u"&&i.typeError(e),Object.values(i.devices[e]).find(s=>s.deviceNumber===t)??null}static getCustom(e,t=1){return typeof i.devices[e]>"u"&&i.typeError(e),Object.values(i.devices[e]).find(s=>s.deviceNumber===t)??null}static async connectToAll(){const e=i.getList();for(const t of e)t.isConnected||await t.connect().catch(console.warn);return Promise.resolve(i.areAllConnected())}static async disconnectAll(){const e=i.getList();for(const t of e)t.isDisconnected||await t.disconnect().catch(console.warn);return Promise.resolve(i.areAllDisconnected())}static async areAllConnected(){const e=i.getList();for(const t of e)if(!t.isConnected)return Promise.resolve(!1);return Promise.resolve(!0)}static async areAllDisconnected(){const e=i.getList();for(const t of e)if(!t.isDisconnected)return Promise.resolve(!1);return Promise.resolve(!0)}static async getAllConnected(){const e=i.getList();return Promise.resolve(e.filter(t=>t.isConnected))}static async getAllDisconnected(){const e=i.getList();return Promise.resolve(e.filter(t=>t.isDisconnected))}}i.instance||(i.instance=new i);function b(o=100){return new Promise(e=>setTimeout(()=>e(),o))}class S{#n="http://localhost:3000";#i={transports:["websocket"]};#e=null;#s=!1;#a=!1;#t;constructor(){this.#t={onResponse:this.onResponse.bind(this),onDisconnect:()=>{this.#s=!1,window.dispatchEvent(new Event("serial:socket:disconnected"))},onConnect:()=>{this.#s=!0,window.dispatchEvent(new Event("serial:socket:connected"))},onConnectError:e=>{console.debug("Socket connection error",e),this.#s=!1,window.dispatchEvent(new Event("serial:socket:disconnected"))}}}set uri(e){const t=new URL(e);if(!["http:","https:","ws:","wss:"].includes(t.protocol))throw new Error("URI must start with http://, https://, ws://, or wss://");this.#n=e}get uri(){return this.#n}set options(e){if(typeof e!="object")throw new Error("Options must be an object");this.#i=e}get options(){return this.#i}get socketId(){return this.#e&&this.#e.id?this.#e.id:null}disconnect(){this.#e&&(this.#e.off("response",this.#t.onResponse),this.#e.off("disconnect",this.#t.onDisconnect),this.#e.off("connect",this.#t.onConnect),this.#e.off("connect_error",this.#t.onConnectError),this.#e.disconnect(),this.#e=null,this.#a=!1),this.#s=!1}prepare(){this.#s||this.#a||(this.#e=d.io(this.#n,this.#i),this.#a=!0,this.#e.on("disconnect",this.#t.onDisconnect),this.#e.on("response",this.#t.onResponse),this.#e.on("connect",this.#t.onConnect),this.#e.on("connect_error",this.#t.onConnectError))}connectDevice(e){if(!this.#e)throw new Error("Socket not connected. Call prepare() first.");this.#e.emit("connectDevice",{config:e})}disconnectDevice(e){if(!this.#e)throw new Error("Socket not connected. Call prepare() first.");this.#e.emit("disconnectDevice",{config:e})}disconnectAllDevices(){if(!this.#e)throw new Error("Socket not connected. Call prepare() first.");this.#e.emit("disconnectAll")}write(e){if(!this.#e)throw new Error("Socket not connected. Call prepare() first.");this.#e.emit("cmd",e)}onResponse(e){let t=i.get(e.name,e.uuid);t||(t=i.getByNumber(e.name,e.deviceNumber)),t&&t.socketResponse(e)}isConnected(){return this.#s}isDisconnected(){return!this.#s}}const c=new S,g={baudRate:9600,dataBits:8,stopBits:1,parity:"none",bufferSize:32768,flowControl:"none"};class v extends p{__internal__={bypassSerialBytesConnection:!1,auto_response:!1,device_number:1,aux_port_connector:0,last_error:{message:null,action:null,code:null,no_code:0},serial:{socket:!1,portInfo:{path:null,vendorId:null,productId:null,parser:{name:"inter-byte-timeout",interval:50}},aux_connecting:"idle",connecting:!1,connected:!1,port:null,last_action:null,response:{length:null,buffer:new Uint8Array([]),as:"uint8",replacer:/[\n\r]+/g,limiter:null,prefixLimiter:!1,sufixLimiter:!0,delimited:!1},reader:null,input_done:null,output_done:null,input_stream:null,output_stream:null,keep_reading:!0,time_until_send_bytes:void 0,delay_first_connection:200,bytes_connection:null,filters:[],config_port:g,queue:[],running_queue:!1,auto_response:null,free_timeout_ms:50,useRTSCTS:!1},device:{type:"unknown",id:window.crypto.randomUUID(),listen_on_port:null},time:{response_connection:500,response_engines:2e3,response_general:2e3},timeout:{until_response:0},interval:{reconnection:0}};#n=null;constructor({filters:e=null,config_port:t=g,no_device:n=1,device_listen_on_channel:s=1,bypassSerialBytesConnection:a=!1,socket:r=!1}={filters:null,config_port:g,no_device:1,device_listen_on_channel:1,bypassSerialBytesConnection:!1,socket:!1}){if(super(),!("serial"in navigator))throw new Error("Web Serial not supported");e&&(this.serialFilters=e),t&&(this.serialConfigPort=t),a&&(this.__internal__.bypassSerialBytesConnection=a),n&&this.#b(n),s&&["number","string"].includes(typeof s)&&(this.listenOnChannel=s),this.__internal__.serial.socket=r,this.#g(),this.#y()}set listenOnChannel(e){if(typeof e=="string"&&(e=parseInt(e)),isNaN(e)||e<1||e>255)throw new Error("Invalid port number");this.__internal__.device.listen_on_port=e,!this.__internal__.bypassSerialBytesConnection&&(this.__internal__.serial.bytes_connection=this.serialSetConnectionConstant(e))}get lastAction(){return this.__internal__.serial.last_action}get listenOnChannel(){return this.__internal__.device.listen_on_port??1}set serialFilters(e){if(this.isConnected)throw new Error("Cannot change serial filters while connected");this.__internal__.serial.filters=e}get serialFilters(){return this.__internal__.serial.filters}set serialConfigPort(e){if(this.isConnected)throw new Error("Cannot change serial filters while connected");this.__internal__.serial.config_port=e}get serialConfigPort(){return this.__internal__.serial.config_port}get useRTSCTS(){return this.__internal__.serial.useRTSCTS}set useRTSCTS(e){this.__internal__.serial.useRTSCTS=e}get isConnected(){const e=this.__internal__.serial.connected,t=this.#i(this.__internal__.serial.port);return e&&!t&&this.#e({error:"Port is closed, not readable or writable."}),this.__internal__.serial.connected=t,this.__internal__.serial.connected}get isConnecting(){return this.__internal__.serial.connecting}get isDisconnected(){const e=this.__internal__.serial.connected,t=this.#i(this.__internal__.serial.port);return!e&&t&&(this.dispatch("serial:connected"),this.#o(!1),i.$dispatchChange(this)),this.__internal__.serial.connected=t,!this.__internal__.serial.connected}get deviceNumber(){return this.__internal__.device_number}get uuid(){return this.__internal__.device.id}get typeDevice(){return this.__internal__.device.type}get queue(){return this.__internal__.serial.queue}get responseDelimited(){return this.__internal__.serial.response.delimited}set responseDelimited(e){if(typeof e!="boolean")throw new Error("responseDelimited must be a boolean");this.__internal__.serial.response.delimited=e}get responsePrefixLimited(){return this.__internal__.serial.response.prefixLimiter}set responsePrefixLimited(e){if(typeof e!="boolean")throw new Error("responsePrefixLimited must be a boolean");this.__internal__.serial.response.prefixLimiter=e}get responseSufixLimited(){return this.__internal__.serial.response.sufixLimiter}set responseSufixLimited(e){if(typeof e!="boolean")throw new Error("responseSufixLimited must be a boolean");this.__internal__.serial.response.sufixLimiter=e}get responseLimiter(){return this.__internal__.serial.response.limiter}set responseLimiter(e){if(typeof e!="string"&&!(e instanceof RegExp))throw new Error("responseLimiter must be a string or a RegExp");this.__internal__.serial.response.limiter=e}get fixedBytesMessage(){return this.__internal__.serial.response.length}set fixedBytesMessage(e){if(e!==null&&(typeof e!="number"||e<1))throw new Error("Invalid length for fixed bytes message");this.__internal__.serial.response.length=e}get timeoutBeforeResponseBytes(){return this.__internal__.serial.free_timeout_ms||50}set timeoutBeforeResponseBytes(e){if(e!==void 0&&(typeof e!="number"||e<1))throw new Error("Invalid timeout for response bytes");this.__internal__.serial.free_timeout_ms=e??50}get bypassSerialBytesConnection(){return this.__internal__.bypassSerialBytesConnection}set bypassSerialBytesConnection(e){if(typeof e!="boolean")throw new Error("bypassSerialBytesConnection must be a boolean");this.__internal__.bypassSerialBytesConnection=e}get useSocket(){return this.__internal__.serial.socket}get connectionBytes(){const e=this.__internal__.serial.bytes_connection;return e instanceof Uint8Array?e:typeof e=="string"?this.stringArrayToUint8Array(this.parseStringToBytes(e,"")):Array.isArray(e)&&typeof e[0]=="string"?this.stringArrayToUint8Array(e):Array.isArray(e)&&typeof e[0]=="number"?new Uint8Array(e):new Uint8Array([])}set portPath(e){if(this.isConnected)throw new Error("Cannot change port path while connected");if(typeof e!="string"&&e!==null)throw new TypeError("vendorId must be string or null");this.__internal__.serial.portInfo.path=e}get portPath(){return this.__internal__.serial.portInfo.path}set portVendorId(e){if(this.isConnected)throw new Error("Cannot change port vendorId while connected");if(typeof e=="number"&&typeof e!="string"&&e!==null)throw new TypeError("vendorId must be a number, string or null");this.__internal__.serial.portInfo.vendorId=e}get portVendorId(){return this.__internal__.serial.portInfo.vendorId}set portProductId(e){if(this.isConnected)throw new Error("Cannot change port productId while connected");if(typeof e=="number"&&typeof e!="string"&&e!==null)throw new TypeError("productId must be a number, string or null");this.__internal__.serial.portInfo.productId=e}get portProductId(){return this.__internal__.serial.portInfo.productId}set socketPortParser(e){if(["byte-length","inter-byte-timeout"].includes(e))throw new TypeError("socketPortParser must be a string, either 'byte-length' or 'inter-byte-timeout'");this.__internal__.serial.portInfo.parser.name=e}get socketPortParser(){return this.__internal__.serial.portInfo.parser.name}set socketPortParserInterval(e){if(typeof e!="number"||e<1)throw new TypeError("Interval must be a positive number");this.__internal__.serial.portInfo.parser.interval=e}get socketPortParserInterval(){return this.__internal__.serial.portInfo.parser.interval||50}set socketPortParserLength(e){if(typeof e!="number"||e<1)throw new TypeError("Length must be a positive number or null");this.__internal__.serial.portInfo.parser.length=e}get socketPortParserLength(){return this.__internal__.serial.portInfo.parser.length||14}get parserForSocket(){return this.socketPortParser==="byte-length"?{name:this.socketPortParser,length:this.socketPortParserLength}:{name:this.socketPortParser,interval:this.socketPortParserInterval}}get configDeviceSocket(){return{uuid:this.uuid,name:this.typeDevice,deviceNumber:this.deviceNumber,connectionBytes:Array.from(this.connectionBytes),config:{baudRate:this.__internal__.serial.config_port.baudRate,dataBits:this.__internal__.serial.config_port.dataBits,stopBits:this.__internal__.serial.config_port.stopBits,parity:this.__internal__.serial.config_port.parity,bufferSize:this.__internal__.serial.config_port.bufferSize,flowControl:this.__internal__.serial.config_port.flowControl},info:{vendorId:this.portVendorId,productId:this.portProductId,portName:this.portPath},response:{automatic:this.__internal__.auto_response,autoResponse:this.__internal__.serial.auto_response,parser:this.parserForSocket,timeout:{general:this.__internal__.time.response_general,engines:this.__internal__.time.response_engines,connection:this.__internal__.time.response_connection}}}}#i(e){return this.useSocket?this.__internal__.serial.connected&&c.isConnected():!!(e&&e.readable&&e.writable)}async timeout(e,t){this.__internal__.last_error.message="Operation response timed out.",this.__internal__.last_error.action=t,this.__internal__.last_error.code=e,this.__internal__.timeout.until_response&&(clearTimeout(this.__internal__.timeout.until_response),this.__internal__.timeout.until_response=0),t==="connect"?(this.__internal__.serial.connected=!1,this.dispatch("serial:reconnect",{}),i.$dispatchChange(this)):t==="connection:start"&&(await this.serialDisconnect(),this.__internal__.serial.connected=!1,this.__internal__.aux_port_connector+=1,i.$dispatchChange(this),await this.serialConnect()),this.__internal__.serial.queue.length>0&&this.dispatch("internal:queue",{}),this.dispatch("serial:timeout",{...this.__internal__.last_error,bytes:e,action:t})}async disconnect(e=null){await this.serialDisconnect(),this.#e(e)}#e(e=null){this.__internal__.serial.connected=!1,this.__internal__.aux_port_connector=0,this.dispatch("serial:disconnected",e),i.$dispatchChange(this)}#s(e){this.__internal__.serial.aux_connecting=e.detail.active?"connecting":"finished"}socketResponse(e){const t=this.__internal__.serial.connected;if(e.type==="disconnect"||e.type==="error"&&e.data==="DISCONNECTED"?this.__internal__.serial.connected=!1:e.type==="success"&&(this.__internal__.serial.connected=!0),i.$dispatchChange(this),!t&&this.__internal__.serial.connected&&(this.dispatch("serial:connected"),this.#o(!1)),e.type==="success")this.#r(new Uint8Array(e.data));else if(e.type==="error"){const n=new Error("The port is closed or is not readable/writable");this.serialErrors(n)}else e.type==="timeout"&&this.timeout(e.data.bytes??[],this.lastAction||"unknown");this.__internal__.serial.last_action=null}async connect(){return this.isConnected?!0:(this.__internal__.serial.aux_connecting="idle",new Promise((e,t)=>{this.#n||(this.#n=this.#s.bind(this)),this.on("internal:connecting",this.#n);const n=setInterval(()=>{this.__internal__.serial.aux_connecting==="finished"?(clearInterval(n),this.__internal__.serial.aux_connecting="idle",this.#n!==null&&this.off("internal:connecting",this.#n),this.isConnected?e(!0):t(`${this.typeDevice} device ${this.deviceNumber} not connected`)):this.__internal__.serial.aux_connecting==="connecting"&&(this.__internal__.serial.aux_connecting="idle",this.dispatch("internal:connecting",{active:!0}),this.dispatch("serial:connecting",{active:!0}))},100);this.serialConnect()}))}async serialDisconnect(){try{if(this.useSocket)c.isConnected()&&c.disconnectDevice(this.configDeviceSocket);else{const e=this.__internal__.serial.reader,t=this.__internal__.serial.output_stream;e&&(await e.cancel().catch(s=>this.serialErrors(s)),await this.__internal__.serial.input_done),t&&(await t.getWriter().close(),await this.__internal__.serial.output_done),this.__internal__.serial.connected&&this.__internal__.serial&&this.__internal__.serial.port&&await this.__internal__.serial.port.close()}}catch(e){this.serialErrors(e)}finally{this.__internal__.serial.reader=null,this.__internal__.serial.input_done=null,this.__internal__.serial.output_stream=null,this.__internal__.serial.output_done=null,this.__internal__.serial.connected=!1,this.__internal__.serial.port=null,i.$dispatchChange(this)}}async#a(e){if(c.isDisconnected())throw this.#e({error:"Socket is disconnected."}),new Error("The socket is disconnected");if(this.isDisconnected)throw this.#e({error:"Port is closed, not readable or writable."}),new Error("The port is closed or is not readable/writable");const t=this.validateBytes(e);c.write({config:this.configDeviceSocket,bytes:Array.from(t)})}async#t(e){if(this.useSocket){await this.#a(e);return}const t=this.__internal__.serial.port;if(!t||t&&(!t.readable||!t.writable))throw this.#e({error:"Port is closed, not readable or writable."}),new Error("The port is closed or is not readable/writable");const n=this.validateBytes(e);if(this.useRTSCTS&&await this.#l(t,5e3),t.writable===null)return;const s=t.writable.getWriter();await s.write(n),s.releaseLock()}async#l(e,t=5e3){const n=Date.now();for(;;){if(Date.now()-n>t)throw new Error("Timeout waiting for clearToSend signal");const{clearToSend:s}=await e.getSignals();if(s)return;await b(100)}}#r(e=new Uint8Array([]),t=!1){if(e&&e.length>0){const n=this.__internal__.serial.connected;if(this.__internal__.serial.connected=this.#i(this.__internal__.serial.port),i.$dispatchChange(this),!n&&this.__internal__.serial.connected&&(this.dispatch("serial:connected"),this.#o(!1)),this.__internal__.interval.reconnection&&(clearInterval(this.__internal__.interval.reconnection),this.__internal__.interval.reconnection=0),this.__internal__.timeout.until_response&&(clearTimeout(this.__internal__.timeout.until_response),this.__internal__.timeout.until_response=0),this.__internal__.serial.response.as==="hex")t?this.serialCorruptMessage(this.parseUint8ToHex(e)):this.serialMessage(this.parseUint8ToHex(e));else if(this.__internal__.serial.response.as==="uint8")t?this.serialCorruptMessage(e):this.serialMessage(e);else if(this.__internal__.serial.response.as==="string"){const s=this.parseUint8ArrayToString(e);if(this.__internal__.serial.response.limiter!==null){const a=s.split(this.__internal__.serial.response.limiter);for(const r in a)a[r]&&(t?this.serialCorruptMessage(a[r]):this.serialMessage(a[r]))}else t?this.serialCorruptMessage(s):this.serialMessage(s)}else{const s=this.stringToArrayBuffer(this.parseUint8ArrayToString(e));t?this.serialCorruptMessage(s):this.serialMessage(s)}}if(this.__internal__.serial.queue.length===0){this.__internal__.serial.running_queue=!1;return}this.dispatch("internal:queue",{})}getResponseAsArrayBuffer(){this.__internal__.serial.response.as="arraybuffer"}getResponseAsArrayHex(){this.__internal__.serial.response.as="hex"}getResponseAsUint8Array(){this.__internal__.serial.response.as="uint8"}getResponseAsString(){this.__internal__.serial.response.as="string"}async#_(){const e=this.serialFilters,t=await navigator.serial.getPorts({filters:e});return e.length===0?t:t.filter(s=>{const a=s.getInfo();return e.some(r=>a.usbProductId===r.usbProductId&&a.usbVendorId===r.usbVendorId)}).filter(s=>!this.#i(s))}async serialPortsSaved(e){const t=this.serialFilters;if(this.__internal__.aux_port_connector<e.length){const n=this.__internal__.aux_port_connector;this.__internal__.serial.port=e[n]}else this.__internal__.aux_port_connector=0,this.__internal__.serial.port=await navigator.serial.requestPort({filters:t});if(!this.__internal__.serial.port)throw new Error("Select another port please")}serialErrors(e){const t=e.toString().toLowerCase();switch(!0){case t.includes("must be handling a user gesture to show a permission request"):case t.includes("the port is closed."):case t.includes("the port is closed or is not writable"):case t.includes("the port is closed or is not readable"):case t.includes("the port is closed or is not readable/writable"):case t.includes("select another port please"):case t.includes("no port selected by the user"):case t.includes("this readable stream reader has been released and cannot be used to cancel its previous owner stream"):this.dispatch("serial:need-permission",{}),i.$dispatchChange(this);break;case t.includes("the port is already open."):case t.includes("failed to open serial port"):this.serialDisconnect().then(async()=>{this.__internal__.aux_port_connector+=1,await this.serialConnect()});break;case t.includes("cannot read properties of undefined (reading 'writable')"):case t.includes("cannot read properties of null (reading 'writable')"):case t.includes("cannot read property 'writable' of null"):case t.includes("cannot read property 'writable' of undefined"):this.serialDisconnect().then(async()=>{await this.serialConnect()});break;case t.includes("'close' on 'serialport': a call to close() is already in progress."):break;case t.includes("failed to execute 'open' on 'serialport': a call to open() is already in progress."):break;case t.includes("the port is already closed."):break;case t.includes("the device has been lost"):this.dispatch("serial:lost",{}),i.$dispatchChange(this);break;case t.includes("navigator.serial is undefined"):this.dispatch("serial:unsupported",{});break;default:console.error(e);break}this.dispatch("serial:error",e)}#c(e){if(e){const t=this.__internal__.serial.response.buffer,n=new Uint8Array(t.length+e.byteLength);n.set(t,0),n.set(new Uint8Array(e),t.length),this.__internal__.serial.response.buffer=n}}async#h(){this.__internal__.serial.time_until_send_bytes&&(clearTimeout(this.__internal__.serial.time_until_send_bytes),this.__internal__.serial.time_until_send_bytes=0),this.__internal__.serial.time_until_send_bytes=setTimeout(()=>{this.__internal__.serial.response.buffer&&this.#r(this.__internal__.serial.response.buffer),this.__internal__.serial.response.buffer=new Uint8Array(0)},this.__internal__.serial.free_timeout_ms||50)}async#u(){const e=this.__internal__.serial.response.length;let t=this.__internal__.serial.response.buffer;if(this.__internal__.serial.time_until_send_bytes&&(clearTimeout(this.__internal__.serial.time_until_send_bytes),this.__internal__.serial.time_until_send_bytes=0),!(e===null||!t||t.length===0)){for(;t.length>=e;){const n=t.slice(0,e);this.#r(n),t=t.slice(e)}this.__internal__.serial.response.buffer=t,t.length>0&&(this.__internal__.serial.time_until_send_bytes=setTimeout(()=>{this.#r(this.__internal__.serial.response.buffer,!0)},this.__internal__.serial.free_timeout_ms||50))}}async#d(){const{limiter:e,prefixLimiter:t=!1,sufixLimiter:n=!0}=this.__internal__.serial.response;if(!e)throw new Error("No limiter defined for delimited serial response");const s=this.__internal__.serial.response.buffer;if(!e||!s||s.length===0)return;this.__internal__.serial.time_until_send_bytes&&(clearTimeout(this.__internal__.serial.time_until_send_bytes),this.__internal__.serial.time_until_send_bytes=0);let r=new TextDecoder().decode(s);const f=[];if(typeof e=="string"){let _;if(t&&n)_=new RegExp(`${e}([^${e}]+)${e}`,"g");else if(t)_=new RegExp(`${e}([^${e}]*)`,"g");else if(n)_=new RegExp(`([^${e}]+)${e}`,"g");else return;let u,h=0;for(;(u=_.exec(r))!==null;)f.push(new TextEncoder().encode(u[1])),h=_.lastIndex;r=r.slice(h)}else if(e instanceof RegExp){let _,u=0;if(t&&n){const h=new RegExp(`${e.source}(.*?)${e.source}`,"g");for(;(_=h.exec(r))!==null;)f.push(new TextEncoder().encode(_[1])),u=h.lastIndex}else if(n)for(;(_=e.exec(r))!==null;){const h=_.index,m=r.slice(u,h);f.push(new TextEncoder().encode(m)),u=e.lastIndex}else if(t){const h=r.split(e);h.shift();for(const m of h)f.push(new TextEncoder().encode(m));r=""}r=r.slice(u)}for(const _ of f)this.#r(_);const E=new TextEncoder().encode(r);this.__internal__.serial.response.buffer=E,E.length>0&&(this.__internal__.serial.time_until_send_bytes=setTimeout(()=>{this.#r(this.__internal__.serial.response.buffer,!0),this.__internal__.serial.response.buffer=new Uint8Array(0)},this.__internal__.serial.free_timeout_ms??50))}async#f(){const e=this.__internal__.serial.port;if(!e||!e.readable)throw new Error("Port is not readable");const t=e.readable.getReader();this.__internal__.serial.reader=t;try{for(;this.__internal__.serial.keep_reading;){const{value:n,done:s}=await t.read();if(s)break;this.#c(n),this.__internal__.serial.response.delimited?await this.#d():this.__internal__.serial.response.length===null?await this.#h():await this.#u()}}catch(n){this.serialErrors(n)}finally{t.releaseLock(),this.__internal__.serial.keep_reading=!0,this.__internal__.serial.port&&await this.__internal__.serial.port.close()}}#o(e){e!==this.__internal__.serial.connecting&&(this.__internal__.serial.connecting=e,this.dispatch("serial:connecting",{active:e}),this.dispatch("internal:connecting",{active:e}))}async serialConnect(){try{if(this.#o(!0),this.useSocket){if(c.prepare(),this.__internal__.serial.last_action="connect",this.__internal__.timeout.until_response=setTimeout(async()=>{await this.timeout(this.__internal__.serial.bytes_connection??[],"connection:start")},this.__internal__.time.response_connection),c.isDisconnected())return;c.connectDevice(this.configDeviceSocket),this.dispatch("serial:sent",{action:"connect",bytes:this.__internal__.serial.bytes_connection})}else{const e=await this.#_();if(e.length>0)await this.serialPortsSaved(e);else{const s=this.serialFilters;this.__internal__.serial.port=await navigator.serial.requestPort({filters:s})}const t=this.__internal__.serial.port;if(!t)throw new Error("No port selected by the user");await t.open(this.serialConfigPort);const n=this;t.onconnect=s=>{n.dispatch("serial:connected",s),n.#o(!1),i.$dispatchChange(this),n.__internal__.serial.queue.length>0?n.dispatch("internal:queue",{}):n.__internal__.serial.running_queue=!1},t.ondisconnect=async()=>{await n.disconnect()},await b(this.__internal__.serial.delay_first_connection),this.__internal__.timeout.until_response=setTimeout(async()=>{await n.timeout(n.__internal__.serial.bytes_connection??[],"connection:start")},this.__internal__.time.response_connection),this.__internal__.serial.last_action="connect",await this.#t(this.__internal__.serial.bytes_connection??[]),this.dispatch("serial:sent",{action:"connect",bytes:this.__internal__.serial.bytes_connection}),this.__internal__.auto_response&&this.#r(this.__internal__.serial.auto_response),await this.#f()}}catch(e){this.#o(!1),this.serialErrors(e)}}async#p(){return typeof window>"u"?!1:"serial"in navigator&&"forget"in SerialPort.prototype&&this.__internal__.serial.port?(await this.__internal__.serial.port.forget(),!0):!1}async serialForget(){return await this.#p()}decToHex(e){return typeof e=="string"&&(e=parseInt(e,10)),e.toString(16)}hexToDec(e){return parseInt(e,16)}hexMaker(e="00",t=2){return e.toString().padStart(t,"0").toLowerCase()}add0x(e){const t=[];return e.forEach((n,s)=>{t[s]="0x"+n}),t}bytesToHex(e){return this.add0x(Array.from(e,t=>this.hexMaker(t)))}#g(){["serial:connected","serial:connecting","serial:reconnect","serial:timeout","serial:disconnected","serial:sent","serial:soft-reload","serial:message","serial:corrupt-message","unknown","serial:need-permission","serial:lost","serial:unsupported","serial:error","debug"].forEach(t=>{this.serialRegisterAvailableListener(t)})}#y(){const e=this;this.on("internal:queue",async()=>{await e.#w()});const t=()=>{e.isConnected&&e.#e({error:"Socket disconnected."})},n=()=>{e.isDisconnected&&!e.isConnecting&&e.serialConnect().catch(()=>{})};this.useSocket&&(window.addEventListener("serial:socket:disconnected",t),window.addEventListener("serial:socket:connected",n)),this.#m()}#m(){const e=this;navigator.serial.addEventListener("connect",async()=>{e.isDisconnected&&await e.serialConnect().catch(()=>{})})}async#w(){if(this.useSocket&&c.isDisconnected())return;if(!this.#i(this.__internal__.serial.port)){this.#e({error:"Port is closed, not readable or writable."}),await this.serialConnect();return}if(this.__internal__.timeout.until_response)return;if(this.__internal__.serial.queue.length===0){this.__internal__.serial.running_queue=!1;return}this.__internal__.serial.running_queue=!0;const e=this.__internal__.serial.queue[0];let t=this.__internal__.time.response_general;if(e.action==="connect"&&(t=this.__internal__.time.response_connection),this.__internal__.timeout.until_response=setTimeout(async()=>{await this.timeout(e.bytes,e.action)},t),this.__internal__.serial.last_action=e.action??"unknown",await this.#t(e.bytes),this.dispatch("serial:sent",{action:e.action,bytes:e.bytes}),this.__internal__.auto_response){let s=new Uint8Array(0);try{s=this.validateBytes(this.__internal__.serial.auto_response)}catch(a){this.serialErrors(a)}this.#r(s)}const n=[...this.__internal__.serial.queue];this.__internal__.serial.queue=n.splice(1),this.__internal__.serial.queue.length>0&&(this.__internal__.serial.running_queue=!0)}validateBytes(e){let t=new Uint8Array(0);if(e instanceof Uint8Array)t=e;else if(typeof e=="string")t=this.parseStringToTextEncoder(e);else if(Array.isArray(e)&&typeof e[0]=="string")t=this.stringArrayToUint8Array(e);else if(Array.isArray(e)&&typeof e[0]=="number")t=new Uint8Array(e);else throw new Error("Invalid data type");return t}async appendToQueue(e,t){const n=this.validateBytes(e);if(["connect","connection:start"].includes(t)){if(this.__internal__.serial.connected)return;await this.serialConnect();return}this.__internal__.serial.queue.push({bytes:n,action:t}),this.dispatch("internal:queue",{})}#b(e=1){this.__internal__.device_number=e,!this.__internal__.bypassSerialBytesConnection&&(this.__internal__.serial.bytes_connection=this.serialSetConnectionConstant(e))}serialSetConnectionConstant(e=1){if(this.__internal__.bypassSerialBytesConnection)return this.__internal__.serial.bytes_connection;throw new Error(`Method not implemented 'serialSetConnectionConstant' to listen on channel ${e}`)}serialMessage(e){throw console.log(e),this.dispatch("serial:message",{code:e}),new Error("Method not implemented 'serialMessage'")}serialCorruptMessage(e){throw console.log(e),this.dispatch("serial:corrupt-message",{code:e}),new Error("Method not implemented 'serialCorruptMessage'")}#C(){this.__internal__.last_error={message:null,action:null,code:null,no_code:0}}clearSerialQueue(){this.__internal__.serial.queue=[]}sumHex(e){let t=0;return e.forEach(n=>{t+=parseInt(n,16)}),t.toString(16)}toString(){return JSON.stringify({__class:this.typeDevice,device_number:this.deviceNumber,uuid:this.uuid,connected:this.isConnected,connection:this.__internal__.serial.bytes_connection})}softReload(){this.#C(),this.dispatch("serial:soft-reload",{})}async sendConnect(){if(!this.__internal__.serial.bytes_connection)throw new Error("No connection bytes defined");await this.appendToQueue(this.__internal__.serial.bytes_connection,"connect")}async sendCustomCode({code:e=[]}={code:[]}){if(!e)throw new Error("No data to send");this.__internal__.bypassSerialBytesConnection&&(this.__internal__.serial.bytes_connection=this.validateBytes(e)),await this.appendToQueue(e,"custom")}stringToArrayHex(e){return Array.from(e).map(t=>t.charCodeAt(0).toString(16))}stringToArrayBuffer(e,t=`
`){return this.parseStringToTextEncoder(e,t).buffer}parseStringToTextEncoder(e="",t=`
`){const n=new TextEncoder;return e+=t,n.encode(e)}parseStringToBytes(e="",t=`
`){const n=this.parseStringToTextEncoder(e,t);return Array.from(n).map(s=>s.toString(16))}parseUint8ToHex(e){return Array.from(e).map(t=>t.toString(16).padStart(2,"0").toLowerCase())}parseHexToUint8(e){return new Uint8Array(e.map(t=>parseInt(t,16)))}stringArrayToUint8Array(e){const t=[];return typeof e=="string"?this.parseStringToTextEncoder(e).buffer:(e.forEach(n=>{const s=n.replace("0x","");t.push(parseInt(s,16))}),new Uint8Array(t))}parseUint8ArrayToString(e){let t=new Uint8Array(0);e instanceof Uint8Array?t=e:t=this.stringArrayToUint8Array(e),e=this.parseUint8ToHex(t);const n=e.map(s=>parseInt(s,16));return this.__internal__.serial.response.replacer?String.fromCharCode(...n).replace(this.__internal__.serial.response.replacer,""):String.fromCharCode(...n)}hexToAscii(e){const t=e.toString();let n="";for(let s=0;s<t.length;s+=2)n+=String.fromCharCode(parseInt(t.substring(s,2),16));return n}asciiToHex(e){const t=[];for(let n=0,s=e.length;n<s;n++){const a=Number(e.charCodeAt(n)).toString(16);t.push(a)}return t.join("")}$checkAndDispatchConnection(){return this.isConnected}}var C=(o=>(o.CONNECTION_FAILED="CONNECTION_FAILED",o.DISCONNECTION_FAILED="DISCONNECTION_FAILED",o.WRITE_FAILED="WRITE_FAILED",o.READ_FAILED="READ_FAILED",o.TIMEOUT="TIMEOUT",o.PORT_NOT_FOUND="PORT_NOT_FOUND",o.PERMISSION_DENIED="PERMISSION_DENIED",o.DEVICE_NOT_SUPPORTED="DEVICE_NOT_SUPPORTED",o.INVALID_CONFIGURATION="INVALID_CONFIGURATION",o.SOCKET_ERROR="SOCKET_ERROR",o.UNKNOWN_ERROR="UNKNOWN_ERROR",o))(C||{});class y extends Error{code;context;timestamp;constructor(e,t="UNKNOWN_ERROR",n){super(e),this.name="SerialError",this.code=t,this.context=n,this.timestamp=new Date,Error.captureStackTrace&&Error.captureStackTrace(this,y)}toJSON(){return{name:this.name,message:this.message,code:this.code,context:this.context,timestamp:this.timestamp.toISOString(),stack:this.stack}}toString(){const e=this.context?` | Context: ${JSON.stringify(this.context)}`:"";return`${this.name} [${this.code}]: ${this.message}${e}`}}l.Core=v,l.Devices=i,l.Dispatcher=p,l.SerialError=y,l.SerialErrorCode=C,l.Socket=c,Object.defineProperty(l,Symbol.toStringTag,{value:"Module"})}));
//# sourceMappingURL=webserial-core.umd.cjs.map