@apocaliss92/nodelink-js 0.6.1 → 0.6.2

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-D4TKRGUP.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
  });
@@ -8625,35 +8657,88 @@ function decodeHeader(buf) {
8625
8657
  return { header, headerLen, messageKey };
8626
8658
  }
8627
8659
  var BaichuanFrameParser = class {
8660
+ /** Retained-but-unconsumed contiguous bytes from previous push() calls. */
8628
8661
  buffer = Buffer.alloc(0);
8662
+ /** Chunks received since the last materialization, not yet concatenated. */
8663
+ pending = [];
8664
+ /** Total bytes held in `pending` (kept in sync to avoid re-summing). */
8665
+ pendingLen = 0;
8666
+ /**
8667
+ * Total contiguous bytes (`buffer` + `pending`) required before the next
8668
+ * parse attempt can make progress. While buffered bytes stay below this,
8669
+ * incoming chunks are merely stashed in `pending` with no copy. This is
8670
+ * the mechanism that turns the worst case (a large frame fragmented over
8671
+ * many small TCP chunks) from O(n²) into O(n): we concatenate once, when
8672
+ * enough bytes have arrived, instead of on every chunk.
8673
+ *
8674
+ * Starts at 4 — the minimum needed to inspect the magic header.
8675
+ */
8676
+ needed = 4;
8677
+ /**
8678
+ * Collapse `this.buffer` + all `pending` chunks into a single contiguous
8679
+ * buffer. The retained leftover is copied at most once per materialize(),
8680
+ * and materialize() only runs when `needed` bytes are available — so a
8681
+ * fragmented frame is assembled with a single concat, not one per chunk.
8682
+ */
8683
+ materialize() {
8684
+ if (this.pendingLen === 0) return;
8685
+ if (this.buffer.length === 0 && this.pending.length === 1) {
8686
+ this.buffer = this.pending[0];
8687
+ } else {
8688
+ const parts = this.buffer.length === 0 ? this.pending : [this.buffer, ...this.pending];
8689
+ this.buffer = Buffer.concat(parts);
8690
+ }
8691
+ this.pending = [];
8692
+ this.pendingLen = 0;
8693
+ }
8694
+ /** Total buffered bytes, whether materialized or still pending. */
8695
+ get available() {
8696
+ return this.buffer.length + this.pendingLen;
8697
+ }
8629
8698
  push(chunk) {
8630
8699
  if (chunk.length === 0) return [];
8631
- const c = chunk;
8632
- this.buffer = this.buffer.length === 0 ? c : Buffer.concat([this.buffer, c]);
8700
+ this.pending.push(chunk);
8701
+ this.pendingLen += chunk.length;
8702
+ if (this.available < this.needed) return [];
8703
+ this.materialize();
8633
8704
  const out = [];
8634
8705
  while (true) {
8635
- if (this.buffer.length < 4) break;
8706
+ if (this.buffer.length < 4) {
8707
+ this.needed = 4;
8708
+ break;
8709
+ }
8636
8710
  if (!this.buffer.subarray(0, 4).equals(BC_MAGIC) && !this.buffer.subarray(0, 4).equals(BC_MAGIC_REV)) {
8637
8711
  const idx = this.buffer.indexOf(BC_MAGIC);
8638
8712
  const idxRev = this.buffer.indexOf(BC_MAGIC_REV);
8639
8713
  const next = idx === -1 ? idxRev : idxRev === -1 ? idx : Math.min(idx, idxRev);
8640
8714
  if (next === -1) {
8641
8715
  this.buffer = this.buffer.subarray(Math.max(0, this.buffer.length - 3));
8716
+ this.needed = 4;
8642
8717
  break;
8643
8718
  }
8644
8719
  this.buffer = this.buffer.subarray(next);
8645
- if (this.buffer.length < 20) break;
8720
+ if (this.buffer.length < 20) {
8721
+ this.needed = 20;
8722
+ break;
8723
+ }
8724
+ }
8725
+ if (this.buffer.length < 20) {
8726
+ this.needed = 20;
8727
+ break;
8646
8728
  }
8647
- if (this.buffer.length < 20) break;
8648
8729
  let headerInfo;
8649
8730
  try {
8650
8731
  headerInfo = decodeHeader(this.buffer);
8651
8732
  } catch {
8733
+ this.needed = 24;
8652
8734
  break;
8653
8735
  }
8654
8736
  const { header, headerLen, messageKey } = headerInfo;
8655
8737
  const frameLen = headerLen + header.bodyLen;
8656
- if (this.buffer.length < frameLen) break;
8738
+ if (this.buffer.length < frameLen) {
8739
+ this.needed = frameLen;
8740
+ break;
8741
+ }
8657
8742
  const raw = this.buffer.subarray(0, frameLen);
8658
8743
  const body = raw.subarray(headerLen);
8659
8744
  let extLen = 0;
@@ -8665,6 +8750,7 @@ var BaichuanFrameParser = class {
8665
8750
  const payload = body.subarray(extLen);
8666
8751
  out.push({ header, body, extension, payload, messageKey, raw });
8667
8752
  this.buffer = this.buffer.subarray(frameLen);
8753
+ this.needed = 4;
8668
8754
  }
8669
8755
  return out;
8670
8756
  }