ota-hub-reactjs 0.1.2 → 0.1.4

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.
@@ -6,7 +6,6 @@ export type ESP32ConnectionState = DeviceConnectionState & {
6
6
  transport?: Transport;
7
7
  esp?: ESPLoader;
8
8
  debugLogging?: false;
9
- reader?: AsyncGenerator<Uint8Array>;
10
9
  slipReadWrite?: boolean;
11
10
  isFlashing: boolean;
12
11
  flashProgress: number;
@@ -36,7 +36,7 @@ export function useESP32MultiDeviceWhisperer({ releasePortByDefault, ...props }
36
36
  let inSlipFrame = false; // are we inside a SLIP frame?
37
37
  let escapeNext = false;
38
38
  const reader = transport.rawRead();
39
- base.updateConnection(uuid, (c) => ({ ...c, reader }));
39
+ base.updateConnection(uuid, (c) => ({ ...c }));
40
40
  try {
41
41
  while (true) {
42
42
  const conn = base.getConnection(uuid);
@@ -44,6 +44,8 @@ export function useESP32MultiDeviceWhisperer({ releasePortByDefault, ...props }
44
44
  console.log("Kack!");
45
45
  return;
46
46
  }
47
+ if (conn.isFlashing || !conn.isConnected)
48
+ break;
47
49
  const { value, done } = await reader.next();
48
50
  if (done || !value)
49
51
  break;
@@ -114,6 +116,7 @@ export function useESP32MultiDeviceWhisperer({ releasePortByDefault, ...props }
114
116
  }
115
117
  }
116
118
  }
119
+ await new Promise((resolve) => setTimeout(resolve, 100)); // Little breather before attempt any disconnects
117
120
  }
118
121
  catch (e) {
119
122
  base.appendLog(uuid, {
@@ -122,13 +125,11 @@ export function useESP32MultiDeviceWhisperer({ releasePortByDefault, ...props }
122
125
  });
123
126
  }
124
127
  finally {
125
- // Clear reader state so it's fresh for next connect
126
- base.updateConnection(uuid, (c) => ({ ...c, reader: undefined }));
127
- base.appendLog(uuid, {
128
- level: 0,
129
- message: "[!] Serial disconnected",
130
- });
131
- await disconnect(uuid);
128
+ base.updateConnection(uuid, (c) => ({ ...c }));
129
+ const conn = base.getConnection(uuid);
130
+ if (conn?.isConnected) {
131
+ await disconnect(uuid);
132
+ }
132
133
  }
133
134
  };
134
135
  const restartDevice = async (uuid, default_transport) => {
@@ -155,21 +156,6 @@ export function useESP32MultiDeviceWhisperer({ releasePortByDefault, ...props }
155
156
  });
156
157
  base.updateConnection(uuid, (c) => ({ ...c, port }));
157
158
  }
158
- else {
159
- try {
160
- const oldInfo = port.getInfo();
161
- const authorizedPorts = await navigator.serial.getPorts();
162
- const freshPort = authorizedPorts.find((p) => p.getInfo().usbVendorId === oldInfo.usbVendorId &&
163
- p.getInfo().usbProductId === oldInfo.usbProductId);
164
- if (freshPort) {
165
- port = freshPort; // Swap out the dead pointer
166
- base.updateConnection(uuid, (c) => ({ ...c, port }));
167
- }
168
- }
169
- catch (e) {
170
- console.warn(`[${uuid}] Failed to silently heal port reference:`, e);
171
- }
172
- }
173
159
  base.updateConnection(uuid, (c) => ({ ...c, isConnecting: true }));
174
160
  const use_baudrate = baudrate ?? conn.baudrate ?? 115200;
175
161
  const transport = new Transport(port, false, false);
