@apocaliss92/nodelink-js 0.6.0 → 0.6.1

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.
@@ -645,12 +645,28 @@ async function getServerBinding(uid, options = {}) {
645
645
  const fetchImpl = options.fetchImpl ?? globalThis.fetch;
646
646
  const logger = options.logger;
647
647
  if (typeof fetchImpl !== "function") {
648
- logger?.debug?.(
648
+ logger?.log?.(
649
649
  `[server-binding] global fetch unavailable; skipping cloud lookup`
650
650
  );
651
651
  cache.set(uid, { kind: "err", expires: now + NEGATIVE_TTL_MS });
652
652
  return void 0;
653
653
  }
654
+ try {
655
+ const apiHostname = new URL(baseUrl).hostname;
656
+ const dns2 = await import("dns/promises");
657
+ const answers = await dns2.lookup(apiHostname, { family: 4, all: true });
658
+ const sinkholed = answers.find(
659
+ (a) => a.address?.startsWith("127.") || a.address === "0.0.0.0" || a.address?.startsWith("10.") || a.address?.startsWith("192.168.") || /^172\.(1[6-9]|2[0-9]|3[0-1])\./.test(a.address ?? "")
660
+ );
661
+ if (sinkholed) {
662
+ logger?.log?.(
663
+ `[server-binding] ${uid}: DNS for ${apiHostname} resolves to ${sinkholed.address} (sinkhole / /etc/hosts override). Cloud directory unreachable \u2014 falling back to the 22-hostname P2P sweep. Whitelist ${apiHostname} to enable.`
664
+ );
665
+ cache.set(uid, { kind: "err", expires: now + NEGATIVE_TTL_MS });
666
+ return void 0;
667
+ }
668
+ } catch {
669
+ }
654
670
  const url = `${baseUrl}/devices/${encodeURIComponent(uid)}/server-binding?language=${encodeURIComponent(language)}`;
655
671
  const controller = new AbortController();
656
672
  const timer = setTimeout(() => controller.abort(), timeoutMs);
@@ -661,8 +677,8 @@ async function getServerBinding(uid, options = {}) {
661
677
  headers: { Accept: "application/json" }
662
678
  });
663
679
  if (!res.ok) {
664
- logger?.debug?.(
665
- `[server-binding] ${uid}: HTTP ${res.status} ${res.statusText}`
680
+ logger?.log?.(
681
+ `[server-binding] ${uid}: HTTP ${res.status} ${res.statusText} from ${url}`
666
682
  );
667
683
  cache.set(uid, { kind: "err", expires: now + NEGATIVE_TTL_MS });
668
684
  return void 0;
@@ -670,8 +686,15 @@ async function getServerBinding(uid, options = {}) {
670
686
  const json = await res.json();
671
687
  const parsed = parseServerBindingResponse(json);
672
688
  if (!parsed) {
673
- logger?.debug?.(
674
- `[server-binding] ${uid}: response shape did not match expectations`
689
+ logger?.log?.(
690
+ `[server-binding] ${uid}: response shape did not match expectations (Reolink schema change?)`
691
+ );
692
+ cache.set(uid, { kind: "err", expires: now + NEGATIVE_TTL_MS });
693
+ return void 0;
694
+ }
695
+ if (parsed.availableZones.length === 0) {
696
+ logger?.log?.(
697
+ `[server-binding] ${uid}: cloud returned 0 zones \u2014 UID not registered with Reolink cloud (or wrong region)`
675
698
  );
676
699
  cache.set(uid, { kind: "err", expires: now + NEGATIVE_TTL_MS });
677
700
  return void 0;
@@ -690,9 +713,23 @@ async function getServerBinding(uid, options = {}) {
690
713
  );
691
714
  return parsed;
692
715
  } catch (e) {
693
- logger?.debug?.(
694
- `[server-binding] ${uid}: ${e?.message ?? String(e)}`
695
- );
716
+ const msg = e?.message ?? String(e);
717
+ const errName = e?.name;
718
+ if (errName === "AbortError" || msg.includes("aborted")) {
719
+ logger?.log?.(
720
+ `[server-binding] ${uid}: timed out after ${timeoutMs}ms (cloud unreachable)`
721
+ );
722
+ } else if (msg.includes("ENOTFOUND") || msg.includes("EAI_AGAIN")) {
723
+ logger?.log?.(
724
+ `[server-binding] ${uid}: DNS failed (${msg}) \u2014 apis.reolink.com may be blocked at resolver`
725
+ );
726
+ } else if (msg.includes("ECONNREFUSED") || msg.includes("EHOSTUNREACH") || msg.includes("ENETUNREACH")) {
727
+ logger?.log?.(
728
+ `[server-binding] ${uid}: network unreachable (${msg}) \u2014 cloud port blocked`
729
+ );
730
+ } else {
731
+ logger?.log?.(`[server-binding] ${uid}: fetch failed \u2014 ${msg}`);
732
+ }
696
733
  cache.set(uid, { kind: "err", expires: now + NEGATIVE_TTL_MS });
697
734
  return void 0;
698
735
  } finally {
@@ -1128,12 +1165,19 @@ var BcUdpStream = class extends EventEmitter {
1128
1165
  const tid = (Math.floor(Math.random() * 2147483647) | 0) >>> 0;
1129
1166
  const xml = buildC2mQ({ uid });
1130
1167
  const pkt = encodeDiscoveryPacket(tid, xml);
1168
+ const counters = { sentBytes: 0, rxBytes: 0 };
1131
1169
  return await new Promise((resolve, reject) => {
1132
1170
  const deadline = setTimeout(() => {
1133
1171
  cleanup();
1134
- reject(new Error(`P2P UID lookup timeout (${dest.host}:${dest.port})`));
1172
+ const err = new Error(
1173
+ `P2P UID lookup timeout (${dest.host}:${dest.port}) \u2014 sent=${counters.sentBytes}B rx=${counters.rxBytes}B`
1174
+ );
1175
+ err.sentBytes = counters.sentBytes;
1176
+ err.rxBytes = counters.rxBytes;
1177
+ reject(err);
1135
1178
  }, timeoutMs);
1136
1179
  const onMsg = (msg) => {
1180
+ counters.rxBytes += msg.length;
1137
1181
  try {
1138
1182
  const p = decodeBcUdpPacket(msg);
1139
1183
  if (p.kind !== "discovery") return;
@@ -1141,13 +1185,19 @@ var BcUdpStream = class extends EventEmitter {
1141
1185
  const qr = parseM2cQr(p.xml);
1142
1186
  if (!qr?.reg || !qr?.relay) return;
1143
1187
  cleanup();
1144
- resolve({ reg: qr.reg, relay: qr.relay });
1188
+ resolve({
1189
+ reg: qr.reg,
1190
+ relay: qr.relay,
1191
+ sentBytes: counters.sentBytes,
1192
+ rxBytes: counters.rxBytes
1193
+ });
1145
1194
  } catch {
1146
1195
  }
1147
1196
  };
1148
1197
  const send = () => {
1149
1198
  try {
1150
1199
  sock.send(pkt, dest.port, dest.host);
1200
+ counters.sentBytes += pkt.length;
1151
1201
  } catch {
1152
1202
  }
1153
1203
  };
@@ -25268,4 +25318,4 @@ export {
25268
25318
  tcpReachabilityProbe,
25269
25319
  autoDetectDeviceType
25270
25320
  };
25271
- //# sourceMappingURL=chunk-7HSTETZR.js.map
25321
+ //# sourceMappingURL=chunk-EAHRVNEX.js.map