@alexgyver/serial 1.0.12 → 1.0.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -18,6 +18,11 @@ constructor(params = {});
18
18
  config(params = {});
19
19
  // eol: /\r?\n/
20
20
  // baud: 115200
21
+ // dataBits: 8
22
+ // stopBits: 1
23
+ // parity: 'none'
24
+ // bufferSize: 255
25
+ // flowControl: 'none'
21
26
  // auto_open: false
22
27
  // reconnect: 1000
23
28
  // forgetOtherPorts: true
@@ -55,3 +60,19 @@ sendText(text);
55
60
  - `getPorts()` возвращает все ранее разрешённые порты
56
61
  - `restore()` восстанавливает порт без системного диалога, если выбор однозначен
57
62
  - `forget()` закрывает текущий порт и отзывает только его разрешение
63
+
64
+ ## Настройки порта
65
+
66
+ При открытии библиотека передаёт в `SerialPort.open()` параметры `baud`, `dataBits`, `stopBits`, `parity`, `bufferSize` и `flowControl`. Например:
67
+
68
+ ```js
69
+ const serial = new SerialJS({
70
+ baud: 115200,
71
+ bufferSize: 4096,
72
+ flowControl: 'hardware',
73
+ });
74
+ ```
75
+
76
+ `flowControl: 'hardware'` включает RTS/CTS и работает только при поддержке со стороны порта и физического подключения. Значение по умолчанию — `'none'`.
77
+
78
+ `sendBin()` последовательно выполняет отправки и ожидает Promise от `writer.write()`. Это использует backpressure Web Serial, но не является прикладным подтверждением обработки данных устройством.
package/README_EN.md CHANGED
@@ -3,9 +3,10 @@ This is an automatic translation and may be incorrect in some places. See the so
3
3
  # Serial.js
4
4
  Wrapper on Web Serial API
5
5
  - Automatic reconnection
6
- - Preservation of the last selected port
7
- - Shipment buffering
8
- - Reception buffering, separation of text by separator
6
+ - Restoration of a previously authorized port
7
+ - Safe handling of multiple authorized ports
8
+ - Sequential writes
9
+ - Buffered text reception and splitting
9
10
 