@@ -177,7 +163,7 @@ export function useESP32MultiDeviceWhisperer({ releasePortByDefault, ...props }
177
163
  const esploader = new ESPLoader({
178
164
  transport,
179
165
  baudrate: use_baudrate,
180
- enableTracing: false,
166
+ enableTracing: !!conn.debugLogging,
181
167
  debugLogging: !!conn.debugLogging,
182
168
  });
183
169
  try {
@@ -230,29 +216,9 @@ export function useESP32MultiDeviceWhisperer({ releasePortByDefault, ...props }
230
216
  };
231
217
  const disconnect = async (uuid, timeout = 2000) => {
232
218
  const conn = base.getConnection(uuid);
233
- // 1. Politely kill our high-level generator
234
- if (conn?.reader && typeof conn.reader.return === "function") {
235
- try {
236
- // @ts-ignore
237
- await conn.reader.return();
238
- }
239
- catch (e) {
240
- console.warn(`[${uuid}] Error returning generator lock:`, e);
241
- }
242
- }
243
219
  if (conn?.transport) {
244
220
  try {
245
- // 2. AGGRESSIVE LOCK REMOVAL (The Fix)
246
- // Dig into esptool-js's internal state and force the native reader to release.
247
- // If we don't do this, transport.disconnect() will hang and fail to close the port.
248
- const internalReader = conn.transport.reader;
249
- if (internalReader) {
250
- await internalReader.cancel().catch(() => { });
251
- if (typeof internalReader.releaseLock === "function") {
252
- internalReader.releaseLock();
253
- }
254
- }
255
- // 3. Now that locks are cleared, attempt standard disconnect
221
+ // Attempt disconnect, but don’t hang if the port is crashed
256
222
  await Promise.race([
257
223
  conn.transport.disconnect(),
258
224
  new Promise((resolve) => setTimeout(resolve, timeout)),
@@ -262,32 +228,20 @@ export function useESP32MultiDeviceWhisperer({ releasePortByDefault, ...props }
262
228
  console.warn(`[${uuid}] Serial Disconnect error:`, e);
263
229
  }
264
230
  }
265
- // 4. NATIVE SAFETY NET
266
- // Guarantee the port is closed natively just in case the transport failed
267
- if (conn?.port) {
268
- try {
269
- if (conn.port.readable?.locked) {
270
- await conn.port.readable.cancel().catch(() => { });
271
- }
272
- if (conn.port.writable?.locked) {
273
- await conn.port.writable.abort().catch(() => { });
274
- }
275
- await conn.port.close().catch(() => { });
276
- }
277
- catch (e) {
278
- // It will throw if already closed, which is perfectly fine.
279
- }
280
- }
281
- // 5. Always clear the transport and reset connection state
231
+ // Always clear the transport and reset connection state
282
232
  base.updateConnection(uuid, (c) => ({
283
233
  ...c,
284
- port: releasePortByDefault ? undefined : c.port,
285
- transport: releasePortByDefault ? undefined : c.transport,
286
- reader: undefined, // Guarantee reader is cleared out
234
+ port: releasePortByDefault ? null : c.port,
235
+ transport: releasePortByDefault ? null : c.transport,
236
+ esp: releasePortByDefault ? null : c.transport,
287
237
  isConnected: false,
288
238
  isConnecting: false,
289
239
  autoConnect: false,
290
240
  }));
241
+ base.appendLog(uuid, {
242
+ level: 0,
243
+ message: "[!] Serial disconnected",
244
+ });
291
245
  await conn?.onDisconnect?.();
292
246
  };
293
247
  const addConnection = async ({ uuid, propCreator, }) => {
@@ -362,27 +316,25 @@ export function useESP32MultiDeviceWhisperer({ releasePortByDefault, ...props }
362
316
  // 1. Bail if we don't have the required state
363
317
  if (!conn || !conn.port || assetsToFlash.length === 0)
364
318
  return;
365
- // Gracefully cancel reader and completely drop the transport natively
366
- await disconnect(uuid);
367
- await new Promise((resolve) => setTimeout(resolve, 400));
319
+ const prevAutoConnect = conn.autoConnect;
368
320
  base.updateConnection(uuid, (c) => ({
369
321
  ...c,
370
- port: conn.port, // Restore the port into state in case disconnect wiped it
371
322
  isFlashing: true,
372
323
  flashProgress: 0,
373
324
  flashError: undefined,
325
+ autoConnect: false,
374
326
  }));
375
- // Declare outside the try block (Fixes variable shadowing)
376
- let transport;
377
- let esploader;
327
+ if (conn.isConnected)
328
+ await disconnect(uuid);
329
+ await new Promise((resolve) => setTimeout(resolve, 400));
378
330
  try {
379
331
  // --- Connect ONCE ---
380
- transport = new Transport(conn.port, true);
381
- esploader = new ESPLoader({
332
+ const transport = new Transport(conn.port, true);
333
+ const esploader = new ESPLoader({
382
334
  transport,
383
335
  baudrate: 921600,
384
- enableTracing: false,
385
- // debugLogging: !!conn.debugLogging,
336
+ enableTracing: !!conn.debugLogging,
337
+ debugLogging: !!conn.debugLogging,
386
338
  });
387
339
  // This may fail, that'll bubble the failure up to the upper try/catch
388
340
  await esploader.main();
@@ -436,9 +388,8 @@ export function useESP32MultiDeviceWhisperer({ releasePortByDefault, ...props }
436
388
  ...c,
437
389
  isFlashing: false,
438
390
  flashProgress: 100,
391
+ autoConnect: prevAutoConnect,
439
392
  }));
440
- // --- Reconnect the standard monitor ---
441
- await connect(uuid);
442
393
  }
443
394
  catch (e) {
444
395
  console.error(`[${uuid}] Flashing failed:`, e);
@@ -448,6 +399,8 @@ export function useESP32MultiDeviceWhisperer({ releasePortByDefault, ...props }
448
399
  flashError: e?.message ?? "Unknown error",
449
400
  }));
450
401
  // Attempt recovery
402
+ }
403
+ finally {
451
404
  await connect(uuid);
452
405
  }
453
406
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ota-hub-reactjs",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "ReactJS tools for building web apps to flash MCU devices such as esp32, brought to you by OTA Hub.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",