@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.
- package/dist/{DiagnosticsTools-K4MF2VXZ.js → DiagnosticsTools-QJ3CRYGA.js} +2 -2
- package/dist/{chunk-XDVBNZGR.js → chunk-IQVVVSXO.js} +48 -16
- package/dist/{chunk-XDVBNZGR.js.map → chunk-IQVVVSXO.js.map} +1 -1
- package/dist/{chunk-EAHRVNEX.js → chunk-Q4AXRV2G.js} +214 -36
- package/dist/chunk-Q4AXRV2G.js.map +1 -0
- package/dist/cli/rtsp-server.cjs +250 -48
- package/dist/cli/rtsp-server.cjs.map +1 -1
- package/dist/cli/rtsp-server.js +2 -2
- package/dist/index.cjs +261 -48
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +70 -1
- package/dist/index.d.ts +72 -0
- package/dist/index.js +8 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-EAHRVNEX.js.map +0 -1
- /package/dist/{DiagnosticsTools-K4MF2VXZ.js.map → DiagnosticsTools-QJ3CRYGA.js.map} +0 -0
package/dist/cli/rtsp-server.cjs
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
2854
|
-
if (gopBlockRe.test(next)) {
|
|
2881
|
+
if (GOP_BLOCK_RE.test(next)) {
|
|
2855
2882
|
next = next.replace(
|
|
2856
|
-
|
|
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
|
});
|
|
@@ -11350,8 +11382,14 @@ async function getServerBinding(uid, options = {}) {
|
|
|
11350
11382
|
headers: { Accept: "application/json" }
|
|
11351
11383
|
});
|
|
11352
11384
|
if (!res.ok) {
|
|
11385
|
+
let bodyPreview;
|
|
11386
|
+
try {
|
|
11387
|
+
const text = await res.text();
|
|
11388
|
+
bodyPreview = text.slice(0, 512).replace(/\s+/g, " ").trim();
|
|
11389
|
+
} catch {
|
|
11390
|
+
}
|
|
11353
11391
|
logger?.log?.(
|
|
11354
|
-
`[server-binding] ${uid}: HTTP ${res.status} ${res.statusText} from ${url}`
|
|
11392
|
+
`[server-binding] ${uid}: HTTP ${res.status} ${res.statusText} from ${url}` + (bodyPreview ? ` \u2014 body=${bodyPreview}` : "")
|
|
11355
11393
|
);
|
|
11356
11394
|
cache.set(uid, { kind: "err", expires: now + NEGATIVE_TTL_MS });
|
|
11357
11395
|
return void 0;
|
|
@@ -11467,6 +11505,68 @@ function parseServerBindingResponse(raw) {
|
|
|
11467
11505
|
}
|
|
11468
11506
|
|
|
11469
11507
|
// src/bcudp/BcUdpStream.ts
|
|
11508
|
+
async function probeEgressForHost(destHost, destPort) {
|
|
11509
|
+
return await new Promise((resolve, reject) => {
|
|
11510
|
+
const probe = import_node_dgram.default.createSocket("udp4");
|
|
11511
|
+
let settled = false;
|
|
11512
|
+
const finish = (err, out) => {
|
|
11513
|
+
if (settled) return;
|
|
11514
|
+
settled = true;
|
|
11515
|
+
try {
|
|
11516
|
+
probe.close();
|
|
11517
|
+
} catch {
|
|
11518
|
+
}
|
|
11519
|
+
if (err || !out) reject(err ?? new Error("egress probe failed"));
|
|
11520
|
+
else resolve(out);
|
|
11521
|
+
};
|
|
11522
|
+
probe.on("error", (e) => finish(e));
|
|
11523
|
+
try {
|
|
11524
|
+
probe.connect(destPort, destHost, () => {
|
|
11525
|
+
try {
|
|
11526
|
+
const a = probe.address();
|
|
11527
|
+
if (typeof a === "string") return finish(new Error("probe address is string"));
|
|
11528
|
+
finish(void 0, {
|
|
11529
|
+
localAddress: a.address,
|
|
11530
|
+
localPort: a.port
|
|
11531
|
+
});
|
|
11532
|
+
} catch (e) {
|
|
11533
|
+
finish(e);
|
|
11534
|
+
}
|
|
11535
|
+
});
|
|
11536
|
+
} catch (e) {
|
|
11537
|
+
finish(e);
|
|
11538
|
+
}
|
|
11539
|
+
});
|
|
11540
|
+
}
|
|
11541
|
+
function isSameSubnetAsAnyLocalIface(destHost, srcInfo) {
|
|
11542
|
+
if (!/^\d+\.\d+\.\d+\.\d+$/.test(destHost)) return "unknown";
|
|
11543
|
+
const dest = destHost.split(".").map((s) => Number(s));
|
|
11544
|
+
if (dest.some((n) => !Number.isFinite(n) || n < 0 || n > 255))
|
|
11545
|
+
return "unknown";
|
|
11546
|
+
const ifaces = (0, import_node_os.networkInterfaces)();
|
|
11547
|
+
let ownerSubnet;
|
|
11548
|
+
for (const name of Object.keys(ifaces)) {
|
|
11549
|
+
const entries = ifaces[name];
|
|
11550
|
+
if (!entries) continue;
|
|
11551
|
+
for (const e of entries) {
|
|
11552
|
+
if (e.family !== "IPv4" || e.internal) continue;
|
|
11553
|
+
if (e.address !== srcInfo.localAddress) continue;
|
|
11554
|
+
const addr = e.address.split(".").map((s) => Number(s));
|
|
11555
|
+
const mask = e.netmask.split(".").map((s) => Number(s));
|
|
11556
|
+
if (addr.length !== 4 || mask.length !== 4 || addr.some((n) => !Number.isFinite(n)) || mask.some((n) => !Number.isFinite(n)))
|
|
11557
|
+
continue;
|
|
11558
|
+
ownerSubnet = { addr, mask };
|
|
11559
|
+
break;
|
|
11560
|
+
}
|
|
11561
|
+
if (ownerSubnet) break;
|
|
11562
|
+
}
|
|
11563
|
+
if (!ownerSubnet) return "unknown";
|
|
11564
|
+
for (let i = 0; i < 4; i++) {
|
|
11565
|
+
if ((ownerSubnet.addr[i] & ownerSubnet.mask[i]) !== (dest[i] & ownerSubnet.mask[i]))
|
|
11566
|
+
return "mismatch";
|
|
11567
|
+
}
|
|
11568
|
+
return "same";
|
|
11569
|
+
}
|
|
11470
11570
|
var AckLatency = class {
|
|
11471
11571
|
currentValues = [];
|
|
11472
11572
|
lastReceiveTime = null;
|
|
@@ -11545,6 +11645,11 @@ function isUnroutableForP2P(ip) {
|
|
|
11545
11645
|
var P2P_LOOKUP_PORT = 9999;
|
|
11546
11646
|
var P2P_MAX_WAIT_MS = 15e3;
|
|
11547
11647
|
var P2P_RESEND_WAIT_MS = 500;
|
|
11648
|
+
var inflightP2pLookups = /* @__PURE__ */ new Map();
|
|
11649
|
+
var cachedP2pLookups = /* @__PURE__ */ new Map();
|
|
11650
|
+
var negCachedP2pLookups = /* @__PURE__ */ new Map();
|
|
11651
|
+
var P2P_LOOKUP_CACHE_TTL_MS = 3e4;
|
|
11652
|
+
var P2P_LOOKUP_NEG_CACHE_TTL_MS = 15e3;
|
|
11548
11653
|
var BcUdpStream = class extends import_node_events3.EventEmitter {
|
|
11549
11654
|
opts;
|
|
11550
11655
|
/**
|
|
@@ -11633,31 +11738,14 @@ var BcUdpStream = class extends import_node_events3.EventEmitter {
|
|
|
11633
11738
|
});
|
|
11634
11739
|
sock.on("error", (e) => this.emit("error", e));
|
|
11635
11740
|
sock.on("close", () => this.emit("close"));
|
|
11636
|
-
|
|
11637
|
-
|
|
11638
|
-
|
|
11639
|
-
|
|
11640
|
-
|
|
11641
|
-
|
|
11642
|
-
|
|
11643
|
-
|
|
11644
|
-
await new Promise((resolve, reject) => {
|
|
11645
|
-
sock.once("error", reject);
|
|
11646
|
-
sock.bind(port, "0.0.0.0", () => {
|
|
11647
|
-
sock.removeListener("error", reject);
|
|
11648
|
-
resolve();
|
|
11649
|
-
});
|
|
11650
|
-
});
|
|
11651
|
-
bound = true;
|
|
11652
|
-
break;
|
|
11653
|
-
} catch {
|
|
11654
|
-
}
|
|
11655
|
-
}
|
|
11656
|
-
if (!bound) {
|
|
11657
|
-
await new Promise(
|
|
11658
|
-
(resolve) => sock.bind(0, "0.0.0.0", () => resolve())
|
|
11659
|
-
);
|
|
11660
|
-
}
|
|
11741
|
+
await new Promise((resolve, reject) => {
|
|
11742
|
+
const onErr = (e) => reject(e);
|
|
11743
|
+
sock.once("error", onErr);
|
|
11744
|
+
sock.bind(0, "0.0.0.0", () => {
|
|
11745
|
+
sock.removeListener("error", onErr);
|
|
11746
|
+
resolve();
|
|
11747
|
+
});
|
|
11748
|
+
});
|
|
11661
11749
|
if (this.opts.mode === "direct") {
|
|
11662
11750
|
this.remote = { host: this.opts.host, port: this.opts.port };
|
|
11663
11751
|
this.clientId = this.opts.clientId;
|
|
@@ -11728,6 +11816,49 @@ var BcUdpStream = class extends import_node_events3.EventEmitter {
|
|
|
11728
11816
|
this.remote = { host: connected.rhost, port: connected.rport };
|
|
11729
11817
|
}
|
|
11730
11818
|
async p2pUidLookup(sock, uid) {
|
|
11819
|
+
const log = (msg) => this.discoveryLogger?.log?.(`[P2P] ${msg}`);
|
|
11820
|
+
const shortUid = uid.length > 7 ? `${uid.slice(0, 5)}\u2026${uid.slice(-2)}` : uid;
|
|
11821
|
+
const cached = cachedP2pLookups.get(uid);
|
|
11822
|
+
if (cached && cached.expires > Date.now()) {
|
|
11823
|
+
log(
|
|
11824
|
+
`UID=${shortUid} cached lookup hit (relay=${cached.result.relay.ip}:${cached.result.relay.port})`
|
|
11825
|
+
);
|
|
11826
|
+
return cached.result;
|
|
11827
|
+
}
|
|
11828
|
+
const negCached = negCachedP2pLookups.get(uid);
|
|
11829
|
+
if (negCached && negCached.expires > Date.now()) {
|
|
11830
|
+
const remaining = negCached.expires - Date.now();
|
|
11831
|
+
log(
|
|
11832
|
+
`UID=${shortUid} negative-cache hit (fail-fast, retry in ${Math.ceil(remaining / 1e3)}s)`
|
|
11833
|
+
);
|
|
11834
|
+
throw negCached.error;
|
|
11835
|
+
}
|
|
11836
|
+
const inflight = inflightP2pLookups.get(uid);
|
|
11837
|
+
if (inflight) {
|
|
11838
|
+
log(`UID=${shortUid} sharing in-flight lookup with concurrent race lane`);
|
|
11839
|
+
return await inflight;
|
|
11840
|
+
}
|
|
11841
|
+
const work = this._doP2pUidLookupWork(sock, uid);
|
|
11842
|
+
inflightP2pLookups.set(uid, work);
|
|
11843
|
+
try {
|
|
11844
|
+
const result = await work;
|
|
11845
|
+
cachedP2pLookups.set(uid, {
|
|
11846
|
+
result,
|
|
11847
|
+
expires: Date.now() + P2P_LOOKUP_CACHE_TTL_MS
|
|
11848
|
+
});
|
|
11849
|
+
return result;
|
|
11850
|
+
} catch (e) {
|
|
11851
|
+
const err = e instanceof Error ? e : new Error(String(e));
|
|
11852
|
+
negCachedP2pLookups.set(uid, {
|
|
11853
|
+
error: err,
|
|
11854
|
+
expires: Date.now() + P2P_LOOKUP_NEG_CACHE_TTL_MS
|
|
11855
|
+
});
|
|
11856
|
+
throw err;
|
|
11857
|
+
} finally {
|
|
11858
|
+
inflightP2pLookups.delete(uid);
|
|
11859
|
+
}
|
|
11860
|
+
}
|
|
11861
|
+
async _doP2pUidLookupWork(sock, uid) {
|
|
11731
11862
|
const log = (msg) => this.discoveryLogger?.log?.(`[P2P] ${msg}`);
|
|
11732
11863
|
const shortUid = uid.length > 7 ? `${uid.slice(0, 5)}\u2026${uid.slice(-2)}` : uid;
|
|
11733
11864
|
const t0 = Date.now();
|
|
@@ -12195,6 +12326,23 @@ var BcUdpStream = class extends import_node_events3.EventEmitter {
|
|
|
12195
12326
|
log(
|
|
12196
12327
|
`local discovery: mode=${localMode} uid=${shortUid} ports=[${ports.join(", ")}] broadcasts=[${broadcastHosts.join(", ")}]${directHost ? ` direct=${directHost}` : ""} localBindPort=${localPort} timeout=${discoveryTimeout}ms`
|
|
12197
12328
|
);
|
|
12329
|
+
if (directHost && localMode === "local-direct") {
|
|
12330
|
+
try {
|
|
12331
|
+
const egress = await probeEgressForHost(directHost, ports[0] ?? 2015);
|
|
12332
|
+
const sameSubnet = isSameSubnetAsAnyLocalIface(directHost, egress);
|
|
12333
|
+
if (sameSubnet === "mismatch") {
|
|
12334
|
+
log(
|
|
12335
|
+
`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.`
|
|
12336
|
+
);
|
|
12337
|
+
} else {
|
|
12338
|
+
log(
|
|
12339
|
+
`egress for ${directHost} \u2192 src=${egress.localAddress}` + (sameSubnet === "same" ? ` (same subnet \u2713)` : ` (subnet relationship unknown)`)
|
|
12340
|
+
);
|
|
12341
|
+
}
|
|
12342
|
+
} catch (e) {
|
|
12343
|
+
this.emit("debug", "egress_probe_failed", e);
|
|
12344
|
+
}
|
|
12345
|
+
}
|
|
12198
12346
|
let bytesSent = 0;
|
|
12199
12347
|
let pktsRecv = 0;
|
|
12200
12348
|
sock.on("message", () => {
|
|
@@ -12861,35 +13009,88 @@ function decodeHeader(buf) {
|
|
|
12861
13009
|
return { header, headerLen, messageKey };
|
|
12862
13010
|
}
|
|
12863
13011
|
var BaichuanFrameParser = class {
|
|
13012
|
+
/** Retained-but-unconsumed contiguous bytes from previous push() calls. */
|
|
12864
13013
|
buffer = Buffer.alloc(0);
|
|
13014
|
+
/** Chunks received since the last materialization, not yet concatenated. */
|
|
13015
|
+
pending = [];
|
|
13016
|
+
/** Total bytes held in `pending` (kept in sync to avoid re-summing). */
|
|
13017
|
+
pendingLen = 0;
|
|
13018
|
+
/**
|
|
13019
|
+
* Total contiguous bytes (`buffer` + `pending`) required before the next
|
|
13020
|
+
* parse attempt can make progress. While buffered bytes stay below this,
|
|
13021
|
+
* incoming chunks are merely stashed in `pending` with no copy. This is
|
|
13022
|
+
* the mechanism that turns the worst case (a large frame fragmented over
|
|
13023
|
+
* many small TCP chunks) from O(n²) into O(n): we concatenate once, when
|
|
13024
|
+
* enough bytes have arrived, instead of on every chunk.
|
|
13025
|
+
*
|
|
13026
|
+
* Starts at 4 — the minimum needed to inspect the magic header.
|
|
13027
|
+
*/
|
|
13028
|
+
needed = 4;
|
|
13029
|
+
/**
|
|
13030
|
+
* Collapse `this.buffer` + all `pending` chunks into a single contiguous
|
|
13031
|
+
* buffer. The retained leftover is copied at most once per materialize(),
|
|
13032
|
+
* and materialize() only runs when `needed` bytes are available — so a
|
|
13033
|
+
* fragmented frame is assembled with a single concat, not one per chunk.
|
|
13034
|
+
*/
|
|
13035
|
+
materialize() {
|
|
13036
|
+
if (this.pendingLen === 0) return;
|
|
13037
|
+
if (this.buffer.length === 0 && this.pending.length === 1) {
|
|
13038
|
+
this.buffer = this.pending[0];
|
|
13039
|
+
} else {
|
|
13040
|
+
const parts = this.buffer.length === 0 ? this.pending : [this.buffer, ...this.pending];
|
|
13041
|
+
this.buffer = Buffer.concat(parts);
|
|
13042
|
+
}
|
|
13043
|
+
this.pending = [];
|
|
13044
|
+
this.pendingLen = 0;
|
|
13045
|
+
}
|
|
13046
|
+
/** Total buffered bytes, whether materialized or still pending. */
|
|
13047
|
+
get available() {
|
|
13048
|
+
return this.buffer.length + this.pendingLen;
|
|
13049
|
+
}
|
|
12865
13050
|
push(chunk) {
|
|
12866
13051
|
if (chunk.length === 0) return [];
|
|
12867
|
-
|
|
12868
|
-
this.
|
|
13052
|
+
this.pending.push(chunk);
|
|
13053
|
+
this.pendingLen += chunk.length;
|
|
13054
|
+
if (this.available < this.needed) return [];
|
|
13055
|
+
this.materialize();
|
|
12869
13056
|
const out = [];
|
|
12870
13057
|
while (true) {
|
|
12871
|
-
if (this.buffer.length < 4)
|
|
13058
|
+
if (this.buffer.length < 4) {
|
|
13059
|
+
this.needed = 4;
|
|
13060
|
+
break;
|
|
13061
|
+
}
|
|
12872
13062
|
if (!this.buffer.subarray(0, 4).equals(BC_MAGIC) && !this.buffer.subarray(0, 4).equals(BC_MAGIC_REV)) {
|
|
12873
13063
|
const idx = this.buffer.indexOf(BC_MAGIC);
|
|
12874
13064
|
const idxRev = this.buffer.indexOf(BC_MAGIC_REV);
|
|
12875
13065
|
const next = idx === -1 ? idxRev : idxRev === -1 ? idx : Math.min(idx, idxRev);
|
|
12876
13066
|
if (next === -1) {
|
|
12877
13067
|
this.buffer = this.buffer.subarray(Math.max(0, this.buffer.length - 3));
|
|
13068
|
+
this.needed = 4;
|
|
12878
13069
|
break;
|
|
12879
13070
|
}
|
|
12880
13071
|
this.buffer = this.buffer.subarray(next);
|
|
12881
|
-
if (this.buffer.length < 20)
|
|
13072
|
+
if (this.buffer.length < 20) {
|
|
13073
|
+
this.needed = 20;
|
|
13074
|
+
break;
|
|
13075
|
+
}
|
|
13076
|
+
}
|
|
13077
|
+
if (this.buffer.length < 20) {
|
|
13078
|
+
this.needed = 20;
|
|
13079
|
+
break;
|
|
12882
13080
|
}
|
|
12883
|
-
if (this.buffer.length < 20) break;
|
|
12884
13081
|
let headerInfo;
|
|
12885
13082
|
try {
|
|
12886
13083
|
headerInfo = decodeHeader(this.buffer);
|
|
12887
13084
|
} catch {
|
|
13085
|
+
this.needed = 24;
|
|
12888
13086
|
break;
|
|
12889
13087
|
}
|
|
12890
13088
|
const { header, headerLen, messageKey } = headerInfo;
|
|
12891
13089
|
const frameLen = headerLen + header.bodyLen;
|
|
12892
|
-
if (this.buffer.length < frameLen)
|
|
13090
|
+
if (this.buffer.length < frameLen) {
|
|
13091
|
+
this.needed = frameLen;
|
|
13092
|
+
break;
|
|
13093
|
+
}
|
|
12893
13094
|
const raw = this.buffer.subarray(0, frameLen);
|
|
12894
13095
|
const body = raw.subarray(headerLen);
|
|
12895
13096
|
let extLen = 0;
|
|
@@ -12901,6 +13102,7 @@ var BaichuanFrameParser = class {
|
|
|
12901
13102
|
const payload = body.subarray(extLen);
|
|
12902
13103
|
out.push({ header, body, extension, payload, messageKey, raw });
|
|
12903
13104
|
this.buffer = this.buffer.subarray(frameLen);
|
|
13105
|
+
this.needed = 4;
|
|
12904
13106
|
}
|
|
12905
13107
|
return out;
|
|
12906
13108
|
}
|