@apocaliss92/nodelink-js 0.6.1 → 0.6.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.
@@ -3,8 +3,8 @@ import {
3
3
  BaichuanRtspServer,
4
4
  ReolinkBaichuanApi,
5
5
  autoDetectDeviceType
6
- } from "../chunk-EAHRVNEX.js";
7
- import "../chunk-XDVBNZGR.js";
6
+ } from "../chunk-Q4AXRV2G.js";
7
+ import "../chunk-IQVVVSXO.js";
8
8
  import {
9
9
  __require
10
10
  } from "../chunk-MZUSWKF3.js";
package/dist/index.cjs CHANGED
@@ -268,6 +268,41 @@ var init_crypto = __esm({
268
268
  });
269
269
 
270
270
  // src/protocol/xml.ts
271
+ function xmlTextRe(tag) {
272
+ let re = xmlTextReCache.get(tag);
273
+ if (re === void 0) {
274
+ re = new RegExp(`<${tag}>([^<]*)</${tag}>`);
275
+ xmlTextReCache.set(tag, re);
276
+ }
277
+ return re;
278
+ }
279
+ function xmlTagRe(tag) {
280
+ let re = xmlTagReCache.get(tag);
281
+ if (re === void 0) {
282
+ re = new RegExp(`<${tag}>[^<]*</${tag}>`);
283
+ xmlTagReCache.set(tag, re);
284
+ }
285
+ return re;
286
+ }
287
+ function xmlNestedRe(parent, child) {
288
+ const key = `${parent}\0${child}`;
289
+ let re = xmlNestedReCache.get(key);
290
+ if (re === void 0) {
291
+ re = new RegExp(
292
+ `(<${parent}[^>]*>[\\s\\S]*?<${child}>)[^<]*(</${child}>[\\s\\S]*?</${parent}>)`
293
+ );
294
+ xmlNestedReCache.set(key, re);
295
+ }
296
+ return re;
297
+ }
298
+ function xmlStreamBlockRe(streamTag) {
299
+ let re = xmlStreamBlockReCache.get(streamTag);
300
+ if (re === void 0) {
301
+ re = new RegExp(`(<${streamTag}[^>]*>)([\\s\\S]*?)(</${streamTag}>)`);
302
+ xmlStreamBlockReCache.set(streamTag, re);
303
+ }
304
+ return re;
305
+ }
271
306
  function xmlEscape(text) {
272
307
  if (text === void 0 || text === null || typeof text !== "string") {
273
308
  const error = new Error(
@@ -388,8 +423,7 @@ function buildPreviewStopXmlV11(params) {
388
423
  </body>`;
389
424
  }
390
425
  function getXmlText(xml, tagName) {
391
- const re = new RegExp(`<${tagName}>([^<]*)</${tagName}>`);
392
- const m = re.exec(xml);
426
+ const m = xmlTextRe(tagName).exec(xml);
393
427
  return m?.[1];
394
428
  }
395
429
  function buildPtzControlXml(channelId, command, speed) {
@@ -493,13 +527,12 @@ ${xml}`;
493
527
  function applyXmlTagPatch(xml, tag, value) {
494
528
  if (value === void 0) return xml;
495
529
  const v = typeof value === "boolean" ? value ? 1 : 0 : value;
496
- const re = new RegExp(`<${tag}>[^<]*</${tag}>`);
497
- return xml.replace(re, `<${tag}>${v}</${tag}>`);
530
+ return xml.replace(xmlTagRe(tag), `<${tag}>${v}</${tag}>`);
498
531
  }
499
532
  function upsertXmlTag(xml, tag, value) {
500
533
  if (value === void 0) return xml;
501
534
  const v = typeof value === "boolean" ? value ? 1 : 0 : value;
502
- const re = new RegExp(`<${tag}>[^<]*</${tag}>`);
535
+ const re = xmlTagRe(tag);
503
536
  if (re.test(xml)) {
504
537
  return xml.replace(re, `<${tag}>${v}</${tag}>`);
505
538
  }
@@ -508,16 +541,11 @@ function upsertXmlTag(xml, tag, value) {
508
541
  function patchNestedTag(xml, parent, child, value) {
509
542
  if (value === void 0) return xml;
510
543
  const v = typeof value === "boolean" ? value ? 1 : 0 : value;
511
- const re = new RegExp(
512
- `(<${parent}[^>]*>[\\s\\S]*?<${child}>)[^<]*(</${child}>[\\s\\S]*?</${parent}>)`
513
- );
514
- return xml.replace(re, `$1${v}$2`);
544
+ return xml.replace(xmlNestedRe(parent, child), `$1${v}$2`);
515
545
  }
516
546
  function applyStreamPatch(xml, streamTag, patch) {
517
547
  if (!patch) return xml;
518
- const re = new RegExp(
519
- `(<${streamTag}[^>]*>)([\\s\\S]*?)(</${streamTag}>)`
520
- );
548
+ const re = xmlStreamBlockRe(streamTag);
521
549
  return xml.replace(re, (_match, open, body, close) => {
522
550
  let next = body;
523
551
  if (patch.audio !== void 0) {
@@ -547,10 +575,9 @@ function applyStreamPatch(xml, streamTag, patch) {
547
575
  next = upsertXmlTag(next, "encoderProfile", patch.encoderProfile);
548
576
  }
549
577
  if (patch.gop !== void 0) {
550
- const gopBlockRe = /(<gop[^>]*>)([\s\S]*?)(<\/gop>)/;
551
- if (gopBlockRe.test(next)) {
578
+ if (GOP_BLOCK_RE.test(next)) {
552
579
  next = next.replace(
553
- gopBlockRe,
580
+ GOP_BLOCK_RE,
554
581
  (_m, gOpen, gBody, gClose) => `${gOpen}${applyXmlTagPatch(gBody, "cur", patch.gop)}${gClose}`
555
582
  );
556
583
  } else {
@@ -579,10 +606,15 @@ function buildAbilityInfoExtensionXml(username) {
579
606
  <token>system, streaming, PTZ, IO, security, replay, disk, network, alarm, record, video, image</token>
580
607
  </Extension>`;
581
608
  }
582
- var XML_HEADER;
609
+ var xmlTextReCache, xmlTagReCache, xmlNestedReCache, GOP_BLOCK_RE, xmlStreamBlockReCache, XML_HEADER;
583
610
  var init_xml = __esm({
584
611
  "src/protocol/xml.ts"() {
585
612
  "use strict";
613
+ xmlTextReCache = /* @__PURE__ */ new Map();
614
+ xmlTagReCache = /* @__PURE__ */ new Map();
615
+ xmlNestedReCache = /* @__PURE__ */ new Map();
616
+ GOP_BLOCK_RE = /(<gop[^>]*>)([\s\S]*?)(<\/gop>)/;
617
+ xmlStreamBlockReCache = /* @__PURE__ */ new Map();
586
618
  XML_HEADER = `<?xml version="1.0" encoding="UTF-8" ?>`;
587
619
  }
588
620
  });
@@ -8410,6 +8442,7 @@ __export(index_exports, {
8410
8442
  ReolinkHttpClient: () => ReolinkHttpClient,
8411
8443
  Rfc4571Muxer: () => Rfc4571Muxer,
8412
8444
  RtspBackchannel: () => RtspBackchannel,
8445
+ _clearP2pLookupDedupForTests: () => _clearP2pLookupDedupForTests,
8413
8446
  _resetEmailPushBusForTests: () => _resetEmailPushBusForTests,
8414
8447
  abilitiesHasAny: () => abilitiesHasAny,
8415
8448
  aesDecrypt: () => aesDecrypt,
@@ -8522,6 +8555,7 @@ __export(index_exports, {
8522
8555
  isH265Irap: () => isH265Irap,
8523
8556
  isH265KeyframeAnnexB: () => isH265KeyframeAnnexB,
8524
8557
  isNvrHubModel: () => isNvrHubModel,
8558
+ isSameSubnetAsAnyLocalIface: () => isSameSubnetAsAnyLocalIface,
8525
8559
  isTcpFailureThatShouldFallbackToUdp: () => isTcpFailureThatShouldFallbackToUdp,
8526
8560
  isUnroutableForP2P: () => isUnroutableForP2P,
8527
8561
  isValidH264AnnexBAccessUnit: () => isValidH264AnnexBAccessUnit,
@@ -8550,6 +8584,7 @@ __export(index_exports, {
8550
8584
  patchNestedTag: () => patchNestedTag,
8551
8585
  patchShelterXml: () => patchShelterXml,
8552
8586
  printNvrDiagnostics: () => printNvrDiagnostics,
8587
+ probeEgressForHost: () => probeEgressForHost,
8553
8588
  runAllDiagnosticsConsecutively: () => runAllDiagnosticsConsecutively,
8554
8589
  runMultifocalDiagnosticsConsecutively: () => runMultifocalDiagnosticsConsecutively,
8555
8590
  sampleStreams: () => sampleStreams,
@@ -8625,35 +8660,88 @@ function decodeHeader(buf) {
8625
8660
  return { header, headerLen, messageKey };
8626
8661
  }
8627
8662
  var BaichuanFrameParser = class {
8663
+ /** Retained-but-unconsumed contiguous bytes from previous push() calls. */
8628
8664
  buffer = Buffer.alloc(0);
8665
+ /** Chunks received since the last materialization, not yet concatenated. */
8666
+ pending = [];
8667
+ /** Total bytes held in `pending` (kept in sync to avoid re-summing). */
8668
+ pendingLen = 0;
8669
+ /**
8670
+ * Total contiguous bytes (`buffer` + `pending`) required before the next
8671
+ * parse attempt can make progress. While buffered bytes stay below this,
8672
+ * incoming chunks are merely stashed in `pending` with no copy. This is
8673
+ * the mechanism that turns the worst case (a large frame fragmented over
8674
+ * many small TCP chunks) from O(n²) into O(n): we concatenate once, when
8675
+ * enough bytes have arrived, instead of on every chunk.
8676
+ *
8677
+ * Starts at 4 — the minimum needed to inspect the magic header.
8678
+ */
8679
+ needed = 4;
8680
+ /**
8681
+ * Collapse `this.buffer` + all `pending` chunks into a single contiguous
8682
+ * buffer. The retained leftover is copied at most once per materialize(),
8683
+ * and materialize() only runs when `needed` bytes are available — so a
8684
+ * fragmented frame is assembled with a single concat, not one per chunk.
8685
+ */
8686
+ materialize() {
8687
+ if (this.pendingLen === 0) return;
8688
+ if (this.buffer.length === 0 && this.pending.length === 1) {
8689
+ this.buffer = this.pending[0];
8690
+ } else {
8691
+ const parts = this.buffer.length === 0 ? this.pending : [this.buffer, ...this.pending];
8692
+ this.buffer = Buffer.concat(parts);
8693
+ }
8694
+ this.pending = [];
8695
+ this.pendingLen = 0;
8696
+ }
8697
+ /** Total buffered bytes, whether materialized or still pending. */
8698
+ get available() {
8699
+ return this.buffer.length + this.pendingLen;
8700
+ }
8629
8701
  push(chunk) {
8630
8702
  if (chunk.length === 0) return [];
8631
- const c = chunk;
8632
- this.buffer = this.buffer.length === 0 ? c : Buffer.concat([this.buffer, c]);
8703
+ this.pending.push(chunk);
8704
+ this.pendingLen += chunk.length;
8705
+ if (this.available < this.needed) return [];
8706
+ this.materialize();
8633
8707
  const out = [];
8634
8708
  while (true) {
8635
- if (this.buffer.length < 4) break;
8709
+ if (this.buffer.length < 4) {
8710
+ this.needed = 4;
8711
+ break;
8712
+ }
8636
8713
  if (!this.buffer.subarray(0, 4).equals(BC_MAGIC) && !this.buffer.subarray(0, 4).equals(BC_MAGIC_REV)) {
8637
8714
  const idx = this.buffer.indexOf(BC_MAGIC);
8638
8715
  const idxRev = this.buffer.indexOf(BC_MAGIC_REV);
8639
8716
  const next = idx === -1 ? idxRev : idxRev === -1 ? idx : Math.min(idx, idxRev);
8640
8717
  if (next === -1) {
8641
8718
  this.buffer = this.buffer.subarray(Math.max(0, this.buffer.length - 3));
8719
+ this.needed = 4;
8642
8720
  break;
8643
8721
  }
8644
8722
  this.buffer = this.buffer.subarray(next);
8645
- if (this.buffer.length < 20) break;
8723
+ if (this.buffer.length < 20) {
8724
+ this.needed = 20;
8725
+ break;
8726
+ }
8727
+ }
8728
+ if (this.buffer.length < 20) {
8729
+ this.needed = 20;
8730
+ break;
8646
8731
  }
8647
- if (this.buffer.length < 20) break;
8648
8732
  let headerInfo;
8649
8733
  try {
8650
8734
  headerInfo = decodeHeader(this.buffer);
8651
8735
  } catch {
8736
+ this.needed = 24;
8652
8737
  break;
8653
8738
  }
8654
8739
  const { header, headerLen, messageKey } = headerInfo;
8655
8740
  const frameLen = headerLen + header.bodyLen;
8656
- if (this.buffer.length < frameLen) break;
8741
+ if (this.buffer.length < frameLen) {
8742
+ this.needed = frameLen;
8743
+ break;
8744
+ }
8657
8745
  const raw = this.buffer.subarray(0, frameLen);
8658
8746
  const body = raw.subarray(headerLen);
8659
8747
  let extLen = 0;
@@ -8665,6 +8753,7 @@ var BaichuanFrameParser = class {
8665
8753
  const payload = body.subarray(extLen);
8666
8754
  out.push({ header, body, extension, payload, messageKey, raw });
8667
8755
  this.buffer = this.buffer.subarray(frameLen);
8756
+ this.needed = 4;
8668
8757
  }
8669
8758
  return out;
8670
8759
  }
@@ -9079,8 +9168,14 @@ async function getServerBinding(uid, options = {}) {
9079
9168
  headers: { Accept: "application/json" }
9080
9169
  });
9081
9170
  if (!res.ok) {
9171
+ let bodyPreview;
9172
+ try {
9173
+ const text = await res.text();
9174
+ bodyPreview = text.slice(0, 512).replace(/\s+/g, " ").trim();
9175
+ } catch {
9176
+ }
9082
9177
  logger?.log?.(
9083
- `[server-binding] ${uid}: HTTP ${res.status} ${res.statusText} from ${url}`
9178
+ `[server-binding] ${uid}: HTTP ${res.status} ${res.statusText} from ${url}` + (bodyPreview ? ` \u2014 body=${bodyPreview}` : "")
9084
9179
  );
9085
9180
  cache.set(uid, { kind: "err", expires: now + NEGATIVE_TTL_MS });
9086
9181
  return void 0;
@@ -9196,6 +9291,68 @@ function parseServerBindingResponse(raw) {
9196
9291
  }
9197
9292
 
9198
9293
  // src/bcudp/BcUdpStream.ts
9294
+ async function probeEgressForHost(destHost, destPort) {
9295
+ return await new Promise((resolve, reject) => {
9296
+ const probe = import_node_dgram.default.createSocket("udp4");
9297
+ let settled = false;
9298
+ const finish = (err, out) => {
9299
+ if (settled) return;
9300
+ settled = true;
9301
+ try {
9302
+ probe.close();
9303
+ } catch {
9304
+ }
9305
+ if (err || !out) reject(err ?? new Error("egress probe failed"));
9306
+ else resolve(out);
9307
+ };
9308
+ probe.on("error", (e) => finish(e));
9309
+ try {
9310
+ probe.connect(destPort, destHost, () => {
9311
+ try {
9312
+ const a = probe.address();
9313
+ if (typeof a === "string") return finish(new Error("probe address is string"));
9314
+ finish(void 0, {
9315
+ localAddress: a.address,
9316
+ localPort: a.port
9317
+ });
9318
+ } catch (e) {
9319
+ finish(e);
9320
+ }
9321
+ });
9322
+ } catch (e) {
9323
+ finish(e);
9324
+ }
9325
+ });
9326
+ }
9327
+ function isSameSubnetAsAnyLocalIface(destHost, srcInfo) {
9328
+ if (!/^\d+\.\d+\.\d+\.\d+$/.test(destHost)) return "unknown";
9329
+ const dest = destHost.split(".").map((s) => Number(s));
9330
+ if (dest.some((n) => !Number.isFinite(n) || n < 0 || n > 255))
9331
+ return "unknown";
9332
+ const ifaces = (0, import_node_os.networkInterfaces)();
9333
+ let ownerSubnet;
9334
+ for (const name of Object.keys(ifaces)) {
9335
+ const entries = ifaces[name];
9336
+ if (!entries) continue;
9337
+ for (const e of entries) {
9338
+ if (e.family !== "IPv4" || e.internal) continue;
9339
+ if (e.address !== srcInfo.localAddress) continue;
9340
+ const addr = e.address.split(".").map((s) => Number(s));
9341
+ const mask = e.netmask.split(".").map((s) => Number(s));
9342
+ if (addr.length !== 4 || mask.length !== 4 || addr.some((n) => !Number.isFinite(n)) || mask.some((n) => !Number.isFinite(n)))
9343
+ continue;
9344
+ ownerSubnet = { addr, mask };
9345
+ break;
9346
+ }
9347
+ if (ownerSubnet) break;
9348
+ }
9349
+ if (!ownerSubnet) return "unknown";
9350
+ for (let i = 0; i < 4; i++) {
9351
+ if ((ownerSubnet.addr[i] & ownerSubnet.mask[i]) !== (dest[i] & ownerSubnet.mask[i]))
9352
+ return "mismatch";
9353
+ }
9354
+ return "same";
9355
+ }
9199
9356
  var AckLatency = class {
9200
9357
  currentValues = [];
9201
9358
  lastReceiveTime = null;
@@ -9274,6 +9431,16 @@ function isUnroutableForP2P(ip) {
9274
9431
  var P2P_LOOKUP_PORT = 9999;
9275
9432
  var P2P_MAX_WAIT_MS = 15e3;
9276
9433
  var P2P_RESEND_WAIT_MS = 500;
9434
+ var inflightP2pLookups = /* @__PURE__ */ new Map();
9435
+ var cachedP2pLookups = /* @__PURE__ */ new Map();
9436
+ var negCachedP2pLookups = /* @__PURE__ */ new Map();
9437
+ var P2P_LOOKUP_CACHE_TTL_MS = 3e4;
9438
+ var P2P_LOOKUP_NEG_CACHE_TTL_MS = 15e3;
9439
+ function _clearP2pLookupDedupForTests() {
9440
+ inflightP2pLookups.clear();
9441
+ cachedP2pLookups.clear();
9442
+ negCachedP2pLookups.clear();
9443
+ }
9277
9444
  var BcUdpStream = class extends import_node_events.EventEmitter {
9278
9445
  opts;
9279
9446
  /**
@@ -9362,31 +9529,14 @@ var BcUdpStream = class extends import_node_events.EventEmitter {
9362
9529
  });
9363
9530
  sock.on("error", (e) => this.emit("error", e));
9364
9531
  sock.on("close", () => this.emit("close"));
9365
- const portRange = Array.from({ length: 500 }, (_, i) => 53500 + i);
9366
- for (let i = portRange.length - 1; i > 0; i--) {
9367
- const j = Math.floor(Math.random() * (i + 1));
9368
- [portRange[i], portRange[j]] = [portRange[j], portRange[i]];
9369
- }
9370
- let bound = false;
9371
- for (const port of portRange) {
9372
- try {
9373
- await new Promise((resolve, reject) => {
9374
- sock.once("error", reject);
9375
- sock.bind(port, "0.0.0.0", () => {
9376
- sock.removeListener("error", reject);
9377
- resolve();
9378
- });
9379
- });
9380
- bound = true;
9381
- break;
9382
- } catch {
9383
- }
9384
- }
9385
- if (!bound) {
9386
- await new Promise(
9387
- (resolve) => sock.bind(0, "0.0.0.0", () => resolve())
9388
- );
9389
- }
9532
+ await new Promise((resolve, reject) => {
9533
+ const onErr = (e) => reject(e);
9534
+ sock.once("error", onErr);
9535
+ sock.bind(0, "0.0.0.0", () => {
9536
+ sock.removeListener("error", onErr);
9537
+ resolve();
9538
+ });
9539
+ });
9390
9540
  if (this.opts.mode === "direct") {
9391
9541
  this.remote = { host: this.opts.host, port: this.opts.port };
9392
9542
  this.clientId = this.opts.clientId;
@@ -9457,6 +9607,49 @@ var BcUdpStream = class extends import_node_events.EventEmitter {
9457
9607
  this.remote = { host: connected.rhost, port: connected.rport };
9458
9608
  }
9459
9609
  async p2pUidLookup(sock, uid) {
9610
+ const log = (msg) => this.discoveryLogger?.log?.(`[P2P] ${msg}`);
9611
+ const shortUid = uid.length > 7 ? `${uid.slice(0, 5)}\u2026${uid.slice(-2)}` : uid;
9612
+ const cached = cachedP2pLookups.get(uid);
9613
+ if (cached && cached.expires > Date.now()) {
9614
+ log(
9615
+ `UID=${shortUid} cached lookup hit (relay=${cached.result.relay.ip}:${cached.result.relay.port})`
9616
+ );
9617
+ return cached.result;
9618
+ }
9619
+ const negCached = negCachedP2pLookups.get(uid);
9620
+ if (negCached && negCached.expires > Date.now()) {
9621
+ const remaining = negCached.expires - Date.now();
9622
+ log(
9623
+ `UID=${shortUid} negative-cache hit (fail-fast, retry in ${Math.ceil(remaining / 1e3)}s)`
9624
+ );
9625
+ throw negCached.error;
9626
+ }
9627
+ const inflight = inflightP2pLookups.get(uid);
9628
+ if (inflight) {
9629
+ log(`UID=${shortUid} sharing in-flight lookup with concurrent race lane`);
9630
+ return await inflight;
9631
+ }
9632
+ const work = this._doP2pUidLookupWork(sock, uid);
9633
+ inflightP2pLookups.set(uid, work);
9634
+ try {
9635
+ const result = await work;
9636
+ cachedP2pLookups.set(uid, {
9637
+ result,
9638
+ expires: Date.now() + P2P_LOOKUP_CACHE_TTL_MS
9639
+ });
9640
+ return result;
9641
+ } catch (e) {
9642
+ const err = e instanceof Error ? e : new Error(String(e));
9643
+ negCachedP2pLookups.set(uid, {
9644
+ error: err,
9645
+ expires: Date.now() + P2P_LOOKUP_NEG_CACHE_TTL_MS
9646
+ });
9647
+ throw err;
9648
+ } finally {
9649
+ inflightP2pLookups.delete(uid);
9650
+ }
9651
+ }
9652
+ async _doP2pUidLookupWork(sock, uid) {
9460
9653
  const log = (msg) => this.discoveryLogger?.log?.(`[P2P] ${msg}`);
9461
9654
  const shortUid = uid.length > 7 ? `${uid.slice(0, 5)}\u2026${uid.slice(-2)}` : uid;
9462
9655
  const t0 = Date.now();
@@ -9924,6 +10117,23 @@ var BcUdpStream = class extends import_node_events.EventEmitter {
9924
10117
  log(
9925
10118
  `local discovery: mode=${localMode} uid=${shortUid} ports=[${ports.join(", ")}] broadcasts=[${broadcastHosts.join(", ")}]${directHost ? ` direct=${directHost}` : ""} localBindPort=${localPort} timeout=${discoveryTimeout}ms`
9926
10119
  );
10120
+ if (directHost && localMode === "local-direct") {
10121
+ try {
10122
+ const egress = await probeEgressForHost(directHost, ports[0] ?? 2015);
10123
+ const sameSubnet = isSameSubnetAsAnyLocalIface(directHost, egress);
10124
+ if (sameSubnet === "mismatch") {
10125
+ log(
10126
+ `WARN: kernel-chosen source IP ${egress.localAddress} is NOT in the same subnet as ${directHost}. Some Reolink battery cams silently drop discovery packets with off-subnet source IPs. If discovery fails, check your routing table \u2014 likely a Tailscale / VPN / secondary NIC stealing the default route.`
10127
+ );
10128
+ } else {
10129
+ log(
10130
+ `egress for ${directHost} \u2192 src=${egress.localAddress}` + (sameSubnet === "same" ? ` (same subnet \u2713)` : ` (subnet relationship unknown)`)
10131
+ );
10132
+ }
10133
+ } catch (e) {
10134
+ this.emit("debug", "egress_probe_failed", e);
10135
+ }
10136
+ }
9927
10137
  let bytesSent = 0;
9928
10138
  let pktsRecv = 0;
9929
10139
  sock.on("message", () => {
@@ -43477,6 +43687,7 @@ function buildInitialStatus(config) {
43477
43687
  ReolinkHttpClient,
43478
43688
  Rfc4571Muxer,
43479
43689
  RtspBackchannel,
43690
+ _clearP2pLookupDedupForTests,
43480
43691
  _resetEmailPushBusForTests,
43481
43692
  abilitiesHasAny,
43482
43693
  aesDecrypt,
@@ -43589,6 +43800,7 @@ function buildInitialStatus(config) {
43589
43800
  isH265Irap,
43590
43801
  isH265KeyframeAnnexB,
43591
43802
  isNvrHubModel,
43803
+ isSameSubnetAsAnyLocalIface,
43592
43804
  isTcpFailureThatShouldFallbackToUdp,
43593
43805
  isUnroutableForP2P,
43594
43806
  isValidH264AnnexBAccessUnit,
@@ -43617,6 +43829,7 @@ function buildInitialStatus(config) {
43617
43829
  patchNestedTag,
43618
43830
  patchShelterXml,
43619
43831
  printNvrDiagnostics,
43832
+ probeEgressForHost,
43620
43833
  runAllDiagnosticsConsecutively,
43621
43834
  runMultifocalDiagnosticsConsecutively,
43622
43835
  sampleStreams,