@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.
@@ -11318,12 +11318,28 @@ async function getServerBinding(uid, options = {}) {
11318
11318
  const fetchImpl = options.fetchImpl ?? globalThis.fetch;
11319
11319
  const logger = options.logger;
11320
11320
  if (typeof fetchImpl !== "function") {
11321
- logger?.debug?.(
11321
+ logger?.log?.(
11322
11322
  `[server-binding] global fetch unavailable; skipping cloud lookup`
11323
11323
  );
11324
11324
  cache.set(uid, { kind: "err", expires: now + NEGATIVE_TTL_MS });
11325
11325
  return void 0;
11326
11326
  }
11327
+ try {
11328
+ const apiHostname = new URL(baseUrl).hostname;
11329
+ const dns2 = await import("dns/promises");
11330
+ const answers = await dns2.lookup(apiHostname, { family: 4, all: true });
11331
+ const sinkholed = answers.find(
11332
+ (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 ?? "")
11333
+ );
11334
+ if (sinkholed) {
11335
+ logger?.log?.(
11336
+ `[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.`
11337
+ );
11338
+ cache.set(uid, { kind: "err", expires: now + NEGATIVE_TTL_MS });
11339
+ return void 0;
11340
+ }
11341
+ } catch {
11342
+ }
11327
11343
  const url = `${baseUrl}/devices/${encodeURIComponent(uid)}/server-binding?language=${encodeURIComponent(language)}`;
11328
11344
  const controller = new AbortController();
11329
11345
  const timer = setTimeout(() => controller.abort(), timeoutMs);
@@ -11334,8 +11350,8 @@ async function getServerBinding(uid, options = {}) {
11334
11350
  headers: { Accept: "application/json" }
11335
11351
  });
11336
11352
  if (!res.ok) {
11337
- logger?.debug?.(
11338
- `[server-binding] ${uid}: HTTP ${res.status} ${res.statusText}`
11353
+ logger?.log?.(
11354
+ `[server-binding] ${uid}: HTTP ${res.status} ${res.statusText} from ${url}`
11339
11355
  );
11340
11356
  cache.set(uid, { kind: "err", expires: now + NEGATIVE_TTL_MS });
11341
11357
  return void 0;
@@ -11343,8 +11359,15 @@ async function getServerBinding(uid, options = {}) {
11343
11359
  const json = await res.json();
11344
11360
  const parsed = parseServerBindingResponse(json);
11345
11361
  if (!parsed) {
11346
- logger?.debug?.(
11347
- `[server-binding] ${uid}: response shape did not match expectations`
11362
+ logger?.log?.(
11363
+ `[server-binding] ${uid}: response shape did not match expectations (Reolink schema change?)`
11364
+ );
11365
+ cache.set(uid, { kind: "err", expires: now + NEGATIVE_TTL_MS });
11366
+ return void 0;
11367
+ }
11368
+ if (parsed.availableZones.length === 0) {
11369
+ logger?.log?.(
11370
+ `[server-binding] ${uid}: cloud returned 0 zones \u2014 UID not registered with Reolink cloud (or wrong region)`
11348
11371
  );
11349
11372
  cache.set(uid, { kind: "err", expires: now + NEGATIVE_TTL_MS });
11350
11373
  return void 0;
@@ -11363,9 +11386,23 @@ async function getServerBinding(uid, options = {}) {
11363
11386
  );
11364
11387
  return parsed;
11365
11388
  } catch (e) {
11366
- logger?.debug?.(
11367
- `[server-binding] ${uid}: ${e?.message ?? String(e)}`
11368
- );
11389
+ const msg = e?.message ?? String(e);
11390
+ const errName = e?.name;
11391
+ if (errName === "AbortError" || msg.includes("aborted")) {
11392
+ logger?.log?.(
11393
+ `[server-binding] ${uid}: timed out after ${timeoutMs}ms (cloud unreachable)`
11394
+ );
11395
+ } else if (msg.includes("ENOTFOUND") || msg.includes("EAI_AGAIN")) {
11396
+ logger?.log?.(
11397
+ `[server-binding] ${uid}: DNS failed (${msg}) \u2014 apis.reolink.com may be blocked at resolver`
11398
+ );
11399
+ } else if (msg.includes("ECONNREFUSED") || msg.includes("EHOSTUNREACH") || msg.includes("ENETUNREACH")) {
11400
+ logger?.log?.(
11401
+ `[server-binding] ${uid}: network unreachable (${msg}) \u2014 cloud port blocked`
11402
+ );
11403
+ } else {
11404
+ logger?.log?.(`[server-binding] ${uid}: fetch failed \u2014 ${msg}`);
11405
+ }
11369
11406
  cache.set(uid, { kind: "err", expires: now + NEGATIVE_TTL_MS });
11370
11407
  return void 0;
11371
11408
  } finally {
@@ -11801,12 +11838,19 @@ var BcUdpStream = class extends import_node_events3.EventEmitter {
11801
11838
  const tid = (Math.floor(Math.random() * 2147483647) | 0) >>> 0;
11802
11839
  const xml = buildC2mQ({ uid });
11803
11840
  const pkt = encodeDiscoveryPacket(tid, xml);
11841
+ const counters = { sentBytes: 0, rxBytes: 0 };
11804
11842
  return await new Promise((resolve, reject) => {
11805
11843
  const deadline = setTimeout(() => {
11806
11844
  cleanup();
11807
- reject(new Error(`P2P UID lookup timeout (${dest.host}:${dest.port})`));
11845
+ const err = new Error(
11846
+ `P2P UID lookup timeout (${dest.host}:${dest.port}) \u2014 sent=${counters.sentBytes}B rx=${counters.rxBytes}B`
11847
+ );
11848
+ err.sentBytes = counters.sentBytes;
11849
+ err.rxBytes = counters.rxBytes;
11850
+ reject(err);
11808
11851
  }, timeoutMs);
11809
11852
  const onMsg = (msg) => {
11853
+ counters.rxBytes += msg.length;
11810
11854
  try {
11811
11855
  const p = decodeBcUdpPacket(msg);
11812
11856
  if (p.kind !== "discovery") return;
@@ -11814,13 +11858,19 @@ var BcUdpStream = class extends import_node_events3.EventEmitter {
11814
11858
  const qr = parseM2cQr(p.xml);
11815
11859
  if (!qr?.reg || !qr?.relay) return;
11816
11860
  cleanup();
11817
- resolve({ reg: qr.reg, relay: qr.relay });
11861
+ resolve({
11862
+ reg: qr.reg,
11863
+ relay: qr.relay,
11864
+ sentBytes: counters.sentBytes,
11865
+ rxBytes: counters.rxBytes
11866
+ });
11818
11867
  } catch {
11819
11868
  }
11820
11869
  };
11821
11870
  const send = () => {
11822
11871
  try {
11823
11872
  sock.send(pkt, dest.port, dest.host);
11873
+ counters.sentBytes += pkt.length;
11824
11874
  } catch {
11825
11875
  }
11826
11876
  };