@dainprotocol/tunnel 2.0.1 → 2.0.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.
@@ -18,6 +18,11 @@ const RECONNECT = {
18
18
  const LIMITS = {
19
19
  MAX_PENDING_WS_MESSAGES_PER_CONNECTION: 256,
20
20
  };
21
+ function emitClientError(emitter, error) {
22
+ if (emitter.listenerCount("error") > 0) {
23
+ emitter.emit("error", error);
24
+ }
25
+ }
21
26
  function rawDataToString(message) {
22
27
  if (typeof message === "string")
23
28
  return message;
@@ -81,7 +86,7 @@ class DainTunnel extends EventEmitter {
81
86
  this.ws.ping();
82
87
  }
83
88
  catch {
84
- this.emit("error", new Error("Heartbeat ping failed"));
89
+ emitClientError(this, new Error("Heartbeat ping failed"));
85
90
  }
86
91
  }
87
92
  }, TIMEOUTS.HEARTBEAT);
@@ -160,7 +165,7 @@ class DainTunnel extends EventEmitter {
160
165
  finish(undefined, new Error("Connection closed before tunnel established"));
161
166
  }
162
167
  });
163
- this.ws.on("error", (error) => this.emit("error", error));
168
+ this.ws.on("error", (error) => emitClientError(this, error));
164
169
  connectionTimeoutId = setTimeout(() => {
165
170
  if (!resolved && (!this.ws || this.ws.readyState !== WebSocket.OPEN)) {
166
171
  finish(undefined, new Error("Connection timeout"));
@@ -552,8 +557,12 @@ class DainTunnel extends EventEmitter {
552
557
  }
553
558
  if (this.ws) {
554
559
  try {
555
- if (this.ws.readyState === WebSocket.OPEN)
556
- this.ws.close();
560
+ if (this.ws.readyState === WebSocket.OPEN || this.ws.readyState === WebSocket.CONNECTING) {
561
+ this.ws.close(1000, "Client stopping");
562
+ }
563
+ else if (this.ws.readyState !== WebSocket.CLOSED) {
564
+ this.ws.terminate();
565
+ }
557
566
  }
558
567
  catch { }
559
568
  this.ws = null;
@@ -20,6 +20,15 @@ const LIMITS = {
20
20
  WS_BACKLOG: 100,
21
21
  TUNNEL_RETRY_COUNT: 2,
22
22
  };
23
+ const DEBUG_TUNNEL = process.env.DAIN_TUNNEL_DEBUG === "1";
24
+ function debugLog(...args) {
25
+ if (DEBUG_TUNNEL)
26
+ console.log(...args);
27
+ }
28
+ function debugError(...args) {
29
+ if (DEBUG_TUNNEL)
30
+ console.error(...args);
31
+ }
23
32
  let idCounter = 0;
24
33
  const ID_PREFIX = Date.now().toString(36);
25
34
  function fastId() {
@@ -76,7 +85,7 @@ class DainTunnelServer {
76
85
  }
77
86
  }
78
87
  catch (error) {
79
- console.error("[Tunnel] SafeSend error:", error);
88
+ debugError("[Tunnel] SafeSend error:", error);
80
89
  }
81
90
  return false;
82
91
  }
@@ -200,7 +209,7 @@ class DainTunnelServer {
200
209
  }
201
210
  }
202
211
  catch (error) {
203
- console.error("[Tunnel] Message error:", error);
212
+ debugError("[Tunnel] Message error:", error);
204
213
  ws.close(1008, "Invalid message");
205
214
  }
206
215
  }
@@ -296,7 +305,7 @@ class DainTunnelServer {
296
305
  if (!isAlive) {
297
306
  missedPongs++;
298
307
  if (missedPongs >= LIMITS.MAX_MISSED_PONGS) {
299
- console.log(`[Tunnel] ${tunnelId} failed liveness check (${missedPongs} missed pongs), terminating`);
308
+ debugLog(`[Tunnel] ${tunnelId} failed liveness check (${missedPongs} missed pongs), terminating`);
300
309
  clearInterval(intervalId);
301
310
  ws.close(1001, "Liveness check failed");
302
311
  return;
@@ -317,13 +326,13 @@ class DainTunnelServer {
317
326
  // We track isAlive through the pong handler set up in Bun.serve websocket config
318
327
  const tunnelUrl = this.buildTunnelUrl(tunnelId);
319
328
  ws.send(JSON.stringify({ type: "tunnelUrl", url: tunnelUrl }));
320
- console.log(`[Tunnel] Created: ${tunnelUrl}`);
329
+ debugLog(`[Tunnel] Created: ${tunnelUrl}`);
321
330
  // Store isAlive/missedPongs on ws.data for the pong handler
322
331
  ws.data._isAlive = true;
323
332
  ws.data._missedPongsReset = () => { isAlive = true; missedPongs = 0; };
324
333
  }
325
334
  catch (error) {
326
- console.error(`Error in handleStartMessage for tunnel ${tunnelId}:`, error);
335
+ debugError(`Error in handleStartMessage for tunnel ${tunnelId}:`, error);
327
336
  ws.close(1011, "Internal server error");
328
337
  }
329
338
  }
@@ -646,7 +655,7 @@ class DainTunnelServer {
646
655
  this.cleanupChallengesForSocket(ws);
647
656
  }
648
657
  catch (error) {
649
- console.error("[Tunnel] Remove error:", error);
658
+ debugError("[Tunnel] Remove error:", error);
650
659
  }
651
660
  }
652
661
  findAndRemoveTunnel(ws) {
@@ -816,7 +825,7 @@ class DainTunnelServer {
816
825
  backpressureLimit: LIMITS.BACKPRESSURE_THRESHOLD,
817
826
  },
818
827
  });
819
- console.log(`DainTunnel Server is running on ${this.hostname}:${this.port}`);
828
+ debugLog(`DainTunnel Server is running on ${this.hostname}:${this.port}`);
820
829
  }
