@naeemo/capnp 0.9.1 → 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
@@ -1690,6 +1697,18 @@ var WebSocketTransport = class WebSocketTransport {
1690
1697
  get connected() {
1691
1698
  return this._connected;
1692
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
+ }
1693
1712
  connect(url) {
1694
1713
  this.ws = new WebSocket(url);
1695
1714
  this.ws.binaryType = this.options.binaryType ?? "arraybuffer";
@@ -1846,6 +1865,432 @@ var WebSocketTransport = class WebSocketTransport {
1846
1865
  }
1847
1866
  };
1848
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
+
1849
2294
  //#endregion
1850
2295
  //#region src/rpc/tcp-transport.ts
1851
2296
  /**
@@ -1854,11 +2299,20 @@ var WebSocketTransport = class WebSocketTransport {
1854
2299
  * Implements RpcTransport over raw TCP socket for C++ interop testing.
1855
2300
  * This allows direct communication with the official Cap'n Proto C++ implementation.
1856
2301
  */
2302
+ const FRAME_MAGIC = 1280980035;
2303
+ const ALGORITHM_LZ4 = 1;
2304
+ const CAPABILITY_VERSION = 1;
2305
+ const DEFAULT_COMPRESSION_THRESHOLD = 256;
1857
2306
  /**
1858
2307
  * TCP Transport for Cap'n Proto RPC
1859
2308
  *
1860
2309
  * Uses length-prefixed binary message framing compatible with Cap'n Proto C++ implementation.
1861
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
1862
2316
  */
1863
2317
  var TcpTransport = class TcpTransport {
1864
2318
  socket = null;
@@ -1868,12 +2322,35 @@ var TcpTransport = class TcpTransport {
1868
2322
  pendingBuffer = Buffer.alloc(0);
1869
2323
  pendingLength = 0;
1870
2324
  hasPendingLength = false;
2325
+ compressionConfig;
2326
+ localSupportsLz4 = false;
2327
+ remoteSupportsLz4 = false;
2328
+ compressionEnabled = false;
2329
+ compressionState;
2330
+ negotiationComplete = false;
1871
2331
  onClose;
1872
2332
  onError;
1873
2333
  constructor(host, port, options = {}) {
1874
2334
  this.host = host;
1875
2335
  this.port = port;
1876
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
+ };
1877
2354
  }
1878
2355
  static async connect(host, port, options) {
1879
2356
  const transport = new TcpTransport(host, port, options);
@@ -1888,6 +2365,9 @@ var TcpTransport = class TcpTransport {
1888
2365
  get connected() {
1889
2366
  return this._connected && this.socket?.readyState === "open";
1890
2367
  }
2368
+ getCompressionState() {
2369
+ return { ...this.compressionState };
2370
+ }
1891
2371
  connect() {
1892
2372
  return new Promise((resolve, reject) => {
1893
2373
  const timeout = setTimeout(() => {
@@ -1895,10 +2375,15 @@ var TcpTransport = class TcpTransport {
1895
2375
  reject(/* @__PURE__ */ new Error("Connection timeout"));
1896
2376
  }, this.options.connectTimeoutMs ?? 1e4);
1897
2377
  this.socket = new net$1.Socket();
1898
- this.socket.on("connect", () => {
2378
+ this.socket.on("connect", async () => {
1899
2379
  clearTimeout(timeout);
1900
2380
  this._connected = true;
1901
- resolve();
2381
+ try {
2382
+ await this.performCapabilityNegotiation();
2383
+ resolve();
2384
+ } catch (err) {
2385
+ reject(err instanceof Error ? err : new Error(String(err)));
2386
+ }
1902
2387
  });
1903
2388
  this.socket.on("data", (data) => {
1904
2389
  this.handleData(data);
@@ -1940,6 +2425,85 @@ var TcpTransport = class TcpTransport {
1940
2425
  socket.on("end", () => {
1941
2426
  this._connected = false;
1942
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
+ });
1943
2507
  }
1944
2508
  handleData(data) {
1945
2509
  this.pendingBuffer = Buffer.concat([this.pendingBuffer, data]);
@@ -1955,14 +2519,50 @@ var TcpTransport = class TcpTransport {
1955
2519
  }
1956
2520
  const totalLength = 4 + this.pendingLength;
1957
2521
  if (this.pendingBuffer.length < totalLength) break;
1958
- const messageData = this.pendingBuffer.subarray(4, totalLength);
1959
- this.pendingBuffer = this.pendingBuffer.subarray(totalLength);
1960
- this.hasPendingLength = false;
1961
- try {
1962
- const message = deserializeRpcMessage(new Uint8Array(messageData));
1963
- this.handleRpcMessage(message);
1964
- } catch (err) {
1965
- 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
+ }
1966
2566
  }
1967
2567
  }
1968
2568
  }
@@ -1974,10 +2574,31 @@ var TcpTransport = class TcpTransport {
1974
2574
  }
1975
2575
  async send(message) {
1976
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");
1977
2578
  const data = serializeRpcMessage(message);
1978
- const frame = Buffer.allocUnsafe(4 + data.length);
1979
- frame.writeUInt32LE(data.length, 0);
1980
- 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
+ }
1981
2602
  return new Promise((resolve, reject) => {
1982
2603
  this.socket.write(frame, (err) => {
1983
2604
  if (err) reject(err);
@@ -2045,6 +2666,18 @@ var EzRpcTransport = class EzRpcTransport {
2045
2666
  get connected() {
2046
2667
  return this._connected && this.socket !== null && !this.socket.destroyed;
2047
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
+ }
2048
2681
  doConnect() {
2049
2682
  return new Promise((resolve, reject) => {
2050
2683
  const timeout = setTimeout(() => {
@@ -9819,6 +10452,131 @@ var import_sender = /* @__PURE__ */ __toESM(require_sender(), 1);
9819
10452
  var import_websocket = /* @__PURE__ */ __toESM(require_websocket(), 1);
9820
10453
  var import_websocket_server = /* @__PURE__ */ __toESM(require_websocket_server(), 1);
9821
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
+
9822
10580
  //#endregion
9823
10581
  //#region src/proxy/websocket-proxy.ts
9824
10582
  /**
@@ -9827,6 +10585,27 @@ var import_websocket_server = /* @__PURE__ */ __toESM(require_websocket_server()
9827
10585
  * Allows browsers to connect to native Cap'n Proto services (C++, etc.)
9828
10586
  * via WebSocket. Handles the protocol bridging between WebSocket (browser)
9829
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
9830
10609
  */
9831
10610
  var ProxyConnection = class extends EventEmitter {
9832
10611
  ws;
@@ -9834,16 +10613,22 @@ var ProxyConnection = class extends EventEmitter {
9834
10613
  stats;
9835
10614
  options;
9836
10615
  closed = false;
10616
+ wsCompressionConfig;
10617
+ tcpCompressionConfig;
9837
10618
  constructor(ws, options) {
9838
10619
  super();
9839
10620
  this.ws = ws;
9840
10621
  this.options = options;
10622
+ this.wsCompressionConfig = createCompressionConfig(options.compression?.ws);
10623
+ this.tcpCompressionConfig = createCompressionConfig(options.compression?.tcp);
9841
10624
  this.stats = {
9842
10625
  wsMessagesIn: 0,
9843
10626
  wsMessagesOut: 0,
9844
10627
  tcpBytesIn: 0,
9845
10628
  tcpBytesOut: 0,
9846
- connectedAt: /* @__PURE__ */ new Date()
10629
+ connectedAt: /* @__PURE__ */ new Date(),
10630
+ wsCompression: createCompressionStats(),
10631
+ tcpCompression: createCompressionStats()
9847
10632
  };
9848
10633
  this.setupWebSocket();
9849
10634
  }
@@ -9871,6 +10656,7 @@ var ProxyConnection = class extends EventEmitter {
9871
10656
  });
9872
10657
  this.tcpSocket.on("connect", () => {
9873
10658
  this.log("Connected to target TCP service");
10659
+ this.log(`Compression - WS: ${this.wsCompressionConfig.enabled ? "enabled" : "disabled"}, TCP: ${this.tcpCompressionConfig.enabled ? "enabled" : "disabled"}`);
9874
10660
  this.emit("connected");
9875
10661
  });
9876
10662
  this.tcpSocket.on("data", (data) => {
@@ -9890,29 +10676,78 @@ var ProxyConnection = class extends EventEmitter {
9890
10676
  this.close();
9891
10677
  });
9892
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
+ */
9893
10685
  handleWebSocketMessage(data) {
9894
10686
  if (this.closed) return;
9895
10687
  const buffer = Buffer.isBuffer(data) ? data : Array.isArray(data) ? Buffer.concat(data) : Buffer.from(data);
9896
10688
  const maxSize = this.options.maxMessageSize ?? 16 * 1024 * 1024;
9897
- if (buffer.length > maxSize) {
9898
- 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})`);
9899
10694
  this.close();
9900
10695
  return;
9901
10696
  }
9902
10697
  this.stats.wsMessagesIn++;
9903
- 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;
9904
10715
  if (this.tcpSocket?.writable) {
9905
- this.tcpSocket.write(buffer);
9906
- 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})`);
9907
10719
  }
9908
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
+ */
9909
10727
  handleTcpData(data) {
9910
10728
  if (this.closed) return;
9911
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
+ }
9912
10747
  this.stats.wsMessagesOut++;
9913
10748
  if (this.ws.readyState === import_websocket.default.OPEN) {
9914
- this.ws.send(data);
9915
- 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})`);
9916
10751
  }
9917
10752
  }
9918
10753
  log(...args) {
@@ -9929,6 +10764,10 @@ var ProxyConnection = class extends EventEmitter {
9929
10764
  this.emit("closed");
9930
10765
  }
9931
10766
  };
10767
+ /**
10768
+ * WebSocket-to-TCP Proxy server
10769
+ * Manages multiple proxy connections
10770
+ */
9932
10771
  var CapnpWebSocketProxy = class extends EventEmitter {
9933
10772
  wss;
9934
10773
  connections = /* @__PURE__ */ new Map();
@@ -9986,6 +10825,9 @@ if (import.meta.url === `file://${process.argv[1]}`) {
9986
10825
  let targetHost = "localhost";
9987
10826
  let targetPort = 8081;
9988
10827
  let debug = false;
10828
+ let wsCompression = false;
10829
+ let tcpCompression = false;
10830
+ let compressionThreshold = 1024;
9989
10831
  for (let i = 0; i < args.length; i++) switch (args[i]) {
9990
10832
  case "--ws-port":
9991
10833
  case "-p":
@@ -10002,6 +10844,17 @@ if (import.meta.url === `file://${process.argv[1]}`) {
10002
10844
  case "-d":
10003
10845
  debug = true;
10004
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;
10005
10858
  case "--help":
10006
10859
  case "-h":
10007
10860
  console.log(`
@@ -10010,13 +10863,25 @@ Cap'n Proto WebSocket-to-TCP Proxy
10010
10863
  Usage: npx @naeemo/capnp proxy [options]
10011
10864
 
10012
10865
  Options:
10013
- -p, --ws-port <port> WebSocket server port (default: 8080)
10014
- -t, --target <host:port> Target TCP service (default: localhost:8081)
10015
- -d, --debug Enable debug logging
10016
- -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
10017
10873
 
10018
- Example:
10874
+ Examples:
10019
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
10020
10885
  `);
10021
10886
  process.exit(0);
10022
10887
  }
@@ -10024,11 +10889,26 @@ Example:
10024
10889
  wsPort,
10025
10890
  targetHost,
10026
10891
  targetPort,
10027
- debug
10892
+ debug,
10893
+ compression: {
10894
+ ws: {
10895
+ enabled: wsCompression,
10896
+ threshold: compressionThreshold
10897
+ },
10898
+ tcp: {
10899
+ enabled: tcpCompression,
10900
+ threshold: compressionThreshold
10901
+ }
10902
+ }
10028
10903
  });
10029
10904
  console.log("WebSocket-to-TCP Proxy started");
10030
10905
  console.log(` WebSocket: ws://localhost:${wsPort}`);
10031
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
+ }
10032
10912
  proxy.on("connection", () => {
10033
10913
  console.log(`Active connections: ${proxy.getConnectionCount()}`);
10034
10914
  });
@@ -10043,5 +10923,5 @@ Example:
10043
10923
  }
10044
10924
 
10045
10925
  //#endregion
10046
- 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 };
10047
10927
  //# sourceMappingURL=index.js.map