@mega-yfue/eufy-sdk 0.3.0-beta.1 → 0.3.0-beta.3

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/dist/index.js CHANGED
@@ -5813,6 +5813,7 @@ var LIGHT_SWITCH_JSON_TYPES = /* @__PURE__ */ new Set([
5813
5813
  DeviceType.CAMERA_4G_S330
5814
5814
  ]);
5815
5815
  var LIGHT_SWITCH_INT_STRING_TYPES = /* @__PURE__ */ new Set([
5816
+ DeviceType.FLOODLIGHT_CAMERA_8423,
5816
5817
  DeviceType.INDOOR_OUTDOOR_CAMERA_1080P_NO_LIGHT,
5817
5818
  DeviceType.INDOOR_OUTDOOR_CAMERA_2K,
5818
5819
  DeviceType.INDOOR_OUTDOOR_CAMERA_1080P,
@@ -15140,6 +15141,8 @@ var LIVE_START_ACK_DEADLINE_MS = 3e3;
15140
15141
  var SEQUENCE_LOOKBACK = 32768;
15141
15142
  var STALE_RETRANSMIT_DEPTH = 1024;
15142
15143
  var PUNCH_PROBE_SOCKETS = 7;
15144
+ var REORDER_WAIT_MS = 250;
15145
+ var REORDER_MAX_DATAGRAMS = 128;
15143
15146
  var MAX_TRACED_DATAGRAM_GAPS = 8;
15144
15147
  var CMD_SET_PAYLOAD = 1350;
15145
15148
  var CMD_NOTIFY_PAYLOAD = 1351;
@@ -15209,9 +15212,10 @@ var P2PSession = class _P2PSession extends EventEmitter2 {
15209
15212
  selfHost;
15210
15213
  /** In-flight multi-datagram frame per data channel (see onData). */
15211
15214
  pendingByDataType = /* @__PURE__ */ new Map();
15212
- /** Last datagram sequence number seen per dataType — used to detect a lost/reordered datagram
15213
- * mid-frame and drop the (now unrecoverable) partial frame instead of splicing wrong bytes. */
15215
+ /** Last delivered datagram sequence number per data type. */
15214
15216
  lastSeqByType = /* @__PURE__ */ new Map();
15217
+ /** Held datagrams and their active gap timer, grouped by data type. */
15218
+ reorderByType = /* @__PURE__ */ new Map();
15215
15219
  tracedDatagramGaps = 0;
15216
15220
  level1Key;
15217
15221
  /** Negotiated 32-byte level-2/gateway key (AES-256-GCM). Set via setLevel2Key once known. */
@@ -16466,8 +16470,7 @@ var P2PSession = class _P2PSession extends EventEmitter2 {
16466
16470
  * Acknowledge and reassemble one DATA datagram, sequenced independently per data type.
16467
16471
  *
16468
16472
  * The device numbers each data type's datagrams in its own 16-bit space and repeats what it thinks was
16469
- * lost, so a datagram that does not advance the sequence — a duplicate, or one already superseded — is a
16470
- * retransmission of something already reassembled: it is acknowledged, then ignored. Distance is measured
16473
+ * lost. A repeat of something already delivered is acknowledged, then ignored. Distance is measured
16471
16474
  * modulo the sequence space and read as backwards beyond {@link SEQUENCE_LOOKBACK}, which is what lets the
16472
16475
  * numbering wrap without the next datagram looking like a jump of nearly a full space.
16473
16476
  *
@@ -16477,9 +16480,13 @@ var P2PSession = class _P2PSession extends EventEmitter2 {
16477
16480
  * numbering and the half-assembled frame goes — because ignoring it would freeze the mark, and every
16478
16481
  * datagram of the new numbering would then be read as behind it too, for as long as it took to climb back.
16479
16482
  *
16480
- * Only a forward gap means a datagram is genuinely missing. A logical frame's payload spans datagrams that
16481
- * carry no header of their own, so the bytes cannot be reassembled around the hole: whatever was pending
16482
- * for that data type is discarded, and the frame is rebuilt from the next header.
16483
+ * A forward gap means a datagram has not arrived YET, which is not the same as lost. The device repeats
16484
+ * unacknowledged datagrams, so successors are held for a bounded wait and delivered strictly in sequence
16485
+ * if the missing one arrives. A logical frame's payload spans datagrams that carry no header of their
16486
+ * own, so reassembling around a hole is impossible; waiting for it is what keeps the frame whole.
16487
+ *
16488
+ * Only once {@link REORDER_WAIT_MS} passes, or {@link REORDER_MAX_DATAGRAMS} pile up, is the datagram
16489
+ * treated as lost: a pending frame is discarded and delivery resumes from the earliest held datagram.
16483
16490
  */
16484
16491
  onData(msg, addr) {
16485
16492
  const dataTypeBuffer = msg.subarray(4, 6);
@@ -16490,18 +16497,98 @@ var P2PSession = class _P2PSession extends EventEmitter2 {
16490
16497
  const advance = prevSeq === void 0 ? 1 : seqNo - prevSeq & 65535;
16491
16498
  if (advance === 0)
16492
16499
  return;
16493
- const restarted = advance > SEQUENCE_LOOKBACK && 65536 - advance > STALE_RETRANSMIT_DEPTH;
16494
- if (advance > SEQUENCE_LOOKBACK && !restarted)
16495
- return;
16496
- this.lastSeqByType.set(dataType, seqNo);
16497
- if ((advance > 1 || restarted) && this.pendingByDataType.has(dataType)) {
16498
- if (this.tracedDatagramGaps++ < MAX_TRACED_DATAGRAM_GAPS) {
16499
- this.trace({ phase: restarted ? "sequence-restart" : "datagram-gap", dataType });
16500
- }
16500
+ if (advance > SEQUENCE_LOOKBACK) {
16501
+ if (65536 - advance <= STALE_RETRANSMIT_DEPTH)
16502
+ return;
16503
+ if (this.pendingByDataType.has(dataType) && this.tracedDatagramGaps++ < MAX_TRACED_DATAGRAM_GAPS)
16504
+ this.trace({ phase: "sequence-restart", dataType });
16505
+ this.clearReorderTimer(dataType);
16506
+ this.reorderByType.delete(dataType);
16501
16507
  this.pendingByDataType.delete(dataType);
16508
+ this.lastSeqByType.set(dataType, seqNo);
16509
+ this.reassemble(dataType, msg.subarray(8));
16510
+ return;
16511
+ }
16512
+ if (advance === 1) {
16513
+ this.lastSeqByType.set(dataType, seqNo);
16514
+ this.reassemble(dataType, msg.subarray(8));
16515
+ this.drainReorder(dataType);
16516
+ return;
16517
+ }
16518
+ this.holdForRetransmit(dataType, seqNo, msg.subarray(8));
16519
+ }
16520
+ /** Hold a datagram that arrived past a hole, and arm the wait for the missing one to be repeated. */
16521
+ holdForRetransmit(dataType, seqNo, body) {
16522
+ let reorder = this.reorderByType.get(dataType);
16523
+ if (!reorder) {
16524
+ reorder = { held: /* @__PURE__ */ new Map() };
16525
+ this.reorderByType.set(dataType, reorder);
16526
+ }
16527
+ if (!reorder.held.has(seqNo))
16528
+ reorder.held.set(seqNo, body);
16529
+ if (reorder.held.size > REORDER_MAX_DATAGRAMS) {
16530
+ this.abandonHole(dataType);
16531
+ return;
16532
+ }
16533
+ this.armReorderTimer(dataType);
16534
+ }
16535
+ /** Start a full wait for the current hole, leaving an existing wait undisturbed. */
16536
+ armReorderTimer(dataType) {
16537
+ const reorder = this.reorderByType.get(dataType);
16538
+ if (!reorder?.held.size || reorder.timer)
16539
+ return;
16540
+ const timer = setTimeout(() => {
16541
+ reorder.timer = void 0;
16542
+ this.abandonHole(dataType);
16543
+ }, REORDER_WAIT_MS);
16544
+ timer.unref?.();
16545
+ reorder.timer = timer;
16546
+ }
16547
+ /** Deliver held datagrams that now follow directly on from the last one delivered. */
16548
+ drainReorder(dataType) {
16549
+ const reorder = this.reorderByType.get(dataType);
16550
+ if (!reorder?.held.size)
16551
+ return;
16552
+ this.clearReorderTimer(dataType);
16553
+ for (; ; ) {
16554
+ const next = this.lastSeqByType.get(dataType) + 1 & 65535;
16555
+ const body = reorder.held.get(next);
16556
+ if (body === void 0)
16557
+ break;
16558
+ reorder.held.delete(next);
16559
+ this.lastSeqByType.set(dataType, next);
16560
+ this.reassemble(dataType, body);
16502
16561
  }
16562
+ if (reorder.held.size)
16563
+ this.armReorderTimer(dataType);
16564
+ else
16565
+ this.reorderByType.delete(dataType);
16566
+ }
16567
+ /**
16568
+ * The missing datagram was not repeated in time: report the gap, drop the frame it belonged to, and
16569
+ * resume from the earliest datagram still held so the stream keeps moving.
16570
+ */
16571
+ abandonHole(dataType) {
16572
+ const held = this.reorderByType.get(dataType).held;
16573
+ if (this.pendingByDataType.has(dataType) && this.tracedDatagramGaps++ < MAX_TRACED_DATAGRAM_GAPS)
16574
+ this.trace({ phase: "datagram-gap", dataType });
16575
+ this.pendingByDataType.delete(dataType);
16576
+ const last = this.lastSeqByType.get(dataType);
16577
+ const earliest = [...held.keys()].sort((a, b) => (a - last & 65535) - (b - last & 65535))[0];
16578
+ this.lastSeqByType.set(dataType, earliest - 1 & 65535);
16579
+ this.drainReorder(dataType);
16580
+ }
16581
+ clearReorderTimer(dataType) {
16582
+ const reorder = this.reorderByType.get(dataType);
16583
+ if (!reorder?.timer)
16584
+ return;
16585
+ clearTimeout(reorder.timer);
16586
+ reorder.timer = void 0;
16587
+ }
16588
+ /** Reassemble one in-sequence datagram body into logical frames. */
16589
+ reassemble(dataType, datagramBody) {
16503
16590
  const pending = this.pendingByDataType.get(dataType);
16504
- let body = pending ? Buffer.concat([pending.buf, msg.subarray(8)]) : msg.subarray(8);
16591
+ let body = pending ? Buffer.concat([pending.buf, datagramBody]) : datagramBody;
16505
16592
  const carryHeader = pending?.header;
16506
16593
  this.pendingByDataType.delete(dataType);
16507
16594
  if (carryHeader) {
@@ -16532,6 +16619,9 @@ var P2PSession = class _P2PSession extends EventEmitter2 {
16532
16619
  * completed either.
16533
16620
  */
16534
16621
  resetInboundSequencing() {
16622
+ for (const dataType of this.reorderByType.keys())
16623
+ this.clearReorderTimer(dataType);
16624
+ this.reorderByType.clear();
16535
16625
  this.lastSeqByType.clear();
16536
16626
  this.pendingByDataType.clear();
16537
16627
  this.tracedDatagramGaps = 0;