@alexgyver/serial 1.0.9 → 1.0.11

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.
Files changed (3) hide show
  1. package/package.json +1 -1
  2. package/serial.js +41 -63
  3. package/serial.min.js +1 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alexgyver/serial",
3
- "version": "1.0.9",
3
+ "version": "1.0.11",
4
4
  "description": "Web Serial API wrapper",
5
5
  "main": "./serial.js",
6
6
  "module": "./serial.js",
package/serial.js CHANGED
@@ -8,14 +8,17 @@ export default class SerialJS {
8
8
  Closing: 'closing',
9
9
  };
10
10
 
11
+ //#region handlers
12
+ onbin = null;
11
13
  ontext = null;
12
- onbin(b) { }
14
+
13
15
  onopen() { }
14
16
  onclose() { }
15
17
  onchange(s) { }
16
18
  onselect(name) { }
17
19
  onerror(e) { }
18
20
 
21
+ //#region constructor
19
22
  constructor(params = {}) {
20
23
  const def = {
21
24
  eol: /\r?\n/,
@@ -23,21 +26,20 @@ export default class SerialJS {
23
26
  auto_open: false,
24
27
  reconnect: 1000,
25
28
  };
26
-
27
29
  this.cfg = { ...def, ...params };
28
30
 
31
+ this._setLastPort().then(() => this.onselect(this.getName()));
29
32
  this.splitter = new StreamSplitter(this.cfg.eol);
30
33
  this.splitter.ontext = (t) => {
31
34
  try {
32
- if (this.ontext) this.ontext(t);
35
+ this.ontext?.(t);
33
36
  } catch (e) {
34
37
  this._error(e);
35
38
  }
36
39
  };
37
-
38
- this._setLastPort().then(() => this.onselect(this.getName()));
39
40
  }
40
41
 
42
+ //#region methods
41
43
  config(params = {}) {
42
44
  this.cfg = { ...this.cfg, ...params };
43
45
  this.splitter.eol = this.cfg.eol;
@@ -48,7 +50,7 @@ export default class SerialJS {
48
50
  }
49
51
 
50
52
  opened() {
51
- return this._state === SerialJS.State.Open;
53
+ return this._state == SerialJS.State.Open;
52
54
  }
53
55
 
54
56
  selected() {
@@ -56,7 +58,7 @@ export default class SerialJS {
56
58
  }
57
59
 
58
60
  getName() {
59
- if (!this._port) return null;
61
+ if (!this._port) return 'None';
60
62
 
61
63
  switch (this._port.getInfo().usbProductId) {
62
64
  case 0x55d3: return 'CH343';
@@ -66,62 +68,56 @@ export default class SerialJS {
66
68
  case 0x0402: case 0x0403: case 0x0404: case 0x0405: case 0x6001: case 0x0602: case 0x6010: return 'FT232';
67
69
  case 0x9500: case 0x0102: case 0x0501: case 0x80a9: case 0xea60: case 0xea61: case 0xea63: return 'CP210x';
68
70
  }
69
-
70
71
  return 'Unknown';
71
72
  }
72
73
 
73
74
  async select() {
74
75
  try {
75
76
  await this.close();
76
-
77
77
  this._port = null;
78
-
79
- const ports = await navigator.serial.getPorts();
80
- for (const p of ports) await p.forget();
81
-
78
+ let ports = await navigator.serial.getPorts();
79
+ for (let p of ports) await p.forget();
82
80
  await sleep(50);
83
81
  await navigator.serial.requestPort();
84
82
  await this._setLastPort();
85
83
  } catch (e) {
86
84
  this._error(e);
87
85
  }
88
-
89
86
  this.onselect(this.getName());
90
-
91
87
  if (this.cfg.auto_open) this.open();
92
-
93
88
  return this.selected();
94
89
  }
95
90
 
96
91
  async open() {
97
- return this._lifecycle.runNothrow(async () => {
98
- const ports = await navigator.serial.getPorts();
99
- if (!ports.length) return false;
100
-
101
- if (this.cfg.reconnect) this.retry = true;
92
+ const ports = await navigator.serial.getPorts();
93
+ if (!ports.length) return false;
102
94
 
103
- await this._open();
95
+ if (this.cfg.reconnect) this.retry = true;
104
96
 
105
- return this.opened();
106
- });
97
+ this._open();
98
+ return true;
107
99
  }
108
-
109
100
  async _open() {
110
- if (this.opened() || this._state === SerialJS.State.Opening) return;
101
+ if (this._state !== SerialJS.State.Closed) return;
111
102
 
112
103
  this._change(SerialJS.State.Opening);
113
104
 
114
105
  try {
115
- await this._close();
116
106
  await this._setLastPort();
117
107
 
118
108
  if (!this._port) {
119
- this._change(SerialJS.State.Closed);
120
- return;
109
+ throw new Error('No port');
121
110
  }
122
111
 
112
+ if (this._state === SerialJS.State.Closing) return;
113
+
123
114
  await this._port.open({ baudRate: this.cfg.baud });
124
115
 
116
+ if (this._state === SerialJS.State.Closing) {
117
+ await this._port.close();
118
+ return;
119
+ }
120
+
125
121
  this.writer = this._port.writable.getWriter();
126
122
  this.reader = this._port.readable.getReader();
127
123
 
@@ -135,7 +131,7 @@ export default class SerialJS {
135
131
 
136
132
  if (value) {
137
133
  try {
138
- this.onbin(value);
134
+ this.onbin?.(value);
139
135
  } catch (e) {
140
136
  this._error(e);
141
137
  }
@@ -153,10 +149,6 @@ export default class SerialJS {
153
149
 
154
150
  this._sender.reset();
155
151
 
156
- if (this._state !== SerialJS.State.Closing) {
157
- this._change(SerialJS.State.Closing);
158
- }
159
-
160
152
  try {
161
153
  if (this.reader) this.reader.releaseLock();
162
154
  } catch (e) { }
@@ -175,25 +167,27 @@ export default class SerialJS {
175
167
  this._change(SerialJS.State.Closed);
176
168
 
177
169
  if (this.retry) {
178
- setTimeout(() => this._lifecycle.runNothrow(() => this._open()), this.cfg.reconnect);
170
+ setTimeout(() => {
171
+ if (this.retry) this._open();
172
+ }, this.cfg.reconnect);
179
173
  }
180
174
  }
181
175
 
182
176
  async close() {
183
- return this._lifecycle.runNothrow(async () => {
184
- this.retry = false;
185
- this._sender.reset();
186
- await this._close();
187
- return true;
188
- });
177
+ this.retry = false;
178
+ this._sender.reset();
179
+ await this._close();
180
+ return true;
189
181
  }
190
-
191
182
  async _close() {
192
183
  switch (this._state) {
193
184
  case SerialJS.State.Closed:
194
185
  return;
195
186
 
196
187
  case SerialJS.State.Opening:
188
+ this._change(SerialJS.State.Closing);
189
+ break;
190
+
197
191
  case SerialJS.State.Open:
198
192
  this._change(SerialJS.State.Closing);
199
193
 
@@ -204,13 +198,9 @@ export default class SerialJS {
204
198
  }
205
199
 
206
200
  break;
207
-
208
- case SerialJS.State.Closing:
209
- break;
210
201
  }
211
202
 
212
203
  let i = 0;
213
-
214
204
  while (this._state === SerialJS.State.Closing) {
215
205
  await sleep(10);
216
206
 
@@ -231,32 +221,25 @@ export default class SerialJS {
231
221
 
232
222
  return this._sender.runNothrow(async () => {
233
223
  if (!this.opened() || !this.writer) return false;
234
-
235
224
  await this.writer.write(data);
236
225
  return true;
237
226
  });
238
227
  }
239
228
 
229
+ //#region private
240
230
  _port = null;
241
- _state = SerialJS.State.Closed;
242
-
243
- reader = null;
244
- writer = null;
245
- retry = false;
246
-
247
231
  _decoder = new TextDecoder();
248
232
  _sender = new SerialExecutor();
249
- _lifecycle = new SerialExecutor();
233
+ _state = SerialJS.State.Closed;
250
234
 
251
235
  async _setLastPort() {
252
- const ports = await navigator.serial.getPorts();
236
+ let ports = await navigator.serial.getPorts();
253
237
  this._port = ports.length ? ports[0] : null;
254
238
  }
255
239
 
256
240
  _error(e) {
257
241
  this.onerror('[SerialJS] ' + e);
258
242
  }
259
-
260
243
  _change(s) {
261
244
  if (this._state === s) return;
262
245
 
@@ -264,13 +247,8 @@ export default class SerialJS {
264
247
  this.onchange(s);
265
248
 
266
249
  switch (s) {
267
- case SerialJS.State.Open:
268
- this.onopen();
269
- break;
270
-
271
- case SerialJS.State.Closed:
272
- this.onclose();
273
- break;
250
+ case SerialJS.State.Open: this.onopen(); break;
251
+ case SerialJS.State.Closed: this.onclose(); break;
274
252
  }
275
253
  }
276
254
  }
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"};ontext=null;onbin(t){}onopen(){}onclose(){}onchange(t){}onselect(t){}onerror(t){}constructor(t={}){this.cfg={eol:/\r?\n/,baud:115200,auto_open:!1,reconnect:1e3,...t},this.splitter=new e(this.cfg.eol),this.splitter.ontext=t=>{try{this.ontext&&this.ontext(t)}catch(t){this._error(t)}},this._setLastPort().then(()=>this.onselect(this.getName()))}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}getName(){if(!this._port)return null;switch(this._port.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(),this._port=null;const e=await navigator.serial.getPorts();for(const t of e)await t.forget();await t(50),await navigator.serial.requestPort(),await this._setLastPort()}catch(t){this._error(t)}return this.onselect(this.getName()),this.cfg.auto_open&&this.open(),this.selected()}async open(){return this._lifecycle.runNothrow(async()=>!!(await navigator.serial.getPorts()).length&&(this.cfg.reconnect&&(this.retry=!0),await this._open(),this.opened()))}async _open(){if(!this.opened()&&this._state!==r.State.Opening){this._change(r.State.Opening);try{if(await this._close(),await this._setLastPort(),!this._port)return void this._change(r.State.Closed);for(await this._port.open({baudRate:this.cfg.baud}),this.writer=this._port.writable.getWriter(),this.reader=this._port.readable.getReader(),this._sender.reset(),this._change(r.State.Open);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)}this._sender.reset(),this._state!==r.State.Closing&&this._change(r.State.Closing);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._lifecycle.runNothrow(()=>this._open()),this.cfg.reconnect)}}async close(){return this._lifecycle.runNothrow(async()=>(this.retry=!1,this._sender.reset(),await this._close(),!0))}async _close(){switch(this._state){case r.State.Closed:return;case r.State.Opening:case r.State.Open:if(this._change(r.State.Closing),this.reader)try{await this.reader.cancel()}catch(t){}case r.State.Closing:}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){return!(!this.opened()||!this.writer)&&this._sender.runNothrow(async()=>!(!this.opened()||!this.writer||(await this.writer.write(t),0)))}_port=null;_state=r.State.Closed;reader=null;writer=null;retry=!1;_decoder=new TextDecoder;_sender=new s;_lifecycle=new s;async _setLastPort(){const t=await navigator.serial.getPorts();this._port=t.length?t[0]: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,auto_open:!1,reconnect:1e3,...t},this._setLastPort().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}getName(){if(!this._port)return"None";switch(this._port.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(),this._port=null;let e=await navigator.serial.getPorts();for(let t of e)await t.forget();await t(50),await navigator.serial.requestPort(),await this._setLastPort()}catch(t){this._error(t)}return this.onselect(this.getName()),this.cfg.auto_open&&this.open(),this.selected()}async open(){return!!(await navigator.serial.getPorts()).length&&(this.cfg.reconnect&&(this.retry=!0),this._open(),!0)}async _open(){if(this._state===r.State.Closed){this._change(r.State.Opening);try{if(await this._setLastPort(),!this._port)throw new Error("No port");if(this._state===r.State.Closing)return;if(await this._port.open({baudRate:this.cfg.baud}),this._state===r.State.Closing)return void await this._port.close();for(this.writer=this._port.writable.getWriter(),this.reader=this._port.readable.getReader(),this._sender.reset(),this._change(r.State.Open);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)}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){return!(!this.opened()||!this.writer)&&this._sender.runNothrow(async()=>!(!this.opened()||!this.writer||(await this.writer.write(t),0)))}_port=null;_decoder=new TextDecoder;_sender=new s;_state=r.State.Closed;async _setLastPort(){let t=await navigator.serial.getPorts();this._port=t.length?t[0]: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};