@naeemo/capnp 0.9.0 → 0.9.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/index.js CHANGED
@@ -106,6 +106,13 @@ function encodeStructPointer(offset, dataWords, pointerCount) {
106
106
  function encodeListPointer(offset, elementSize, elementCount) {
107
107
  return (BigInt(offset < 0 ? offset + 1073741824 : offset) & BigInt(1073741823)) << BigInt(2) | BigInt(1) | BigInt(elementSize) << BigInt(32) | BigInt(elementCount) << BigInt(35);
108
108
  }
109
+ /**
110
+ * 编码 Far 指针
111
+ */
112
+ function encodeFarPointer(segment, offset, doubleFar = false) {
113
+ const offsetBits = BigInt(offset) & BigInt(536870911);
114
+ return (BigInt(segment) & BigInt(4294967295)) << BigInt(32) | offsetBits << BigInt(3) | (doubleFar ? BigInt(1) << BigInt(2) : BigInt(0)) | BigInt(2);
115
+ }
109
116
 
110
117
  //#endregion
111
118
  //#region src/core/segment.ts
@@ -550,21 +557,38 @@ var ListBuilder = class {
550
557
  * Cap'n Proto MessageReader
551
558
  * 纯 TypeScript 实现
552
559
  */
560
+ /** 默认安全选项 */
561
+ const DEFAULT_SECURITY_OPTIONS = {
562
+ maxSegments: 64,
563
+ maxTotalSize: 64 * 1024 * 1024,
564
+ strictMode: false
565
+ };
553
566
  var MessageReader = class {
554
567
  segments;
555
- constructor(buffer) {
568
+ securityOptions;
569
+ constructor(buffer, securityOptions) {
570
+ this.securityOptions = {
571
+ ...DEFAULT_SECURITY_OPTIONS,
572
+ ...securityOptions
573
+ };
556
574
  const uint8Array = buffer instanceof ArrayBuffer ? new Uint8Array(buffer) : buffer;
557
575
  this.segments = [];
576
+ if (this.securityOptions.strictMode && uint8Array.byteLength > this.securityOptions.maxTotalSize) throw new Error(`Message size (${uint8Array.byteLength} bytes) exceeds maximum allowed size (${this.securityOptions.maxTotalSize} bytes)`);
558
577
  if (uint8Array.byteLength < 8) return;
559
578
  const view = new DataView(uint8Array.buffer, uint8Array.byteOffset, uint8Array.byteLength);
560
579
  const firstWordLow = view.getUint32(0, true);
561
580
  const firstWordHigh = view.getUint32(4, true);
562
581
  const segmentCount = (firstWordLow & 4294967295) + 1;
563
582
  const firstSegmentSize = firstWordHigh;
583
+ if (segmentCount > this.securityOptions.maxSegments) {
584
+ if (this.securityOptions.strictMode) throw new Error(`Segment count (${segmentCount}) exceeds maximum allowed (${this.securityOptions.maxSegments})`);
585
+ return;
586
+ }
564
587
  let offset = 8;
565
588
  const segmentSizes = [firstSegmentSize];
566
589
  for (let i = 1; i < segmentCount; i++) {
567
590
  if (offset + 4 > uint8Array.byteLength) {
591
+ if (this.securityOptions.strictMode) throw new Error(`Message ended prematurely while reading segment ${i} size (offset: ${offset}, length: ${uint8Array.byteLength})`);
568
592
  this.segments = [];
569
593
  return;
570
594
  }
@@ -573,12 +597,24 @@ var MessageReader = class {
573
597
  }
574
598
  offset = offset + 7 & -8;
575
599
  if (offset > uint8Array.byteLength) {
600
+ if (this.securityOptions.strictMode) throw new Error(`Insufficient data for segment table (offset: ${offset}, length: ${uint8Array.byteLength})`);
576
601
  this.segments = [];
577
602
  return;
578
603
  }
604
+ const headerSize = offset;
605
+ let totalBodySize = 0;
606
+ for (const size of segmentSizes) totalBodySize += size * WORD_SIZE;
607
+ const expectedTotalSize = headerSize + totalBodySize;
608
+ if (expectedTotalSize > this.securityOptions.maxTotalSize) {
609
+ if (this.securityOptions.strictMode) throw new Error(`Total message size (${expectedTotalSize} bytes) exceeds maximum allowed size (${this.securityOptions.maxTotalSize} bytes)`);
610
+ return;
611
+ }
579
612
  this.segments = [];
580
613
  for (const size of segmentSizes) {
581
- if (offset + size * WORD_SIZE > uint8Array.byteLength) break;
614
+ if (offset + size * WORD_SIZE > uint8Array.byteLength) {
615
+ if (this.securityOptions.strictMode) throw new Error(`Insufficient data for segment (expected ${size * WORD_SIZE} bytes at offset ${offset}, but message is ${uint8Array.byteLength} bytes)`);
616
+ break;
617
+ }
582
618
  const segmentBuffer = uint8Array.slice(offset, offset + size * WORD_SIZE);
583
619
  this.segments.push(Segment.fromBuffer(segmentBuffer.buffer));
584
620
  offset += size * WORD_SIZE;
@@ -672,6 +708,12 @@ var MessageReader = class {
672
708
  get segmentCount() {
673
709
  return this.segments.length;
674
710
  }
711
+ /**
712
+ * 获取当前安全选项配置
713
+ */
714
+ getSecurityOptions() {
715
+ return { ...this.securityOptions };
716
+ }
675
717
  };
676
718
  /**
677
719
  * 结构读取器
@@ -1655,6 +1697,18 @@ var WebSocketTransport = class WebSocketTransport {
1655
1697
  get connected() {
1656
1698
  return this._connected;
1657
1699
  }
1700
+ getCompressionState() {
1701
+ return {
1702
+ enabled: false,
1703
+ algorithm: "none",
1704
+ bytesSent: 0,
1705
+ bytesReceived: 0,
1706
+ uncompressedBytesSent: 0,
1707
+ uncompressedBytesReceived: 0,
1708
+ messagesCompressed: 0,
1709
+ messagesDecompressed: 0
1710
+ };
1711
+ }
1658
1712
  connect(url) {
1659
1713
  this.ws = new WebSocket(url);
1660
1714
  this.ws.binaryType = this.options.binaryType ?? "arraybuffer";
@@ -1811,6 +1865,432 @@ var WebSocketTransport = class WebSocketTransport {
1811
1865
  }
1812
1866
  };
1813
1867
 
1868
+ //#endregion
1869
+ //#region node_modules/.pnpm/lz4js@0.2.0/node_modules/lz4js/util.js
1870
+ var require_util = /* @__PURE__ */ __commonJSMin(((exports) => {
1871
+ exports.hashU32 = function hashU32(a) {
1872
+ a = a | 0;
1873
+ a = a + 2127912214 + (a << 12) | 0;
1874
+ a = a ^ -949894596 ^ a >>> 19;
1875
+ a = a + 374761393 + (a << 5) | 0;
1876
+ a = a + -744332180 ^ a << 9;
1877
+ a = a + -42973499 + (a << 3) | 0;
1878
+ return a ^ -1252372727 ^ a >>> 16 | 0;
1879
+ };
1880
+ exports.readU64 = function readU64(b, n) {
1881
+ var x = 0;
1882
+ x |= b[n++] << 0;
1883
+ x |= b[n++] << 8;
1884
+ x |= b[n++] << 16;
1885
+ x |= b[n++] << 24;
1886
+ x |= b[n++] << 32;
1887
+ x |= b[n++] << 40;
1888
+ x |= b[n++] << 48;
1889
+ x |= b[n++] << 56;
1890
+ return x;
1891
+ };
1892
+ exports.readU32 = function readU32(b, n) {
1893
+ var x = 0;
1894
+ x |= b[n++] << 0;
1895
+ x |= b[n++] << 8;
1896
+ x |= b[n++] << 16;
1897
+ x |= b[n++] << 24;
1898
+ return x;
1899
+ };
1900
+ exports.writeU32 = function writeU32(b, n, x) {
1901
+ b[n++] = x >> 0 & 255;
1902
+ b[n++] = x >> 8 & 255;
1903
+ b[n++] = x >> 16 & 255;
1904
+ b[n++] = x >> 24 & 255;
1905
+ };
1906
+ exports.imul = function imul(a, b) {
1907
+ var ah = a >>> 16;
1908
+ var al = a & 65535;
1909
+ var bh = b >>> 16;
1910
+ var bl = b & 65535;
1911
+ return al * bl + (ah * bl + al * bh << 16) | 0;
1912
+ };
1913
+ }));
1914
+
1915
+ //#endregion
1916
+ //#region node_modules/.pnpm/lz4js@0.2.0/node_modules/lz4js/xxh32.js
1917
+ var require_xxh32 = /* @__PURE__ */ __commonJSMin(((exports) => {
1918
+ var util = require_util();
1919
+ var prime1 = 2654435761;
1920
+ var prime2 = 2246822519;
1921
+ var prime3 = 3266489917;
1922
+ var prime4 = 668265263;
1923
+ var prime5 = 374761393;
1924
+ function rotl32(x, r) {
1925
+ x = x | 0;
1926
+ r = r | 0;
1927
+ return x >>> (32 - r | 0) | x << r | 0;
1928
+ }
1929
+ function rotmul32(h, r, m) {
1930
+ h = h | 0;
1931
+ r = r | 0;
1932
+ m = m | 0;
1933
+ return util.imul(h >>> (32 - r | 0) | h << r, m) | 0;
1934
+ }
1935
+ function shiftxor32(h, s) {
1936
+ h = h | 0;
1937
+ s = s | 0;
1938
+ return h >>> s ^ h | 0;
1939
+ }
1940
+ function xxhapply(h, src, m0, s, m1) {
1941
+ return rotmul32(util.imul(src, m0) + h, s, m1);
1942
+ }
1943
+ function xxh1(h, src, index) {
1944
+ return rotmul32(h + util.imul(src[index], prime5), 11, prime1);
1945
+ }
1946
+ function xxh4(h, src, index) {
1947
+ return xxhapply(h, util.readU32(src, index), prime3, 17, prime4);
1948
+ }
1949
+ function xxh16(h, src, index) {
1950
+ return [
1951
+ xxhapply(h[0], util.readU32(src, index + 0), prime2, 13, prime1),
1952
+ xxhapply(h[1], util.readU32(src, index + 4), prime2, 13, prime1),
1953
+ xxhapply(h[2], util.readU32(src, index + 8), prime2, 13, prime1),
1954
+ xxhapply(h[3], util.readU32(src, index + 12), prime2, 13, prime1)
1955
+ ];
1956
+ }
1957
+ function xxh32(seed, src, index, len) {
1958
+ var h, l = len;
1959
+ if (len >= 16) {
1960
+ h = [
1961
+ seed + prime1 + prime2,
1962
+ seed + prime2,
1963
+ seed,
1964
+ seed - prime1
1965
+ ];
1966
+ while (len >= 16) {
1967
+ h = xxh16(h, src, index);
1968
+ index += 16;
1969
+ len -= 16;
1970
+ }
1971
+ h = rotl32(h[0], 1) + rotl32(h[1], 7) + rotl32(h[2], 12) + rotl32(h[3], 18) + l;
1972
+ } else h = seed + prime5 + len >>> 0;
1973
+ while (len >= 4) {
1974
+ h = xxh4(h, src, index);
1975
+ index += 4;
1976
+ len -= 4;
1977
+ }
1978
+ while (len > 0) {
1979
+ h = xxh1(h, src, index);
1980
+ index++;
1981
+ len--;
1982
+ }
1983
+ h = shiftxor32(util.imul(shiftxor32(util.imul(shiftxor32(h, 15), prime2), 13), prime3), 16);
1984
+ return h >>> 0;
1985
+ }
1986
+ exports.hash = xxh32;
1987
+ }));
1988
+
1989
+ //#endregion
1990
+ //#region node_modules/.pnpm/lz4js@0.2.0/node_modules/lz4js/lz4.js
1991
+ var require_lz4 = /* @__PURE__ */ __commonJSMin(((exports) => {
1992
+ var xxhash = require_xxh32();
1993
+ var util = require_util();
1994
+ var minMatch = 4;
1995
+ var minLength = 13;
1996
+ var searchLimit = 5;
1997
+ var skipTrigger = 6;
1998
+ var hashSize = 65536;
1999
+ var mlBits = 4;
2000
+ var mlMask = (1 << mlBits) - 1;
2001
+ var runMask = 15;
2002
+ var blockBuf = makeBuffer(5 << 20);
2003
+ var hashTable = makeHashTable();
2004
+ var magicNum = 407708164;
2005
+ var fdContentChksum = 4;
2006
+ var fdContentSize = 8;
2007
+ var fdBlockChksum = 16;
2008
+ var fdVersion = 64;
2009
+ var fdVersionMask = 192;
2010
+ var bsUncompressed = 2147483648;
2011
+ var bsDefault = 7;
2012
+ var bsShift = 4;
2013
+ var bsMask = 7;
2014
+ var bsMap = {
2015
+ 4: 65536,
2016
+ 5: 262144,
2017
+ 6: 1048576,
2018
+ 7: 4194304
2019
+ };
2020
+ function makeHashTable() {
2021
+ try {
2022
+ return new Uint32Array(hashSize);
2023
+ } catch (error) {
2024
+ var hashTable = new Array(hashSize);
2025
+ for (var i = 0; i < hashSize; i++) hashTable[i] = 0;
2026
+ return hashTable;
2027
+ }
2028
+ }
2029
+ function clearHashTable(table) {
2030
+ for (var i = 0; i < hashSize; i++) hashTable[i] = 0;
2031
+ }
2032
+ function makeBuffer(size) {
2033
+ try {
2034
+ return new Uint8Array(size);
2035
+ } catch (error) {
2036
+ var buf = new Array(size);
2037
+ for (var i = 0; i < size; i++) buf[i] = 0;
2038
+ return buf;
2039
+ }
2040
+ }
2041
+ function sliceArray(array, start, end) {
2042
+ if (Uint8Array.prototype.slice) return array.slice(start, end);
2043
+ else {
2044
+ var len = array.length;
2045
+ start = start | 0;
2046
+ start = start < 0 ? Math.max(len + start, 0) : Math.min(start, len);
2047
+ end = end === void 0 ? len : end | 0;
2048
+ end = end < 0 ? Math.max(len + end, 0) : Math.min(end, len);
2049
+ var arraySlice = new Uint8Array(end - start);
2050
+ for (var i = start, n = 0; i < end;) arraySlice[n++] = array[i++];
2051
+ return arraySlice;
2052
+ }
2053
+ }
2054
+ exports.compressBound = function compressBound(n) {
2055
+ return n + n / 255 + 16 | 0;
2056
+ };
2057
+ exports.decompressBound = function decompressBound(src) {
2058
+ var sIndex = 0;
2059
+ if (util.readU32(src, sIndex) !== magicNum) throw new Error("invalid magic number");
2060
+ sIndex += 4;
2061
+ var descriptor = src[sIndex++];
2062
+ if ((descriptor & fdVersionMask) !== fdVersion) throw new Error("incompatible descriptor version " + (descriptor & fdVersionMask));
2063
+ var useBlockSum = (descriptor & fdBlockChksum) !== 0;
2064
+ var useContentSize = (descriptor & fdContentSize) !== 0;
2065
+ var bsIdx = src[sIndex++] >> bsShift & bsMask;
2066
+ if (bsMap[bsIdx] === void 0) throw new Error("invalid block size " + bsIdx);
2067
+ var maxBlockSize = bsMap[bsIdx];
2068
+ if (useContentSize) return util.readU64(src, sIndex);
2069
+ sIndex++;
2070
+ var maxSize = 0;
2071
+ while (true) {
2072
+ var blockSize = util.readU32(src, sIndex);
2073
+ sIndex += 4;
2074
+ if (blockSize & bsUncompressed) {
2075
+ blockSize &= ~bsUncompressed;
2076
+ maxSize += blockSize;
2077
+ } else maxSize += maxBlockSize;
2078
+ if (blockSize === 0) return maxSize;
2079
+ if (useBlockSum) sIndex += 4;
2080
+ sIndex += blockSize;
2081
+ }
2082
+ };
2083
+ exports.makeBuffer = makeBuffer;
2084
+ exports.decompressBlock = function decompressBlock(src, dst, sIndex, sLength, dIndex) {
2085
+ var mLength, mOffset, sEnd = sIndex + sLength, n, i;
2086
+ while (sIndex < sEnd) {
2087
+ var token = src[sIndex++];
2088
+ var literalCount = token >> 4;
2089
+ if (literalCount > 0) {
2090
+ if (literalCount === 15) while (true) {
2091
+ literalCount += src[sIndex];
2092
+ if (src[sIndex++] !== 255) break;
2093
+ }
2094
+ for (n = sIndex + literalCount; sIndex < n;) dst[dIndex++] = src[sIndex++];
2095
+ }
2096
+ if (sIndex >= sEnd) break;
2097
+ mLength = token & 15;
2098
+ mOffset = src[sIndex++] | src[sIndex++] << 8;
2099
+ if (mLength === 15) while (true) {
2100
+ mLength += src[sIndex];
2101
+ if (src[sIndex++] !== 255) break;
2102
+ }
2103
+ mLength += minMatch;
2104
+ for (i = dIndex - mOffset, n = i + mLength; i < n;) dst[dIndex++] = dst[i++] | 0;
2105
+ }
2106
+ return dIndex;
2107
+ };
2108
+ exports.compressBlock = function compressBlock(src, dst, sIndex, sLength, hashTable) {
2109
+ var mIndex, mAnchor, mLength, mOffset, mStep;
2110
+ var literalCount, dIndex = 0, sEnd = sLength + sIndex, n;
2111
+ mAnchor = sIndex;
2112
+ if (sLength >= minLength) {
2113
+ var searchMatchCount = (1 << skipTrigger) + 3;
2114
+ while (sIndex + minMatch < sEnd - searchLimit) {
2115
+ var seq = util.readU32(src, sIndex);
2116
+ var hash = util.hashU32(seq) >>> 0;
2117
+ hash = (hash >> 16 ^ hash) >>> 0 & 65535;
2118
+ mIndex = hashTable[hash] - 1;
2119
+ hashTable[hash] = sIndex + 1;
2120
+ if (mIndex < 0 || sIndex - mIndex >>> 16 > 0 || util.readU32(src, mIndex) !== seq) {
2121
+ mStep = searchMatchCount++ >> skipTrigger;
2122
+ sIndex += mStep;
2123
+ continue;
2124
+ }
2125
+ searchMatchCount = (1 << skipTrigger) + 3;
2126
+ literalCount = sIndex - mAnchor;
2127
+ mOffset = sIndex - mIndex;
2128
+ sIndex += minMatch;
2129
+ mIndex += minMatch;
2130
+ mLength = sIndex;
2131
+ while (sIndex < sEnd - searchLimit && src[sIndex] === src[mIndex]) {
2132
+ sIndex++;
2133
+ mIndex++;
2134
+ }
2135
+ mLength = sIndex - mLength;
2136
+ var token = mLength < mlMask ? mLength : mlMask;
2137
+ if (literalCount >= runMask) {
2138
+ dst[dIndex++] = (runMask << mlBits) + token;
2139
+ for (n = literalCount - runMask; n >= 255; n -= 255) dst[dIndex++] = 255;
2140
+ dst[dIndex++] = n;
2141
+ } else dst[dIndex++] = (literalCount << mlBits) + token;
2142
+ for (var i = 0; i < literalCount; i++) dst[dIndex++] = src[mAnchor + i];
2143
+ dst[dIndex++] = mOffset;
2144
+ dst[dIndex++] = mOffset >> 8;
2145
+ if (mLength >= mlMask) {
2146
+ for (n = mLength - mlMask; n >= 255; n -= 255) dst[dIndex++] = 255;
2147
+ dst[dIndex++] = n;
2148
+ }
2149
+ mAnchor = sIndex;
2150
+ }
2151
+ }
2152
+ if (mAnchor === 0) return 0;
2153
+ literalCount = sEnd - mAnchor;
2154
+ if (literalCount >= runMask) {
2155
+ dst[dIndex++] = runMask << mlBits;
2156
+ for (n = literalCount - runMask; n >= 255; n -= 255) dst[dIndex++] = 255;
2157
+ dst[dIndex++] = n;
2158
+ } else dst[dIndex++] = literalCount << mlBits;
2159
+ sIndex = mAnchor;
2160
+ while (sIndex < sEnd) dst[dIndex++] = src[sIndex++];
2161
+ return dIndex;
2162
+ };
2163
+ exports.decompressFrame = function decompressFrame(src, dst) {
2164
+ var useBlockSum, useContentSum, useContentSize, descriptor;
2165
+ var sIndex = 0;
2166
+ var dIndex = 0;
2167
+ if (util.readU32(src, sIndex) !== magicNum) throw new Error("invalid magic number");
2168
+ sIndex += 4;
2169
+ descriptor = src[sIndex++];
2170
+ if ((descriptor & fdVersionMask) !== fdVersion) throw new Error("incompatible descriptor version");
2171
+ useBlockSum = (descriptor & fdBlockChksum) !== 0;
2172
+ useContentSum = (descriptor & fdContentChksum) !== 0;
2173
+ useContentSize = (descriptor & fdContentSize) !== 0;
2174
+ if (bsMap[src[sIndex++] >> bsShift & bsMask] === void 0) throw new Error("invalid block size");
2175
+ if (useContentSize) sIndex += 8;
2176
+ sIndex++;
2177
+ while (true) {
2178
+ var compSize = util.readU32(src, sIndex);
2179
+ sIndex += 4;
2180
+ if (compSize === 0) break;
2181
+ if (useBlockSum) sIndex += 4;
2182
+ if ((compSize & bsUncompressed) !== 0) {
2183
+ compSize &= ~bsUncompressed;
2184
+ for (var j = 0; j < compSize; j++) dst[dIndex++] = src[sIndex++];
2185
+ } else {
2186
+ dIndex = exports.decompressBlock(src, dst, sIndex, compSize, dIndex);
2187
+ sIndex += compSize;
2188
+ }
2189
+ }
2190
+ if (useContentSum) sIndex += 4;
2191
+ return dIndex;
2192
+ };
2193
+ exports.compressFrame = function compressFrame(src, dst) {
2194
+ var dIndex = 0;
2195
+ util.writeU32(dst, dIndex, magicNum);
2196
+ dIndex += 4;
2197
+ dst[dIndex++] = fdVersion;
2198
+ dst[dIndex++] = bsDefault << bsShift;
2199
+ dst[dIndex] = xxhash.hash(0, dst, 4, dIndex - 4) >> 8;
2200
+ dIndex++;
2201
+ var maxBlockSize = bsMap[bsDefault];
2202
+ var remaining = src.length;
2203
+ var sIndex = 0;
2204
+ clearHashTable(hashTable);
2205
+ while (remaining > 0) {
2206
+ var compSize = 0;
2207
+ var blockSize = remaining > maxBlockSize ? maxBlockSize : remaining;
2208
+ compSize = exports.compressBlock(src, blockBuf, sIndex, blockSize, hashTable);
2209
+ if (compSize > blockSize || compSize === 0) {
2210
+ util.writeU32(dst, dIndex, 2147483648 | blockSize);
2211
+ dIndex += 4;
2212
+ for (var z = sIndex + blockSize; sIndex < z;) dst[dIndex++] = src[sIndex++];
2213
+ remaining -= blockSize;
2214
+ } else {
2215
+ util.writeU32(dst, dIndex, compSize);
2216
+ dIndex += 4;
2217
+ for (var j = 0; j < compSize;) dst[dIndex++] = blockBuf[j++];
2218
+ sIndex += blockSize;
2219
+ remaining -= blockSize;
2220
+ }
2221
+ }
2222
+ util.writeU32(dst, dIndex, 0);
2223
+ dIndex += 4;
2224
+ return dIndex;
2225
+ };
2226
+ exports.decompress = function decompress(src, maxSize) {
2227
+ var dst, size;
2228
+ if (maxSize === void 0) maxSize = exports.decompressBound(src);
2229
+ dst = exports.makeBuffer(maxSize);
2230
+ size = exports.decompressFrame(src, dst);
2231
+ if (size !== maxSize) dst = sliceArray(dst, 0, size);
2232
+ return dst;
2233
+ };
2234
+ exports.compress = function compress(src, maxSize) {
2235
+ var dst, size;
2236
+ if (maxSize === void 0) maxSize = exports.compressBound(src.length);
2237
+ dst = exports.makeBuffer(maxSize);
2238
+ size = exports.compressFrame(src, dst);
2239
+ if (size !== maxSize) dst = sliceArray(dst, 0, size);
2240
+ return dst;
2241
+ };
2242
+ }));
2243
+
2244
+ //#endregion
2245
+ //#region src/compression/lz4.ts
2246
+ var import_lz4 = require_lz4();
2247
+ /**
2248
+ * Default compression options
2249
+ */
2250
+ const DEFAULT_COMPRESSION_OPTIONS = {
2251
+ threshold: 1024,
2252
+ acceleration: 1,
2253
+ level: 0
2254
+ };
2255
+ /**
2256
+ * Compress data using LZ4
2257
+ * @param data - Data to compress
2258
+ * @param options - Compression options
2259
+ * @returns Compressed data, or null if compression failed or not beneficial
2260
+ */
2261
+ function compress(data, options = {}) {
2262
+ const opts = {
2263
+ ...DEFAULT_COMPRESSION_OPTIONS,
2264
+ ...options
2265
+ };
2266
+ if (data.length < opts.threshold) return null;
2267
+ try {
2268
+ const compressed = (0, import_lz4.compress)(Array.from(data));
2269
+ if (compressed.length >= data.length) return null;
2270
+ return new Uint8Array(compressed);
2271
+ } catch (_error) {
2272
+ return null;
2273
+ }
2274
+ }
2275
+ /**
2276
+ * Decompress LZ4 compressed data
2277
+ * @param data - Compressed data
2278
+ * @param originalSize - Original uncompressed size
2279
+ * @returns Decompressed data, or null if decompression failed
2280
+ */
2281
+ function decompress(data, originalSize) {
2282
+ try {
2283
+ const decompressed = (0, import_lz4.decompress)(Array.from(data), originalSize);
2284
+ return new Uint8Array(decompressed);
2285
+ } catch (_error) {
2286
+ return null;
2287
+ }
2288
+ }
2289
+ /**
2290
+ * Alias for decompress - same function, different name for API compatibility
2291
+ */
2292
+ const uncompress = decompress;
2293
+
1814
2294
  //#endregion
1815
2295
  //#region src/rpc/tcp-transport.ts
1816
2296
  /**
@@ -1819,11 +2299,20 @@ var WebSocketTransport = class WebSocketTransport {
1819
2299
  * Implements RpcTransport over raw TCP socket for C++ interop testing.
1820
2300
  * This allows direct communication with the official Cap'n Proto C++ implementation.
1821
2301
  */
2302
+ const FRAME_MAGIC = 1280980035;
2303
+ const ALGORITHM_LZ4 = 1;
2304
+ const CAPABILITY_VERSION = 1;
2305
+ const DEFAULT_COMPRESSION_THRESHOLD = 256;
1822
2306
  /**
1823
2307
  * TCP Transport for Cap'n Proto RPC
1824
2308
  *
1825
2309
  * Uses length-prefixed binary message framing compatible with Cap'n Proto C++ implementation.
1826
2310
  * Format: [4 bytes: message length (little-endian)] [N bytes: message data]
2311
+ *
2312
+ * With compression support:
2313
+ * - Connection establishment includes capability negotiation
2314
+ * - Messages larger than threshold are automatically compressed
2315
+ * - Frame header indicates compression algorithm used
1827
2316
  */
1828
2317
  var TcpTransport = class TcpTransport {
1829
2318
  socket = null;
@@ -1833,12 +2322,35 @@ var TcpTransport = class TcpTransport {
1833
2322
  pendingBuffer = Buffer.alloc(0);
1834
2323
  pendingLength = 0;
1835
2324
  hasPendingLength = false;
2325
+ compressionConfig;
2326
+ localSupportsLz4 = false;
2327
+ remoteSupportsLz4 = false;
2328
+ compressionEnabled = false;
2329
+ compressionState;
2330
+ negotiationComplete = false;
1836
2331
  onClose;
1837
2332
  onError;
1838
2333
  constructor(host, port, options = {}) {
1839
2334
  this.host = host;
1840
2335
  this.port = port;
1841
2336
  this.options = options;
2337
+ this.compressionConfig = {
2338
+ enabled: options.compression?.enabled ?? true,
2339
+ algorithm: options.compression?.algorithm ?? "lz4",
2340
+ thresholdBytes: options.compression?.thresholdBytes ?? DEFAULT_COMPRESSION_THRESHOLD,
2341
+ level: options.compression?.level ?? 1
2342
+ };
2343
+ this.localSupportsLz4 = this.compressionConfig.enabled && this.compressionConfig.algorithm === "lz4";
2344
+ this.compressionState = {
2345
+ enabled: false,
2346
+ algorithm: "none",
2347
+ bytesSent: 0,
2348
+ bytesReceived: 0,
2349
+ uncompressedBytesSent: 0,
2350
+ uncompressedBytesReceived: 0,
2351
+ messagesCompressed: 0,
2352
+ messagesDecompressed: 0
2353
+ };
1842
2354
  }
1843
2355
  static async connect(host, port, options) {
1844
2356
  const transport = new TcpTransport(host, port, options);
@@ -1853,6 +2365,9 @@ var TcpTransport = class TcpTransport {
1853
2365
  get connected() {
1854
2366
  return this._connected && this.socket?.readyState === "open";
1855
2367
  }
2368
+ getCompressionState() {
2369
+ return { ...this.compressionState };
2370
+ }
1856
2371
  connect() {
1857
2372
  return new Promise((resolve, reject) => {
1858
2373
  const timeout = setTimeout(() => {
@@ -1860,10 +2375,15 @@ var TcpTransport = class TcpTransport {
1860
2375
  reject(/* @__PURE__ */ new Error("Connection timeout"));
1861
2376
  }, this.options.connectTimeoutMs ?? 1e4);
1862
2377
  this.socket = new net$1.Socket();
1863
- this.socket.on("connect", () => {
2378
+ this.socket.on("connect", async () => {
1864
2379
  clearTimeout(timeout);
1865
2380
  this._connected = true;
1866
- resolve();
2381
+ try {
2382
+ await this.performCapabilityNegotiation();
2383
+ resolve();
2384
+ } catch (err) {
2385
+ reject(err instanceof Error ? err : new Error(String(err)));
2386
+ }
1867
2387
  });
1868
2388
  this.socket.on("data", (data) => {
1869
2389
  this.handleData(data);
@@ -1905,6 +2425,85 @@ var TcpTransport = class TcpTransport {
1905
2425
  socket.on("end", () => {
1906
2426
  this._connected = false;
1907
2427
  });
2428
+ if (this._connected) this.performCapabilityNegotiation().catch((err) => {
2429
+ this.onError?.(err instanceof Error ? err : new Error(String(err)));
2430
+ });
2431
+ }
2432
+ /**
2433
+ * Perform capability negotiation with the remote peer
2434
+ */
2435
+ async performCapabilityNegotiation() {
2436
+ if (!this.socket) throw new Error("Socket not connected");
2437
+ if (this.host === "" && this.port === 0) {
2438
+ this.remoteSupportsLz4 = (await this.readCapabilityNegotiation()).supportsLz4;
2439
+ await this.sendCapabilityNegotiation();
2440
+ } else {
2441
+ await this.sendCapabilityNegotiation();
2442
+ this.remoteSupportsLz4 = (await this.readCapabilityNegotiation()).supportsLz4;
2443
+ }
2444
+ this.compressionEnabled = this.localSupportsLz4 && this.remoteSupportsLz4;
2445
+ this.compressionState.enabled = this.compressionEnabled;
2446
+ this.compressionState.algorithm = this.compressionEnabled ? "lz4" : "none";
2447
+ this.negotiationComplete = true;
2448
+ }
2449
+ /**
2450
+ * Send capability negotiation message
2451
+ */
2452
+ async sendCapabilityNegotiation() {
2453
+ const caps = {
2454
+ type: "capabilities",
2455
+ supportsLz4: this.localSupportsLz4,
2456
+ version: CAPABILITY_VERSION
2457
+ };
2458
+ const data = Buffer.from(JSON.stringify(caps), "utf-8");
2459
+ const frame = Buffer.allocUnsafe(4 + data.length);
2460
+ frame.writeUInt32LE(data.length, 0);
2461
+ frame.set(data, 4);
2462
+ return new Promise((resolve, reject) => {
2463
+ this.socket.write(frame, (err) => {
2464
+ if (err) reject(err);
2465
+ else resolve();
2466
+ });
2467
+ });
2468
+ }
2469
+ /**
2470
+ * Read capability negotiation message
2471
+ */
2472
+ async readCapabilityNegotiation() {
2473
+ return new Promise((resolve, reject) => {
2474
+ const timeout = setTimeout(() => {
2475
+ reject(/* @__PURE__ */ new Error("Capability negotiation timeout"));
2476
+ }, 5e3);
2477
+ const checkBuffer = () => {
2478
+ if (this.pendingBuffer.length < 4) {
2479
+ this.socket.listenerCount("data");
2480
+ const dataHandler = () => {
2481
+ this.socket.off("data", dataHandler);
2482
+ checkBuffer();
2483
+ };
2484
+ this.socket.once("data", dataHandler);
2485
+ return;
2486
+ }
2487
+ const length = this.pendingBuffer.readUInt32LE(0);
2488
+ if (this.pendingBuffer.length < 4 + length) {
2489
+ const dataHandler = () => {
2490
+ this.socket.off("data", dataHandler);
2491
+ checkBuffer();
2492
+ };
2493
+ this.socket.once("data", dataHandler);
2494
+ return;
2495
+ }
2496
+ clearTimeout(timeout);
2497
+ const capsData = this.pendingBuffer.subarray(4, 4 + length);
2498
+ this.pendingBuffer = this.pendingBuffer.subarray(4 + length);
2499
+ try {
2500
+ resolve(JSON.parse(capsData.toString("utf-8")));
2501
+ } catch (err) {
2502
+ reject(/* @__PURE__ */ new Error(`Failed to parse capability negotiation: ${err}`));
2503
+ }
2504
+ };
2505
+ checkBuffer();
2506
+ });
1908
2507
  }
1909
2508
  handleData(data) {
1910
2509
  this.pendingBuffer = Buffer.concat([this.pendingBuffer, data]);
@@ -1920,14 +2519,50 @@ var TcpTransport = class TcpTransport {
1920
2519
  }
1921
2520
  const totalLength = 4 + this.pendingLength;
1922
2521
  if (this.pendingBuffer.length < totalLength) break;
1923
- const messageData = this.pendingBuffer.subarray(4, totalLength);
1924
- this.pendingBuffer = this.pendingBuffer.subarray(totalLength);
1925
- this.hasPendingLength = false;
1926
- try {
1927
- const message = deserializeRpcMessage(new Uint8Array(messageData));
1928
- this.handleRpcMessage(message);
1929
- } catch (err) {
1930
- this.onError?.(err instanceof Error ? err : new Error(String(err)));
2522
+ if (this.pendingBuffer.readUInt32LE(4) === FRAME_MAGIC && this.negotiationComplete) {
2523
+ if (this.pendingLength < 16) {
2524
+ this.onError?.(/* @__PURE__ */ new Error("Invalid compressed frame"));
2525
+ this.close(/* @__PURE__ */ new Error("Invalid compressed frame"));
2526
+ return;
2527
+ }
2528
+ const headerOffset = 4;
2529
+ const originalSize = this.pendingBuffer.readUInt32LE(headerOffset + 4);
2530
+ const compressedSize = this.pendingBuffer.readUInt32LE(headerOffset + 8);
2531
+ const algorithm = this.pendingBuffer.readUInt32LE(headerOffset + 12);
2532
+ const frameTotalLength = 20 + compressedSize;
2533
+ if (this.pendingBuffer.length < frameTotalLength) break;
2534
+ const compressedData = this.pendingBuffer.subarray(headerOffset + 16, frameTotalLength);
2535
+ this.pendingBuffer = this.pendingBuffer.subarray(frameTotalLength);
2536
+ this.hasPendingLength = false;
2537
+ try {
2538
+ let messageData;
2539
+ if (algorithm === ALGORITHM_LZ4) {
2540
+ const decompressed = decompress(compressedData, originalSize);
2541
+ if (!decompressed) throw new Error("LZ4 decompression failed");
2542
+ messageData = decompressed;
2543
+ this.compressionState.messagesDecompressed++;
2544
+ this.compressionState.bytesReceived += compressedData.length;
2545
+ this.compressionState.uncompressedBytesReceived += messageData.length;
2546
+ } else {
2547
+ messageData = new Uint8Array(compressedData);
2548
+ this.compressionState.bytesReceived += compressedData.length;
2549
+ this.compressionState.uncompressedBytesReceived += compressedData.length;
2550
+ }
2551
+ const message = deserializeRpcMessage(messageData);
2552
+ this.handleRpcMessage(message);
2553
+ } catch (err) {
2554
+ this.onError?.(err instanceof Error ? err : new Error(String(err)));
2555
+ }
2556
+ } else {
2557
+ const messageData = this.pendingBuffer.subarray(4, totalLength);
2558
+ this.pendingBuffer = this.pendingBuffer.subarray(totalLength);
2559
+ this.hasPendingLength = false;
2560
+ try {
2561
+ const message = deserializeRpcMessage(new Uint8Array(messageData));
2562
+ this.handleRpcMessage(message);
2563
+ } catch (err) {
2564
+ this.onError?.(err instanceof Error ? err : new Error(String(err)));
2565
+ }
1931
2566
  }
1932
2567
  }
1933
2568
  }
@@ -1939,10 +2574,31 @@ var TcpTransport = class TcpTransport {
1939
2574
  }
1940
2575
  async send(message) {
1941
2576
  if (!this.socket || this.socket.readyState !== "open") throw new Error("Socket not connected");
2577
+ if (!this.negotiationComplete) throw new Error("Capability negotiation not complete");
1942
2578
  const data = serializeRpcMessage(message);
1943
- const frame = Buffer.allocUnsafe(4 + data.length);
1944
- frame.writeUInt32LE(data.length, 0);
1945
- frame.set(data, 4);
2579
+ const shouldCompress = this.compressionEnabled && data.length >= this.compressionConfig.thresholdBytes;
2580
+ let frame;
2581
+ if (shouldCompress) {
2582
+ const compressed = compress(data, { threshold: 0 });
2583
+ if (!compressed) throw new Error("Compression failed");
2584
+ const frameLength = 16 + compressed.length;
2585
+ frame = Buffer.allocUnsafe(4 + frameLength);
2586
+ frame.writeUInt32LE(frameLength, 0);
2587
+ frame.writeUInt32LE(FRAME_MAGIC, 4);
2588
+ frame.writeUInt32LE(data.length, 8);
2589
+ frame.writeUInt32LE(compressed.length, 12);
2590
+ frame.writeUInt32LE(ALGORITHM_LZ4, 16);
2591
+ frame.set(compressed, 20);
2592
+ this.compressionState.bytesSent += compressed.length;
2593
+ this.compressionState.uncompressedBytesSent += data.length;
2594
+ this.compressionState.messagesCompressed++;
2595
+ } else {
2596
+ frame = Buffer.allocUnsafe(4 + data.length);
2597
+ frame.writeUInt32LE(data.length, 0);
2598
+ frame.set(data, 4);
2599
+ this.compressionState.bytesSent += data.length;
2600
+ this.compressionState.uncompressedBytesSent += data.length;
2601
+ }
1946
2602
  return new Promise((resolve, reject) => {
1947
2603
  this.socket.write(frame, (err) => {
1948
2604
  if (err) reject(err);
@@ -2010,6 +2666,18 @@ var EzRpcTransport = class EzRpcTransport {
2010
2666
  get connected() {
2011
2667
  return this._connected && this.socket !== null && !this.socket.destroyed;
2012
2668
  }
2669
+ getCompressionState() {
2670
+ return {
2671
+ enabled: false,
2672
+ algorithm: "none",
2673
+ bytesSent: 0,
2674
+ bytesReceived: 0,
2675
+ uncompressedBytesSent: 0,
2676
+ uncompressedBytesReceived: 0,
2677
+ messagesCompressed: 0,
2678
+ messagesDecompressed: 0
2679
+ };
2680
+ }
2013
2681
  doConnect() {
2014
2682
  return new Promise((resolve, reject) => {
2015
2683
  const timeout = setTimeout(() => {
@@ -9784,6 +10452,131 @@ var import_sender = /* @__PURE__ */ __toESM(require_sender(), 1);
9784
10452
  var import_websocket = /* @__PURE__ */ __toESM(require_websocket(), 1);
9785
10453
  var import_websocket_server = /* @__PURE__ */ __toESM(require_websocket_server(), 1);
9786
10454
 
10455
+ //#endregion
10456
+ //#region src/compression/frame.ts
10457
+ /**
10458
+ * LZ4 Frame Format Module
10459
+ *
10460
+ * Frame format: Magic(4) + Flags(1) + Length(4) + Payload
10461
+ * - Magic: 0x4C5A3401 (LZ4\0\x01)
10462
+ * - Flags: compression options and metadata
10463
+ * - Length: uncompressed data length (4 bytes, little-endian)
10464
+ * - Payload: compressed or uncompressed data
10465
+ */
10466
+ /**
10467
+ * Frame format constants
10468
+ */
10469
+ const LZ4_FRAME_MAGIC = 1280979969;
10470
+ const LZ4_FRAME_HEADER_SIZE = 9;
10471
+ /**
10472
+ * Frame flags
10473
+ */
10474
+ let FrameFlags = /* @__PURE__ */ function(FrameFlags) {
10475
+ /** Data is compressed */
10476
+ FrameFlags[FrameFlags["COMPRESSED"] = 1] = "COMPRESSED";
10477
+ /** Reserved for future use */
10478
+ FrameFlags[FrameFlags["RESERVED_1"] = 2] = "RESERVED_1";
10479
+ /** Reserved for future use */
10480
+ FrameFlags[FrameFlags["RESERVED_2"] = 4] = "RESERVED_2";
10481
+ /** Reserved for future use */
10482
+ FrameFlags[FrameFlags["RESERVED_3"] = 8] = "RESERVED_3";
10483
+ /** Reserved for future use */
10484
+ FrameFlags[FrameFlags["RESERVED_4"] = 16] = "RESERVED_4";
10485
+ /** Reserved for future use */
10486
+ FrameFlags[FrameFlags["RESERVED_5"] = 32] = "RESERVED_5";
10487
+ /** Reserved for future use */
10488
+ FrameFlags[FrameFlags["RESERVED_6"] = 64] = "RESERVED_6";
10489
+ /** Reserved for future use */
10490
+ FrameFlags[FrameFlags["RESERVED_7"] = 128] = "RESERVED_7";
10491
+ return FrameFlags;
10492
+ }({});
10493
+ /**
10494
+ * Check if data has LZ4 frame magic header
10495
+ */
10496
+ function hasFrameMagic(data) {
10497
+ if (data.length < 4) return false;
10498
+ return new DataView(data.buffer, data.byteOffset, data.byteLength).getUint32(0, true) === LZ4_FRAME_MAGIC;
10499
+ }
10500
+ /**
10501
+ * Parse frame header from data
10502
+ * @returns FrameHeader if valid, null otherwise
10503
+ */
10504
+ function parseFrameHeader(data) {
10505
+ if (data.length < LZ4_FRAME_HEADER_SIZE) return null;
10506
+ const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
10507
+ const magic = view.getUint32(0, true);
10508
+ if (magic !== LZ4_FRAME_MAGIC) return null;
10509
+ return {
10510
+ magic,
10511
+ flags: view.getUint8(4),
10512
+ length: view.getUint32(5, true)
10513
+ };
10514
+ }
10515
+ /**
10516
+ * Decompress framed data
10517
+ * @param data - Framed data (with or without frame header)
10518
+ * @param decompressFn - Decompression function for compressed payloads
10519
+ * @returns Decompressed data, or null if invalid
10520
+ */
10521
+ function decompressFrame(data, decompressFn) {
10522
+ const header = parseFrameHeader(data);
10523
+ if (!header) return data;
10524
+ const payloadOffset = LZ4_FRAME_HEADER_SIZE;
10525
+ if (data.length - payloadOffset < 0) return null;
10526
+ const payload = data.subarray(payloadOffset);
10527
+ if (header.flags & FrameFlags.COMPRESSED) return decompressFn(payload, header.length);
10528
+ return payload;
10529
+ }
10530
+
10531
+ //#endregion
10532
+ //#region src/compression/index.ts
10533
+ /**
10534
+ * Create default compression config
10535
+ */
10536
+ function createCompressionConfig(config) {
10537
+ return {
10538
+ enabled: false,
10539
+ algorithm: "lz4",
10540
+ threshold: 1024,
10541
+ level: 0,
10542
+ highCompression: false,
10543
+ ...config
10544
+ };
10545
+ }
10546
+ /**
10547
+ * Create empty compression stats
10548
+ */
10549
+ function createCompressionStats() {
10550
+ return {
10551
+ messagesCompressed: 0,
10552
+ messagesDecompressed: 0,
10553
+ bytesOriginal: 0,
10554
+ bytesCompressed: 0,
10555
+ compressionRatio: 1,
10556
+ savingsPercent: 0
10557
+ };
10558
+ }
10559
+ /**
10560
+ * Check if data is a compression frame (has LZ4 magic)
10561
+ * Works with both Buffer and Uint8Array
10562
+ */
10563
+ function isCompressionFrame(data) {
10564
+ return hasFrameMagic(new Uint8Array(data.buffer, data.byteOffset, data.byteLength));
10565
+ }
10566
+ /**
10567
+ * Try to decompress, return original if not compressed or decompression fails
10568
+ * Works with both Buffer and Uint8Array
10569
+ * Assumes data has LZ4 frame header (created by compressFrame)
10570
+ */
10571
+ function tryDecompress(data) {
10572
+ const uint8Data = data instanceof Buffer ? new Uint8Array(data.buffer, data.byteOffset, data.byteLength) : data;
10573
+ return decompressFrame(uint8Data, uncompress) ?? uint8Data;
10574
+ }
10575
+ /**
10576
+ * Default compression configuration
10577
+ */
10578
+ const DEFAULT_COMPRESSION_CONFIG = createCompressionConfig();
10579
+
9787
10580
  //#endregion
9788
10581
  //#region src/proxy/websocket-proxy.ts
9789
10582
  /**
@@ -9792,6 +10585,27 @@ var import_websocket_server = /* @__PURE__ */ __toESM(require_websocket_server()
9792
10585
  * Allows browsers to connect to native Cap'n Proto services (C++, etc.)
9793
10586
  * via WebSocket. Handles the protocol bridging between WebSocket (browser)
9794
10587
  * and raw TCP (Cap'n Proto services).
10588
+ *
10589
+ * Features:
10590
+ * - Independent compression configuration for WebSocket and TCP sides
10591
+ * - Automatic compression/decompression bridging
10592
+ * - Support for different compression settings on each side
10593
+ * - Backward compatibility with non-compressed peers
10594
+ */
10595
+ /**
10596
+ * Convert Buffer to Uint8Array without copying if possible
10597
+ */
10598
+ function bufferToUint8Array(buffer) {
10599
+ return new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength);
10600
+ }
10601
+ /**
10602
+ * Convert Uint8Array to Buffer without copying if possible
10603
+ */
10604
+ function uint8ArrayToBuffer(arr) {
10605
+ return Buffer.from(arr.buffer, arr.byteOffset, arr.byteLength);
10606
+ }
10607
+ /**
10608
+ * Represents a single proxy connection between WebSocket client and TCP server
9795
10609
  */
9796
10610
  var ProxyConnection = class extends EventEmitter {
9797
10611
  ws;
@@ -9799,16 +10613,22 @@ var ProxyConnection = class extends EventEmitter {
9799
10613
  stats;
9800
10614
  options;
9801
10615
  closed = false;
10616
+ wsCompressionConfig;
10617
+ tcpCompressionConfig;
9802
10618
  constructor(ws, options) {
9803
10619
  super();
9804
10620
  this.ws = ws;
9805
10621
  this.options = options;
10622
+ this.wsCompressionConfig = createCompressionConfig(options.compression?.ws);
10623
+ this.tcpCompressionConfig = createCompressionConfig(options.compression?.tcp);
9806
10624
  this.stats = {
9807
10625
  wsMessagesIn: 0,
9808
10626
  wsMessagesOut: 0,
9809
10627
  tcpBytesIn: 0,
9810
10628
  tcpBytesOut: 0,
9811
- connectedAt: /* @__PURE__ */ new Date()
10629
+ connectedAt: /* @__PURE__ */ new Date(),
10630
+ wsCompression: createCompressionStats(),
10631
+ tcpCompression: createCompressionStats()
9812
10632
  };
9813
10633
  this.setupWebSocket();
9814
10634
  }
@@ -9836,6 +10656,7 @@ var ProxyConnection = class extends EventEmitter {
9836
10656
  });
9837
10657
  this.tcpSocket.on("connect", () => {
9838
10658
  this.log("Connected to target TCP service");
10659
+ this.log(`Compression - WS: ${this.wsCompressionConfig.enabled ? "enabled" : "disabled"}, TCP: ${this.tcpCompressionConfig.enabled ? "enabled" : "disabled"}`);
9839
10660
  this.emit("connected");
9840
10661
  });
9841
10662
  this.tcpSocket.on("data", (data) => {
@@ -9855,29 +10676,78 @@ var ProxyConnection = class extends EventEmitter {
9855
10676
  this.close();
9856
10677
  });
9857
10678
  }
10679
+ /**
10680
+ * Handle incoming message from WebSocket client
10681
+ * - Decompress if compressed (from browser)
10682
+ * - Re-compress for TCP side if TCP compression is enabled
10683
+ * - Forward to TCP server
10684
+ */
9858
10685
  handleWebSocketMessage(data) {
9859
10686
  if (this.closed) return;
9860
10687
  const buffer = Buffer.isBuffer(data) ? data : Array.isArray(data) ? Buffer.concat(data) : Buffer.from(data);
9861
10688
  const maxSize = this.options.maxMessageSize ?? 16 * 1024 * 1024;
9862
- if (buffer.length > maxSize) {
9863
- this.log(`Message too large: ${buffer.length} bytes`);
10689
+ const uint8Data = bufferToUint8Array(buffer);
10690
+ const frameInfo = parseFrameHeader(uint8Data);
10691
+ const uncompressedSize = frameInfo ? frameInfo.length : buffer.length;
10692
+ if (uncompressedSize > maxSize) {
10693
+ this.log(`Message too large: ${uncompressedSize} bytes (max: ${maxSize})`);
9864
10694
  this.close();
9865
10695
  return;
9866
10696
  }
9867
10697
  this.stats.wsMessagesIn++;
9868
- this.stats.tcpBytesOut += buffer.length;
10698
+ let decompressedData;
10699
+ if (hasFrameMagic(uint8Data)) {
10700
+ const decompressed = decompressFrame(uint8Data, uncompress);
10701
+ if (decompressed !== null) {
10702
+ decompressedData = decompressed;
10703
+ this.log(`Decompressed WS message: ${buffer.length} -> ${decompressedData.length} bytes`);
10704
+ } else decompressedData = uint8Data;
10705
+ } else decompressedData = uint8Data;
10706
+ let dataToSend = decompressedData;
10707
+ if (this.tcpCompressionConfig.enabled) {
10708
+ const compressed = compress(decompressedData, { threshold: this.tcpCompressionConfig.threshold });
10709
+ if (compressed !== null) {
10710
+ dataToSend = compressed;
10711
+ this.log(`Compressed for TCP: ${decompressedData.length} -> ${dataToSend.length} bytes`);
10712
+ }
10713
+ }
10714
+ this.stats.tcpBytesOut += uint8ArrayToBuffer(dataToSend).length;
9869
10715
  if (this.tcpSocket?.writable) {
9870
- this.tcpSocket.write(buffer);
9871
- this.log(`Forwarded ${buffer.length} bytes to TCP`);
10716
+ const bufferToSend = uint8ArrayToBuffer(dataToSend);
10717
+ this.tcpSocket.write(bufferToSend);
10718
+ this.log(`Forwarded ${dataToSend.length} bytes to TCP (original: ${decompressedData.length})`);
9872
10719
  }
9873
10720
  }
10721
+ /**
10722
+ * Handle incoming data from TCP server
10723
+ * - Decompress if compressed (from native Cap'n Proto service)
10724
+ * - Re-compress for WebSocket side if WS compression is enabled
10725
+ * - Forward to WebSocket client
10726
+ */
9874
10727
  handleTcpData(data) {
9875
10728
  if (this.closed) return;
9876
10729
  this.stats.tcpBytesIn += data.length;
10730
+ const uint8Data = bufferToUint8Array(data);
10731
+ let decompressedData;
10732
+ if (hasFrameMagic(uint8Data)) {
10733
+ const decompressed = decompressFrame(uint8Data, uncompress);
10734
+ if (decompressed !== null) {
10735
+ decompressedData = decompressed;
10736
+ this.log(`Decompressed TCP message: ${data.length} -> ${decompressedData.length} bytes`);
10737
+ } else decompressedData = uint8Data;
10738
+ } else decompressedData = uint8Data;
10739
+ let dataToSend = decompressedData;
10740
+ if (this.wsCompressionConfig.enabled) {
10741
+ const compressed = compress(decompressedData, { threshold: this.wsCompressionConfig.threshold });
10742
+ if (compressed !== null) {
10743
+ dataToSend = compressed;
10744
+ this.log(`Compressed for WS: ${decompressedData.length} -> ${dataToSend.length} bytes`);
10745
+ }
10746
+ }
9877
10747
  this.stats.wsMessagesOut++;
9878
10748
  if (this.ws.readyState === import_websocket.default.OPEN) {
9879
- this.ws.send(data);
9880
- this.log(`Forwarded ${data.length} bytes to WebSocket`);
10749
+ this.ws.send(dataToSend);
10750
+ this.log(`Forwarded ${dataToSend.length} bytes to WebSocket (original: ${decompressedData.length})`);
9881
10751
  }
9882
10752
  }
9883
10753
  log(...args) {
@@ -9894,6 +10764,10 @@ var ProxyConnection = class extends EventEmitter {
9894
10764
  this.emit("closed");
9895
10765
  }
9896
10766
  };
10767
+ /**
10768
+ * WebSocket-to-TCP Proxy server
10769
+ * Manages multiple proxy connections
10770
+ */
9897
10771
  var CapnpWebSocketProxy = class extends EventEmitter {
9898
10772
  wss;
9899
10773
  connections = /* @__PURE__ */ new Map();
@@ -9951,6 +10825,9 @@ if (import.meta.url === `file://${process.argv[1]}`) {
9951
10825
  let targetHost = "localhost";
9952
10826
  let targetPort = 8081;
9953
10827
  let debug = false;
10828
+ let wsCompression = false;
10829
+ let tcpCompression = false;
10830
+ let compressionThreshold = 1024;
9954
10831
  for (let i = 0; i < args.length; i++) switch (args[i]) {
9955
10832
  case "--ws-port":
9956
10833
  case "-p":
@@ -9967,6 +10844,17 @@ if (import.meta.url === `file://${process.argv[1]}`) {
9967
10844
  case "-d":
9968
10845
  debug = true;
9969
10846
  break;
10847
+ case "--ws-compression":
10848
+ case "-w":
10849
+ wsCompression = true;
10850
+ break;
10851
+ case "--tcp-compression":
10852
+ case "-c":
10853
+ tcpCompression = true;
10854
+ break;
10855
+ case "--compression-threshold":
10856
+ compressionThreshold = Number.parseInt(args[++i], 10);
10857
+ break;
9970
10858
  case "--help":
9971
10859
  case "-h":
9972
10860
  console.log(`
@@ -9975,13 +10863,25 @@ Cap'n Proto WebSocket-to-TCP Proxy
9975
10863
  Usage: npx @naeemo/capnp proxy [options]
9976
10864
 
9977
10865
  Options:
9978
- -p, --ws-port <port> WebSocket server port (default: 8080)
9979
- -t, --target <host:port> Target TCP service (default: localhost:8081)
9980
- -d, --debug Enable debug logging
9981
- -h, --help Show this help
10866
+ -p, --ws-port <port> WebSocket server port (default: 8080)
10867
+ -t, --target <host:port> Target TCP service (default: localhost:8081)
10868
+ -d, --debug Enable debug logging
10869
+ -w, --ws-compression Enable WebSocket side compression
10870
+ -c, --tcp-compression Enable TCP side compression
10871
+ --compression-threshold <n> Minimum size to compress (default: 1024)
10872
+ -h, --help Show this help
9982
10873
 
9983
- Example:
10874
+ Examples:
9984
10875
  npx @naeemo/capnp proxy -p 9000 -t 192.168.1.100:7000
10876
+
10877
+ # Enable compression on WebSocket side only (for browser clients)
10878
+ npx @naeemo/capnp proxy -p 9000 -t localhost:7000 -w
10879
+
10880
+ # Enable compression on both sides
10881
+ npx @naeemo/capnp proxy -p 9000 -t localhost:7000 -w -c
10882
+
10883
+ # Enable compression with lower threshold
10884
+ npx @naeemo/capnp proxy -p 9000 -t localhost:7000 -w --compression-threshold 512
9985
10885
  `);
9986
10886
  process.exit(0);
9987
10887
  }
@@ -9989,11 +10889,26 @@ Example:
9989
10889
  wsPort,
9990
10890
  targetHost,
9991
10891
  targetPort,
9992
- debug
10892
+ debug,
10893
+ compression: {
10894
+ ws: {
10895
+ enabled: wsCompression,
10896
+ threshold: compressionThreshold
10897
+ },
10898
+ tcp: {
10899
+ enabled: tcpCompression,
10900
+ threshold: compressionThreshold
10901
+ }
10902
+ }
9993
10903
  });
9994
10904
  console.log("WebSocket-to-TCP Proxy started");
9995
10905
  console.log(` WebSocket: ws://localhost:${wsPort}`);
9996
10906
  console.log(` Target: ${targetHost}:${targetPort}`);
10907
+ if (wsCompression || tcpCompression) {
10908
+ console.log(" Compression:");
10909
+ console.log(` WebSocket: ${wsCompression ? "enabled" : "disabled"} (threshold: ${compressionThreshold})`);
10910
+ console.log(` TCP: ${tcpCompression ? "enabled" : "disabled"} (threshold: ${compressionThreshold})`);
10911
+ }
9997
10912
  proxy.on("connection", () => {
9998
10913
  console.log(`Active connections: ${proxy.getConnectionCount()}`);
9999
10914
  });
@@ -10008,5 +10923,5 @@ Example:
10008
10923
  }
10009
10924
 
10010
10925
  //#endregion
10011
- export { AnswerTable, BaseCapabilityClient, BulkTransfer, BulkTransferManager, CapnpWebSocketProxy, ConnectionManager, DEFAULT_BULK_CONFIG, DEFAULT_ESCROW_CONFIG, DEFAULT_FLOW_CONTROL, DEFAULT_JOIN_OPTIONS, DEFAULT_JOIN_SECURITY_POLICY, DEFAULT_REALTIME_CONFIG, DEFAULT_STREAMING_CAPABILITIES, DropPolicy, ElementSize, ExportTable, EzRpcTransport, ImportTable, Level3Handlers, Level4Handlers, ListBuilder, ListReader, MemoryPool, MessageBuilder, MessageReader, MultiSegmentMessageBuilder, OptimizedRpcMessageBuilder, PIPELINE_CLIENT_SYMBOL, PipelineOpTracker, PipelineResolutionTracker, PointerTag, QuestionTable, QueuedCallManager, RealtimeStream, RealtimeStreamManager, RestoreHandler, RpcConnection, SCHEMA_MESSAGE_TYPES, SchemaCapabilityClient, SchemaCapabilityServer, SchemaFormat, Segment, Stream, StreamManager, StreamPriority, StreamType, StreamingRpcConnection, StructBuilder, StructReader, SturdyRefManager, TcpTransport, UnionBuilder, UnionReader, WORD_SIZE, WebSocketTransport, configureGlobalMemoryPool, createBulkTransferManager, createDynamicReader, createDynamicReaderByTypeId, createDynamicReaderFromStruct, createDynamicWriter, createDynamicWriterByTypeId, createNestedDynamicWriter, createPipelineClient, createProvisionId, createRealtimeStreamManager, createRecipientId, createSchemaRegistry, createStream, createStreamManager, createStreamingConnection, createSturdyRef, createThirdPartyCapId, createUnionBuilder, createUnionReader, createZeroCopyView, decodePointer, deserializeRpcMessage, deserializeSchemaRequest, deserializeSchemaResponse, deserializeSturdyRef, disableDebug, dumpDynamicReader, enableDebug, encodeListPointer, encodeStructPointer, fastCopy, generateProvisionId, generateVatId, getGlobalMemoryPool, isDebugEnabled, isPipelineClient, isSameBuffer, isStream, isSturdyRefValid, parseSchemaNodes, serializeDynamic, serializeDynamicByTypeId, serializeGetSchemaParams, serializeGetSchemaResults, serializeListSchemasResults, serializeRpcMessage, serializeSchemaRequest, serializeSchemaResponse, serializeSturdyRef, supportsStreaming };
10926
+ export { AnswerTable, BaseCapabilityClient, BulkTransfer, BulkTransferManager, CapnpWebSocketProxy, ConnectionManager, DEFAULT_BULK_CONFIG, DEFAULT_COMPRESSION_CONFIG, DEFAULT_ESCROW_CONFIG, DEFAULT_FLOW_CONTROL, DEFAULT_JOIN_OPTIONS, DEFAULT_JOIN_SECURITY_POLICY, DEFAULT_REALTIME_CONFIG, DEFAULT_STREAMING_CAPABILITIES, DropPolicy, ElementSize, ExportTable, EzRpcTransport, FrameFlags, ImportTable, Level3Handlers, Level4Handlers, ListBuilder, ListReader, MemoryPool, MessageBuilder, MessageReader, MultiSegmentMessageBuilder, OptimizedRpcMessageBuilder, PIPELINE_CLIENT_SYMBOL, PipelineOpTracker, PipelineResolutionTracker, PointerTag, QuestionTable, QueuedCallManager, RealtimeStream, RealtimeStreamManager, RestoreHandler, RpcConnection, SCHEMA_MESSAGE_TYPES, SchemaCapabilityClient, SchemaCapabilityServer, SchemaFormat, Segment, Stream, StreamManager, StreamPriority, StreamType, StreamingRpcConnection, StructBuilder, StructReader, SturdyRefManager, TcpTransport, UnionBuilder, UnionReader, WORD_SIZE, WebSocketTransport, compress, configureGlobalMemoryPool, createBulkTransferManager, createCompressionConfig, createCompressionStats, createDynamicReader, createDynamicReaderByTypeId, createDynamicReaderFromStruct, createDynamicWriter, createDynamicWriterByTypeId, createNestedDynamicWriter, createPipelineClient, createProvisionId, createRealtimeStreamManager, createRecipientId, createSchemaRegistry, createStream, createStreamManager, createStreamingConnection, createSturdyRef, createThirdPartyCapId, createUnionBuilder, createUnionReader, createZeroCopyView, decodePointer, deserializeRpcMessage, deserializeSchemaRequest, deserializeSchemaResponse, deserializeSturdyRef, disableDebug, dumpDynamicReader, enableDebug, encodeFarPointer, encodeListPointer, encodeStructPointer, fastCopy, generateProvisionId, generateVatId, getGlobalMemoryPool, isCompressionFrame, isDebugEnabled, isPipelineClient, isSameBuffer, isStream, isSturdyRefValid, parseFrameHeader, parseSchemaNodes, serializeDynamic, serializeDynamicByTypeId, serializeGetSchemaParams, serializeGetSchemaResults, serializeListSchemasResults, serializeRpcMessage, serializeSchemaRequest, serializeSchemaResponse, serializeSturdyRef, supportsStreaming, tryDecompress, uncompress };
10012
10927
  //# sourceMappingURL=index.js.map