821
830
  async stop() {
822
831
  try {
@@ -827,7 +836,7 @@ class DainTunnelServer {
827
836
  tunnel.ws.close(1001, "Server shutting down");
828
837
  }
829
838
  catch (error) {
830
- console.error(`Error closing tunnel ${tunnel.id}:`, error);
839
+ debugError(`Error closing tunnel ${tunnel.id}:`, error);
831
840
  }
832
841
  }
833
842
  this.tunnels.clear();
@@ -855,7 +864,7 @@ class DainTunnelServer {
855
864
  conn.controller.close();
856
865
  }
857
866
  catch (error) {
858
- console.error(`Error closing SSE ${sseId}:`, error);
867
+ debugError(`Error closing SSE ${sseId}:`, error);
859
868
  }
860
869
  conn.closed = true;
861
870
  this.sseConnections.delete(sseId);
@@ -865,7 +874,7 @@ class DainTunnelServer {
865
874
  conn.clientSocket.close(1001, "Server shutting down");
866
875
  }
867
876
  catch (error) {
868
- console.error(`Error closing WS ${wsId}:`, error);
877
+ debugError(`Error closing WS ${wsId}:`, error);
869
878
  }
870
879
  this.wsConnections.delete(wsId);
871
880
  }
@@ -875,7 +884,7 @@ class DainTunnelServer {
875
884
  }
876
885
  }
877
886
  catch (error) {
878
- console.error('Error during server shutdown:', error);
887
+ debugError('Error during server shutdown:', error);
879
888
  }
880
889
  }
881
890
  }
package/package.json CHANGED
@@ -1,18 +1,22 @@
1
1
  {
2
2
  "name": "@dainprotocol/tunnel",
3
- "version": "2.0.1",
3
+ "version": "2.0.3",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "private": false,
7
+ "packageManager": "bun@1.3.13",
7
8
  "publishConfig": {
8
9
  "access": "public"
9
10
  },
10
11
  "scripts": {
11
- "build": "tsc",
12
- "build:production": "tsc && bun build dist/server/start.js --outdir dist-prod --target bun --minify --sourcemap=external",
13
- "build:types": "tsc --emitDeclarationOnly",
14
- "test": "bun test",
15
- "prepublishOnly": "npm run build && npm run build:types",
12
+ "build": "bunx --bun tsc",
13
+ "build:production": "bun run build && bun build dist/server/start.js --outdir dist-prod --target bun --minify --sourcemap=external",
14
+ "build:types": "bunx --bun tsc --emitDeclarationOnly",
15
+ "test": "bun test --parallel=4",
16
+ "test:serial": "bun test --isolate",
17
+ "test:watch": "bun test --watch",
18
+ "test:coverage": "bun test --coverage --isolate",
19
+ "prepublishOnly": "bun run build && bun run build:types",
16
20
  "start": "bun dist/server/start.js",
17
21
  "start-server": "bun src/server/start.ts"
18
22
  },
@@ -20,22 +24,23 @@
20
24
  "author": "Ryan",
21
25
  "license": "ISC",
22
26
  "dependencies": {
23
- "@dainprotocol/service-sdk": "2.0.91",
24
- "@types/node": "^22.5.4",
25
- "@types/ws": "^8.5.12",
26
- "hono": "^4.7.0",
27
- "ws": "^8.18.0"
27
+ "@dainprotocol/service-sdk": "^2.0.95",
28
+ "hono": "^4.12.14",
29
+ "ws": "^8.20.0"
28
30
  },
29
31
  "devDependencies": {
30
- "@types/bun": "^1.2.0",
31
- "typescript": "^5.0.0"
32
+ "@types/bun": "^1.3.12",
33
+ "@types/node": "^25.6.0",
34
+ "@types/ws": "^8.18.1",
35
+ "eventsource": "^4.1.0",
36
+ "typescript": "^6.0.3"
32
37
  },
33
38
  "files": [
34
39
  "dist",
35
40
  "README.md"
36
41
  ],
37
42
  "engines": {
38
- "bun": ">=1.1.0"
43
+ "bun": ">=1.3.13"
39
44
  },
40
45
  "exports": {
41
46
  ".": "./dist/index.js",