@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.
@@ -2574,6 +2574,41 @@ var init_BaichuanVideoStream = __esm({
2574
2574
  });
2575
2575
 
2576
2576
  // src/protocol/xml.ts
2577
+ function xmlTextRe(tag) {
2578
+ let re = xmlTextReCache.get(tag);
2579
+ if (re === void 0) {
2580
+ re = new RegExp(`<${tag}>([^<]*)</${tag}>`);
2581
+ xmlTextReCache.set(tag, re);
2582
+ }
2583
+ return re;
2584
+ }
2585
+ function xmlTagRe(tag) {
2586
+ let re = xmlTagReCache.get(tag);
2587
+ if (re === void 0) {
2588
+ re = new RegExp(`<${tag}>[^<]*</${tag}>`);
2589
+ xmlTagReCache.set(tag, re);
2590
+ }
2591
+ return re;
2592
+ }
2593
+ function xmlNestedRe(parent, child) {
2594
+ const key = `${parent}\0${child}`;
2595
+ let re = xmlNestedReCache.get(key);
2596
+ if (re === void 0) {
2597
+ re = new RegExp(
2598
+ `(<${parent}[^>]*>[\\s\\S]*?<${child}>)[^<]*(</${child}>[\\s\\S]*?</${parent}>)`
2599
+ );
2600
+ xmlNestedReCache.set(key, re);
2601
+ }
2602
+ return re;
2603
+ }
2604
+ function xmlStreamBlockRe(streamTag) {
2605
+ let re = xmlStreamBlockReCache.get(streamTag);
2606
+ if (re === void 0) {
2607
+ re = new RegExp(`(<${streamTag}[^>]*>)([\\s\\S]*?)(</${streamTag}>)`);
2608
+ xmlStreamBlockReCache.set(streamTag, re);
2609
+ }
2610
+ return re;
2611
+ }
2577
2612
  function xmlEscape(text) {
2578
2613
  if (text === void 0 || text === null || typeof text !== "string") {
2579
2614
  const error = new Error(
@@ -2694,8 +2729,7 @@ function buildPreviewStopXmlV11(params) {
2694
2729
  </body>`;
2695
2730
  }
2696
2731
  function getXmlText(xml, tagName) {
2697
- const re = new RegExp(`<${tagName}>([^<]*)</${tagName}>`);
2698
- const m = re.exec(xml);
2732
+ const m = xmlTextRe(tagName).exec(xml);
2699
2733
  return m?.[1];
2700
2734
  }
2701
2735
  function buildPtzControlXml(channelId, command, speed) {
@@ -2796,13 +2830,12 @@ ${xml}`;
2796
2830
  function applyXmlTagPatch(xml, tag, value) {
2797
2831
  if (value === void 0) return xml;
2798
2832
  const v = typeof value === "boolean" ? value ? 1 : 0 : value;
2799
- const re = new RegExp(`<${tag}>[^<]*</${tag}>`);
2800
- return xml.replace(re, `<${tag}>${v}</${tag}>`);
2833
+ return xml.replace(xmlTagRe(tag), `<${tag}>${v}</${tag}>`);
2801
2834
  }
2802
2835
  function upsertXmlTag(xml, tag, value) {
2803
2836
  if (value === void 0) return xml;
2804
2837
  const v = typeof value === "boolean" ? value ? 1 : 0 : value;
2805
- const re = new RegExp(`<${tag}>[^<]*</${tag}>`);
2838
+ const re = xmlTagRe(tag);
2806
2839
  if (re.test(xml)) {
2807
2840
  return xml.replace(re, `<${tag}>${v}</${tag}>`);
2808
2841
  }
@@ -2811,16 +2844,11 @@ function upsertXmlTag(xml, tag, value) {
2811
2844
  function patchNestedTag(xml, parent, child, value) {
2812
2845
  if (value === void 0) return xml;
2813
2846
  const v = typeof value === "boolean" ? value ? 1 : 0 : value;
2814
- const re = new RegExp(
2815
- `(<${parent}[^>]*>[\\s\\S]*?<${child}>)[^<]*(</${child}>[\\s\\S]*?</${parent}>)`
2816
- );
2817
- return xml.replace(re, `$1${v}$2`);
2847
+ return xml.replace(xmlNestedRe(parent, child), `$1${v}$2`);
2818
2848
  }
2819
2849
  function applyStreamPatch(xml, streamTag, patch) {
2820
2850
  if (!patch) return xml;
2821
- const re = new RegExp(
2822
- `(<${streamTag}[^>]*>)([\\s\\S]*?)(</${streamTag}>)`
2823
- );
2851
+ const re = xmlStreamBlockRe(streamTag);
2824
2852
  return xml.replace(re, (_match, open, body, close) => {
2825
2853
  let next = body;
2826
2854
  if (patch.audio !== void 0) {
@@ -2850,10 +2878,9 @@ function applyStreamPatch(xml, streamTag, patch) {
2850
2878
  next = upsertXmlTag(next, "encoderProfile", patch.encoderProfile);
2851
2879
  }
2852
2880
  if (patch.gop !== void 0) {
2853
- const gopBlockRe = /(<gop[^>]*>)([\s\S]*?)(<\/gop>)/;
2854
- if (gopBlockRe.test(next)) {
2881
+ if (GOP_BLOCK_RE.test(next)) {
2855
2882
  next = next.replace(
2856
- gopBlockRe,
2883
+ GOP_BLOCK_RE,
2857
2884
  (_m, gOpen, gBody, gClose) => `${gOpen}${applyXmlTagPatch(gBody, "cur", patch.gop)}${gClose}`
2858
2885
  );
2859
2886
  } else {
@@ -2882,10 +2909,15 @@ function buildAbilityInfoExtensionXml(username) {
2882
2909
  <token>system, streaming, PTZ, IO, security, replay, disk, network, alarm, record, video, image</token>
2883
2910
  </Extension>`;
2884
2911
  }
2885
- var XML_HEADER;
2912
+ var xmlTextReCache, xmlTagReCache, xmlNestedReCache, GOP_BLOCK_RE, xmlStreamBlockReCache, XML_HEADER;
2886
2913
  var init_xml = __esm({
2887
2914
  "src/protocol/xml.ts"() {
2888
2915
  "use strict";
2916
+ xmlTextReCache = /* @__PURE__ */ new Map();
2917
+ xmlTagReCache = /* @__PURE__ */ new Map();
2918
+ xmlNestedReCache = /* @__PURE__ */ new Map();
2919
+ GOP_BLOCK_RE = /(<gop[^>]*>)([\s\S]*?)(<\/gop>)/;
2920
+ xmlStreamBlockReCache = /* @__PURE__ */ new Map();
2889
2921
  XML_HEADER = `<?xml version="1.0" encoding="UTF-8" ?>`;
2890
2922
  }
2891
2923
  });
@@ -12861,35 +12893,88 @@ function decodeHeader(buf) {
12861
12893
  return { header, headerLen, messageKey };
12862
12894
  }
12863
12895
  var BaichuanFrameParser = class {
12896
+ /** Retained-but-unconsumed contiguous bytes from previous push() calls. */
12864
12897
  buffer = Buffer.alloc(0);
12898
+ /** Chunks received since the last materialization, not yet concatenated. */
12899
+ pending = [];
12900
+ /** Total bytes held in `pending` (kept in sync to avoid re-summing). */
12901
+ pendingLen = 0;
12902
+ /**
12903
+ * Total contiguous bytes (`buffer` + `pending`) required before the next
12904
+ * parse attempt can make progress. While buffered bytes stay below this,
12905
+ * incoming chunks are merely stashed in `pending` with no copy. This is
12906
+ * the mechanism that turns the worst case (a large frame fragmented over
12907
+ * many small TCP chunks) from O(n²) into O(n): we concatenate once, when
12908
+ * enough bytes have arrived, instead of on every chunk.
12909
+ *
12910
+ * Starts at 4 — the minimum needed to inspect the magic header.
12911
+ */
12912
+ needed = 4;
12913
+ /**
12914
+ * Collapse `this.buffer` + all `pending` chunks into a single contiguous
12915
+ * buffer. The retained leftover is copied at most once per materialize(),
12916
+ * and materialize() only runs when `needed` bytes are available — so a
12917
+ * fragmented frame is assembled with a single concat, not one per chunk.
12918
+ */
12919
+ materialize() {
12920
+ if (this.pendingLen === 0) return;
12921
+ if (this.buffer.length === 0 && this.pending.length === 1) {
12922
+ this.buffer = this.pending[0];
12923
+ } else {
12924
+ const parts = this.buffer.length === 0 ? this.pending : [this.buffer, ...this.pending];
12925
+ this.buffer = Buffer.concat(parts);
12926
+ }
12927
+ this.pending = [];
12928
+ this.pendingLen = 0;
12929
+ }
12930
+ /** Total buffered bytes, whether materialized or still pending. */
12931
+ get available() {
12932
+ return this.buffer.length + this.pendingLen;
12933
+ }
12865
12934
  push(chunk) {
12866
12935
  if (chunk.length === 0) return [];
12867
- const c = chunk;
12868
- this.buffer = this.buffer.length === 0 ? c : Buffer.concat([this.buffer, c]);
12936
+ this.pending.push(chunk);
12937
+ this.pendingLen += chunk.length;
12938
+ if (this.available < this.needed) return [];
12939
+ this.materialize();
12869
12940
  const out = [];
12870
12941
  while (true) {
12871
- if (this.buffer.length < 4) break;
12942
+ if (this.buffer.length < 4) {
12943
+ this.needed = 4;
12944
+ break;
12945
+ }
12872
12946
  if (!this.buffer.subarray(0, 4).equals(BC_MAGIC) && !this.buffer.subarray(0, 4).equals(BC_MAGIC_REV)) {
12873
12947
  const idx = this.buffer.indexOf(BC_MAGIC);
12874
12948
  const idxRev = this.buffer.indexOf(BC_MAGIC_REV);
12875
12949
  const next = idx === -1 ? idxRev : idxRev === -1 ? idx : Math.min(idx, idxRev);
12876
12950
  if (next === -1) {
12877
12951
  this.buffer = this.buffer.subarray(Math.max(0, this.buffer.length - 3));
12952
+ this.needed = 4;
12878
12953
  break;
12879
12954
  }
12880
12955
  this.buffer = this.buffer.subarray(next);
12881
- if (this.buffer.length < 20) break;
12956
+ if (this.buffer.length < 20) {
12957
+ this.needed = 20;
12958
+ break;
12959
+ }
12960
+ }
12961
+ if (this.buffer.length < 20) {
12962
+ this.needed = 20;
12963
+ break;
12882
12964
  }
12883
- if (this.buffer.length < 20) break;
12884
12965
  let headerInfo;
12885
12966
  try {
12886
12967
  headerInfo = decodeHeader(this.buffer);
12887
12968
  } catch {
12969
+ this.needed = 24;
12888
12970
  break;
12889
12971
  }
12890
12972
  const { header, headerLen, messageKey } = headerInfo;
12891
12973
  const frameLen = headerLen + header.bodyLen;
12892
- if (this.buffer.length < frameLen) break;
12974
+ if (this.buffer.length < frameLen) {
12975
+ this.needed = frameLen;
12976
+ break;
12977
+ }
12893
12978
  const raw = this.buffer.subarray(0, frameLen);
12894
12979
  const body = raw.subarray(headerLen);
12895
12980
  let extLen = 0;
@@ -12901,6 +12986,7 @@ var BaichuanFrameParser = class {
12901
12986
  const payload = body.subarray(extLen);
12902
12987
  out.push({ header, body, extension, payload, messageKey, raw });
12903
12988
  this.buffer = this.buffer.subarray(frameLen);
12989
+ this.needed = 4;
12904
12990
  }
12905
12991
  return out;
12906
12992
  }