10
11
  [demo](https://gyverlibs.github.io/Serial.js/test/)
11
12
 
@@ -19,8 +20,14 @@ constructor(params = {});
19
20
  config(params = {});
20
21
  // eol: /\r?\n/
21
22
  // baud: 115200
23
+ // dataBits: 8
24
+ // stopBits: 1
25
+ // parity: 'none'
26
+ // bufferSize: 255
27
+ // flowControl: 'none'
22
28
  // auto_open: false
23
29
  // reconnect: 1000
30
+ // forgetOtherPorts: true
24
31
 
25
32
  onbin(b);
26
33
  ontext(t);
@@ -34,12 +41,30 @@ onerror(e);
34
41
  static supported();
35
42
  opened();
36
43
  selected();
44
+ getInfo();
37
45
  getName();
38
46
 
39
47
  select();
48
+ restore();
49
+ getPorts();
50
+ forget();
40
51
  open();
41
52
  close();
42
53
 
43
54
  sendBin(data);
44
55
  sendText(text);
45
56
  ```
57
+
58
+ The library passes `baud`, `dataBits`, `stopBits`, `parity`, `bufferSize`, and `flowControl` to `SerialPort.open()`. For example:
59
+
60
+ ```js
61
+ const serial = new SerialJS({
62
+ baud: 115200,
63
+ bufferSize: 4096,
64
+ flowControl: 'hardware',
65
+ });
66
+ ```
67
+
68
+ Hardware flow control uses RTS/CTS and only works when both the port and the physical connection support it. The default is `'none'`.
69
+
70
+ `sendBin()` serializes writes and awaits the Promise returned by `writer.write()`. This applies Web Serial backpressure, but it does not confirm that the remote application has processed the data.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alexgyver/serial",
3
- "version": "1.0.12",
3
+ "version": "1.0.13",
4
4
  "description": "Web Serial API wrapper",
5
5
  "main": "./serial.js",
6
6
  "module": "./serial.js",
@@ -14,7 +14,7 @@
14
14
  "url": "git+https://github.com/GyverLibs/Serial.js.git"
15
15
  },
16
16
  "devDependencies": {
17
- "webpack": "^5.110.2",
17
+ "webpack": "^5.110.3",
18
18
  "webpack-cli": "^7.2.3",
19
19
  "webpack-dev-server": "^6.0.0",
20
20
  "style-loader": "^4.0.0",
package/serial.js CHANGED
@@ -23,6 +23,11 @@ export default class SerialJS {
23
23
  const def = {
24
24
  eol: /\r?\n/,
25
25
  baud: 115200,
26
+ dataBits: 8,
27
+ stopBits: 1,
28
+ parity: 'none',
29
+ bufferSize: 255,
30
+ flowControl: 'none',
26
31
  auto_open: false,
27
32
  reconnect: 1000,
28
33
  forgetOtherPorts: true,
@@ -155,7 +160,7 @@ export default class SerialJS {
155
160
  return false;
156
161
  }
157
162
 
158
- await this._port.open({ baudRate: this.cfg.baud });
163
+ await this._port.open(this._openOptions());
159
164
 
160
165
  if (this._state === SerialJS.State.Closing) {
161
166
  await this._cleanup();
@@ -297,6 +302,17 @@ export default class SerialJS {
297
302
  _state = SerialJS.State.Closed;
298
303
  _openPromise = null;
299
304
 
305
+ _openOptions() {
306
+ return {
307
+ baudRate: this.cfg.baud,
308
+ dataBits: this.cfg.dataBits,
309
+ stopBits: this.cfg.stopBits,
310
+ parity: this.cfg.parity,
311
+ bufferSize: this.cfg.bufferSize,
312
+ flowControl: this.cfg.flowControl,
313
+ };
314
+ }
315
+
300
316
  _error(e) {
301
317
  this.onerror('[SerialJS] ' + e);
302
318
  }
package/serial.min.js CHANGED
@@ -1 +1 @@
1
- const t=t=>new Promise(e=>setTimeout(e,t));class e{ontext=null;constructor(t=/\r?\n/,e=!1){this.eol=t,this.skip=e}reset(){this._buf=""}write(t){if(!this.ontext)return;if(!this.eol)return void this.ontext(t);this._buf+=t;let e=this._buf.split(this.eol);1!=e.length&&(e[e.length-1].length?this._buf=e.pop():(this._buf="",e.pop()),this.skip&&(e.shift(),this.skip=!1),e.forEach(t=>this.ontext(t)))}_buf=""}class s{constructor(){this._session=0,this.reset()}run(t){const e=this._session,s=this._queue.then(()=>{if(e!==this._session)throw new Error("Queue empty");return t()});return this._queue=s.catch(()=>{}),s}runNothrow(t){return this.run(t).catch(()=>null)}reset(){this._session++,this._queue=Promise.resolve()}}class r{static State={Closed:"closed",Opening:"opening",Open:"open",Closing:"closing"};onbin=null;ontext=null;onopen(){}onclose(){}onchange(t){}onselect(t){}onerror(t){}constructor(t={}){this.cfg={eol:/\r?\n/,baud:115200,auto_open:!1,reconnect:1e3,forgetOtherPorts:!0,...t},this.restore().then(()=>this.onselect(this.getName())),this.splitter=new e(this.cfg.eol),this.splitter.ontext=t=>{try{this.ontext?.(t)}catch(t){this._error(t)}}}config(t={}){this.cfg={...this.cfg,...t},this.splitter.eol=this.cfg.eol}static supported(){return"serial"in navigator}opened(){return this._state==r.State.Open}selected(){return!!this._port}getInfo(){return this._port?.getInfo()??null}getName(){if(!this._port)return"None";switch(this.getInfo().usbProductId){case 21971:return"CH343";case 30084:return"CH340S";case 29986:case 29987:return"CH340";case 21778:case 21795:case 21892:return"CH341";case 1026:case 1027:case 1028:case 1029:case 24577:case 1538:case 24592:return"FT232";case 38144:case 258:case 1281:case 32937:case 6e4:case 60001:case 60003:return"CP210x"}return"Unknown"}async select(){try{await this.close();const t=await navigator.serial.requestPort();if(this.cfg.forgetOtherPorts){const e=await navigator.serial.getPorts();for(const s of e)s!==t&&await s.forget()}this._port=t}catch(t){this._error(t)}return this.onselect(this.getName()),this.cfg.auto_open?this.open():this.selected()}async getPorts(){return navigator.serial.getPorts()}async restore(){const t=await this.getPorts();return this._port=this.cfg.forgetOtherPorts?t[0]??null:1===t.length?t[0]:null,this.selected()}async forget(){if(await this.close(),!this._port)return!1;const t=this._port;this._port=null;try{return await t.forget(),this.onselect(this.getName()),!0}catch(e){return this._port=t,this._error(e),!1}}async open(){if(this.opened())return!0;if(this._openPromise)return this._openPromise;if(this._state!==r.State.Closed)return!1;this.cfg.reconnect&&(this.retry=!0);const t=this._connect();this._openPromise=t;const e=await t;return this._openPromise===t&&(this._openPromise=null),e}async _connect(){this._change(r.State.Opening);try{if(this._port||await this.restore(),!this._port)throw new Error("No port");return this._state===r.State.Closing?(await this._cleanup(),!1):(await this._port.open({baudRate:this.cfg.baud}),this._state===r.State.Closing?(await this._cleanup(),!1):(this.writer=this._port.writable.getWriter(),this.reader=this._port.readable.getReader(),this._sender.reset(),this._change(r.State.Open),this._listen(),!0))}catch(t){this._error(t)}return await this._cleanup(),!1}async _listen(){try{for(;this._state===r.State.Open;){const{value:t,done:e}=await this.reader.read();if(e)break;if(t){try{this.onbin?.(t)}catch(t){this._error(t)}this.ontext&&this.splitter.write(this._decoder.decode(t,{stream:!0}))}}}catch(t){this._error(t)}await this._cleanup()}async _cleanup(){this._sender.reset();try{this.reader&&this.reader.releaseLock()}catch(t){}try{this.writer&&this.writer.releaseLock()}catch(t){}this.reader=null,this.writer=null;try{this._port&&await this._port.close()}catch(t){}this._change(r.State.Closed),this.retry&&setTimeout(()=>{this.retry&&this.open()},this.cfg.reconnect)}async close(){return this.retry=!1,this._sender.reset(),await this._close(),!0}async _close(){switch(this._state){case r.State.Closed:return;case r.State.Opening:this._change(r.State.Closing);break;case r.State.Open:if(this._change(r.State.Closing),this.reader)try{await this.reader.cancel()}catch(t){}}let e=0;for(;this._state===r.State.Closing;)if(await t(10),++e>200){this._error("Close timeout"),this._change(r.State.Closed);break}}async sendText(t){return this.sendBin((new TextEncoder).encode(t))}async sendBin(t){if(!this.opened()||!this.writer)return!1;try{return await this._sender.run(async()=>!(!this.opened()||!this.writer||(await this.writer.write(t),0)))}catch(t){return this.opened()&&this._error(t),!1}}_port=null;_decoder=new TextDecoder;_sender=new s;_state=r.State.Closed;_openPromise=null;_error(t){this.onerror("[SerialJS] "+t)}_change(t){if(this._state!==t)switch(this._state=t,this.onchange(t),t){case r.State.Open:this.onopen();break;case r.State.Closed:this.onclose()}}}export{r as default};
1
+ const t=t=>new Promise(e=>setTimeout(e,t));class e{ontext=null;constructor(t=/\r?\n/,e=!1){this.eol=t,this.skip=e}reset(){this._buf=""}write(t){if(!this.ontext)return;if(!this.eol)return void this.ontext(t);this._buf+=t;let e=this._buf.split(this.eol);1!=e.length&&(e[e.length-1].length?this._buf=e.pop():(this._buf="",e.pop()),this.skip&&(e.shift(),this.skip=!1),e.forEach(t=>this.ontext(t)))}_buf=""}class s{constructor(){this._session=0,this.reset()}run(t){const e=this._session,s=this._queue.then(()=>{if(e!==this._session)throw new Error("Queue empty");return t()});return this._queue=s.catch(()=>{}),s}runNothrow(t){return this.run(t).catch(()=>null)}reset(){this._session++,this._queue=Promise.resolve()}}class r{static State={Closed:"closed",Opening:"opening",Open:"open",Closing:"closing"};onbin=null;ontext=null;onopen(){}onclose(){}onchange(t){}onselect(t){}onerror(t){}constructor(t={}){this.cfg={eol:/\r?\n/,baud:115200,dataBits:8,stopBits:1,parity:"none",bufferSize:255,flowControl:"none",auto_open:!1,reconnect:1e3,forgetOtherPorts:!0,...t},this.restore().then(()=>this.onselect(this.getName())),this.splitter=new e(this.cfg.eol),this.splitter.ontext=t=>{try{this.ontext?.(t)}catch(t){this._error(t)}}}config(t={}){this.cfg={...this.cfg,...t},this.splitter.eol=this.cfg.eol}static supported(){return"serial"in navigator}opened(){return this._state==r.State.Open}selected(){return!!this._port}getInfo(){return this._port?.getInfo()??null}getName(){if(!this._port)return"None";switch(this.getInfo().usbProductId){case 21971:return"CH343";case 30084:return"CH340S";case 29986:case 29987:return"CH340";case 21778:case 21795:case 21892:return"CH341";case 1026:case 1027:case 1028:case 1029:case 24577:case 1538:case 24592:return"FT232";case 38144:case 258:case 1281:case 32937:case 6e4:case 60001:case 60003:return"CP210x"}return"Unknown"}async select(){try{await this.close();const t=await navigator.serial.requestPort();if(this.cfg.forgetOtherPorts){const e=await navigator.serial.getPorts();for(const s of e)s!==t&&await s.forget()}this._port=t}catch(t){this._error(t)}return this.onselect(this.getName()),this.cfg.auto_open?this.open():this.selected()}async getPorts(){return navigator.serial.getPorts()}async restore(){const t=await this.getPorts();return this._port=this.cfg.forgetOtherPorts?t[0]??null:1===t.length?t[0]:null,this.selected()}async forget(){if(await this.close(),!this._port)return!1;const t=this._port;this._port=null;try{return await t.forget(),this.onselect(this.getName()),!0}catch(e){return this._port=t,this._error(e),!1}}async open(){if(this.opened())return!0;if(this._openPromise)return this._openPromise;if(this._state!==r.State.Closed)return!1;this.cfg.reconnect&&(this.retry=!0);const t=this._connect();this._openPromise=t;const e=await t;return this._openPromise===t&&(this._openPromise=null),e}async _connect(){this._change(r.State.Opening);try{if(this._port||await this.restore(),!this._port)throw new Error("No port");return this._state===r.State.Closing?(await this._cleanup(),!1):(await this._port.open(this._openOptions()),this._state===r.State.Closing?(await this._cleanup(),!1):(this.writer=this._port.writable.getWriter(),this.reader=this._port.readable.getReader(),this._sender.reset(),this._change(r.State.Open),this._listen(),!0))}catch(t){this._error(t)}return await this._cleanup(),!1}async _listen(){try{for(;this._state===r.State.Open;){const{value:t,done:e}=await this.reader.read();if(e)break;if(t){try{this.onbin?.(t)}catch(t){this._error(t)}this.ontext&&this.splitter.write(this._decoder.decode(t,{stream:!0}))}}}catch(t){this._error(t)}await this._cleanup()}async _cleanup(){this._sender.reset();try{this.reader&&this.reader.releaseLock()}catch(t){}try{this.writer&&this.writer.releaseLock()}catch(t){}this.reader=null,this.writer=null;try{this._port&&await this._port.close()}catch(t){}this._change(r.State.Closed),this.retry&&setTimeout(()=>{this.retry&&this.open()},this.cfg.reconnect)}async close(){return this.retry=!1,this._sender.reset(),await this._close(),!0}async _close(){switch(this._state){case r.State.Closed:return;case r.State.Opening:this._change(r.State.Closing);break;case r.State.Open:if(this._change(r.State.Closing),this.reader)try{await this.reader.cancel()}catch(t){}}let e=0;for(;this._state===r.State.Closing;)if(await t(10),++e>200){this._error("Close timeout"),this._change(r.State.Closed);break}}async sendText(t){return this.sendBin((new TextEncoder).encode(t))}async sendBin(t){if(!this.opened()||!this.writer)return!1;try{return await this._sender.run(async()=>!(!this.opened()||!this.writer||(await this.writer.write(t),0)))}catch(t){return this.opened()&&this._error(t),!1}}_port=null;_decoder=new TextDecoder;_sender=new s;_state=r.State.Closed;_openPromise=null;_openOptions(){return{baudRate:this.cfg.baud,dataBits:this.cfg.dataBits,stopBits:this.cfg.stopBits,parity:this.cfg.parity,bufferSize:this.cfg.bufferSize,flowControl:this.cfg.flowControl}}_error(t){this.onerror("[SerialJS] "+t)}_change(t){if(this._state!==t)switch(this._state=t,this.onchange(t),t){case r.State.Open:this.onopen();break;case r.State.Closed:this.onclose()}}}export{r as default};