@dainprotocol/tunnel 2.0.0 → 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.
- package/dist/client/index.js +13 -4
- package/dist/server/index.d.ts +3 -0
- package/dist/server/index.js +118 -28
- package/package.json +19 -13
package/dist/client/index.js
CHANGED
|
@@ -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
|
|
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
|
|
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;
|
package/dist/server/index.d.ts
CHANGED
|
@@ -9,7 +9,9 @@ declare class DainTunnelServer {
|
|
|
9
9
|
private challenges;
|
|
10
10
|
private sseConnections;
|
|
11
11
|
private wsConnections;
|
|
12
|
+
private pendingProxiedWSEvents;
|
|
12
13
|
private tunnelRequestCount;
|
|
14
|
+
private buildCorsHeaders;
|
|
13
15
|
private safeSend;
|
|
14
16
|
private decrementRequestCount;
|
|
15
17
|
private buildForwardedHeaders;
|
|
@@ -26,6 +28,7 @@ declare class DainTunnelServer {
|
|
|
26
28
|
private handleSSEMessage;
|
|
27
29
|
private cleanupSSEConnection;
|
|
28
30
|
private handleWebSocketMessage;
|
|
31
|
+
private flushPendingWebSocketEvents;
|
|
29
32
|
private handleRequest;
|
|
30
33
|
private handleSSERequest;
|
|
31
34
|
private removeTunnel;
|
package/dist/server/index.js
CHANGED
|
@@ -15,10 +15,20 @@ const LIMITS = {
|
|
|
15
15
|
MAX_MISSED_PONGS: 2,
|
|
16
16
|
MAX_PAYLOAD_BYTES: 100 * 1024 * 1024,
|
|
17
17
|
BACKPRESSURE_THRESHOLD: 1024 * 1024,
|
|
18
|
+
MAX_PENDING_WS_EVENTS_PER_CONNECTION: 256,
|
|
18
19
|
SERVER_MAX_HEADERS: 100,
|
|
19
20
|
WS_BACKLOG: 100,
|
|
20
21
|
TUNNEL_RETRY_COUNT: 2,
|
|
21
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
|
+
}
|
|
22
32
|
let idCounter = 0;
|
|
23
33
|
const ID_PREFIX = Date.now().toString(36);
|
|
24
34
|
function fastId() {
|
|
@@ -52,6 +62,21 @@ function isCorsOriginAllowed(origin, allowedOrigins) {
|
|
|
52
62
|
}
|
|
53
63
|
const ALLOWED_CORS_ORIGINS = parseAllowedCorsOrigins(process.env.TUNNEL_ALLOWED_ORIGINS || process.env.CORS_ALLOWED_ORIGINS);
|
|
54
64
|
class DainTunnelServer {
|
|
65
|
+
buildCorsHeaders(origin) {
|
|
66
|
+
const headers = {
|
|
67
|
+
"access-control-allow-methods": "GET, POST, PUT, DELETE, PATCH, OPTIONS",
|
|
68
|
+
"access-control-allow-headers": "X-DAIN-SIGNATURE, X-DAIN-SMART-ACCOUNT-PDA, X-DAIN-AGENT-ID, X-DAIN-ORG-ID, X-DAIN-ADDRESS, X-DAIN-TIMESTAMP, X-DAIN-WEBHOOK-URL, X-Service, X-Service-Token, Content-Type, Authorization, Accept, Origin, X-Requested-With",
|
|
69
|
+
};
|
|
70
|
+
if (origin && isCorsOriginAllowed(origin, this.allowedCorsOrigins)) {
|
|
71
|
+
headers["access-control-allow-origin"] = origin;
|
|
72
|
+
headers["access-control-allow-credentials"] = "true";
|
|
73
|
+
headers["vary"] = "Origin";
|
|
74
|
+
}
|
|
75
|
+
else if (!origin) {
|
|
76
|
+
headers["access-control-allow-origin"] = "*";
|
|
77
|
+
}
|
|
78
|
+
return headers;
|
|
79
|
+
}
|
|
55
80
|
safeSend(ws, data) {
|
|
56
81
|
try {
|
|
57
82
|
if (ws.readyState === 1) { // WebSocket.OPEN
|
|
@@ -60,7 +85,7 @@ class DainTunnelServer {
|
|
|
60
85
|
}
|
|
61
86
|
}
|
|
62
87
|
catch (error) {
|
|
63
|
-
|
|
88
|
+
debugError("[Tunnel] SafeSend error:", error);
|
|
64
89
|
}
|
|
65
90
|
return false;
|
|
66
91
|
}
|
|
@@ -99,6 +124,7 @@ class DainTunnelServer {
|
|
|
99
124
|
this.challenges = new Map();
|
|
100
125
|
this.sseConnections = new Map();
|
|
101
126
|
this.wsConnections = new Map();
|
|
127
|
+
this.pendingProxiedWSEvents = new Map();
|
|
102
128
|
this.tunnelRequestCount = new Map();
|
|
103
129
|
this.app = new Hono();
|
|
104
130
|
this.setupRoutes();
|
|
@@ -183,7 +209,7 @@ class DainTunnelServer {
|
|
|
183
209
|
}
|
|
184
210
|
}
|
|
185
211
|
catch (error) {
|
|
186
|
-
|
|
212
|
+
debugError("[Tunnel] Message error:", error);
|
|
187
213
|
ws.close(1008, "Invalid message");
|
|
188
214
|
}
|
|
189
215
|
}
|
|
@@ -279,7 +305,7 @@ class DainTunnelServer {
|
|
|
279
305
|
if (!isAlive) {
|
|
280
306
|
missedPongs++;
|
|
281
307
|
if (missedPongs >= LIMITS.MAX_MISSED_PONGS) {
|
|
282
|
-
|
|
308
|
+
debugLog(`[Tunnel] ${tunnelId} failed liveness check (${missedPongs} missed pongs), terminating`);
|
|
283
309
|
clearInterval(intervalId);
|
|
284
310
|
ws.close(1001, "Liveness check failed");
|
|
285
311
|
return;
|
|
@@ -300,13 +326,13 @@ class DainTunnelServer {
|
|
|
300
326
|
// We track isAlive through the pong handler set up in Bun.serve websocket config
|
|
301
327
|
const tunnelUrl = this.buildTunnelUrl(tunnelId);
|
|
302
328
|
ws.send(JSON.stringify({ type: "tunnelUrl", url: tunnelUrl }));
|
|
303
|
-
|
|
329
|
+
debugLog(`[Tunnel] Created: ${tunnelUrl}`);
|
|
304
330
|
// Store isAlive/missedPongs on ws.data for the pong handler
|
|
305
331
|
ws.data._isAlive = true;
|
|
306
332
|
ws.data._missedPongsReset = () => { isAlive = true; missedPongs = 0; };
|
|
307
333
|
}
|
|
308
334
|
catch (error) {
|
|
309
|
-
|
|
335
|
+
debugError(`Error in handleStartMessage for tunnel ${tunnelId}:`, error);
|
|
310
336
|
ws.close(1011, "Internal server error");
|
|
311
337
|
}
|
|
312
338
|
}
|
|
@@ -314,7 +340,7 @@ class DainTunnelServer {
|
|
|
314
340
|
const pendingRequest = this.pendingRequests.get(data.requestId);
|
|
315
341
|
if (!pendingRequest)
|
|
316
342
|
return;
|
|
317
|
-
const { resolve, tunnelId, timeoutId } = pendingRequest;
|
|
343
|
+
const { resolve, tunnelId, timeoutId, origin } = pendingRequest;
|
|
318
344
|
this.pendingRequests.delete(data.requestId);
|
|
319
345
|
if (timeoutId)
|
|
320
346
|
clearTimeout(timeoutId);
|
|
@@ -322,6 +348,8 @@ class DainTunnelServer {
|
|
|
322
348
|
const headers = data.headers;
|
|
323
349
|
delete headers["transfer-encoding"];
|
|
324
350
|
delete headers["content-length"];
|
|
351
|
+
// Inject CORS headers — Hono middleware doesn't apply to Promise-resolved responses
|
|
352
|
+
Object.assign(headers, this.buildCorsHeaders(origin));
|
|
325
353
|
const bodyBuffer = Buffer.from(data.body, "base64");
|
|
326
354
|
headers["content-length"] = bodyBuffer.length.toString();
|
|
327
355
|
resolve(new Response(bodyBuffer, {
|
|
@@ -373,8 +401,28 @@ class DainTunnelServer {
|
|
|
373
401
|
}
|
|
374
402
|
handleWebSocketMessage(data) {
|
|
375
403
|
const connection = this.wsConnections.get(data.id);
|
|
376
|
-
if (!connection)
|
|
404
|
+
if (!connection) {
|
|
405
|
+
const pendingEvents = this.pendingProxiedWSEvents.get(data.id);
|
|
406
|
+
if (!pendingEvents)
|
|
407
|
+
return;
|
|
408
|
+
if (data.event === "message" &&
|
|
409
|
+
pendingEvents.length >= LIMITS.MAX_PENDING_WS_EVENTS_PER_CONNECTION) {
|
|
410
|
+
pendingEvents.push({
|
|
411
|
+
type: "websocket",
|
|
412
|
+
id: data.id,
|
|
413
|
+
event: "error",
|
|
414
|
+
data: "WebSocket downstream overloaded",
|
|
415
|
+
});
|
|
416
|
+
pendingEvents.push({
|
|
417
|
+
type: "websocket",
|
|
418
|
+
id: data.id,
|
|
419
|
+
event: "close",
|
|
420
|
+
});
|
|
421
|
+
return;
|
|
422
|
+
}
|
|
423
|
+
pendingEvents.push(data);
|
|
377
424
|
return;
|
|
425
|
+
}
|
|
378
426
|
const { clientSocket } = connection;
|
|
379
427
|
const isOpen = clientSocket.readyState === 1;
|
|
380
428
|
switch (data.event) {
|
|
@@ -394,6 +442,17 @@ class DainTunnelServer {
|
|
|
394
442
|
break;
|
|
395
443
|
}
|
|
396
444
|
}
|
|
445
|
+
flushPendingWebSocketEvents(id) {
|
|
446
|
+
const pendingEvents = this.pendingProxiedWSEvents.get(id);
|
|
447
|
+
if (!pendingEvents || pendingEvents.length === 0) {
|
|
448
|
+
this.pendingProxiedWSEvents.delete(id);
|
|
449
|
+
return;
|
|
450
|
+
}
|
|
451
|
+
this.pendingProxiedWSEvents.delete(id);
|
|
452
|
+
for (const event of pendingEvents) {
|
|
453
|
+
this.handleWebSocketMessage(event);
|
|
454
|
+
}
|
|
455
|
+
}
|
|
397
456
|
async handleRequest(c) {
|
|
398
457
|
const tunnelId = c.req.param("tunnelId");
|
|
399
458
|
if (!tunnelId || !tunnelId.includes("_")) {
|
|
@@ -453,6 +512,8 @@ class DainTunnelServer {
|
|
|
453
512
|
}
|
|
454
513
|
catch { }
|
|
455
514
|
}
|
|
515
|
+
const origin = c.req.header("origin");
|
|
516
|
+
const corsHeaders = this.buildCorsHeaders(origin);
|
|
456
517
|
return new Promise((resolve) => {
|
|
457
518
|
const timeoutId = setTimeout(() => {
|
|
458
519
|
const pendingRequest = this.pendingRequests.get(requestId);
|
|
@@ -461,11 +522,11 @@ class DainTunnelServer {
|
|
|
461
522
|
this.pendingRequests.delete(requestId);
|
|
462
523
|
resolve(new Response(JSON.stringify({ error: "Gateway Timeout", message: "Request timed out" }), {
|
|
463
524
|
status: 504,
|
|
464
|
-
headers: { "content-type": "application/json" },
|
|
525
|
+
headers: { "content-type": "application/json", ...corsHeaders },
|
|
465
526
|
}));
|
|
466
527
|
}
|
|
467
528
|
}, TIMEOUTS.REQUEST_TIMEOUT);
|
|
468
|
-
this.pendingRequests.set(requestId, { resolve, startTime: Date.now(), tunnelId, timeoutId });
|
|
529
|
+
this.pendingRequests.set(requestId, { resolve, startTime: Date.now(), tunnelId, timeoutId, origin });
|
|
469
530
|
const sent = this.safeSend(tunnel.ws, {
|
|
470
531
|
type: "request",
|
|
471
532
|
id: requestId,
|
|
@@ -480,19 +541,35 @@ class DainTunnelServer {
|
|
|
480
541
|
this.decrementRequestCount(tunnelId);
|
|
481
542
|
resolve(new Response(JSON.stringify({ error: "Bad Gateway", message: "Tunnel connection lost" }), {
|
|
482
543
|
status: 502,
|
|
483
|
-
headers: { "content-type": "application/json" },
|
|
544
|
+
headers: { "content-type": "application/json", ...corsHeaders },
|
|
484
545
|
}));
|
|
485
546
|
}
|
|
486
547
|
});
|
|
487
548
|
}
|
|
488
|
-
handleSSERequest(c, tunnelId, tunnel) {
|
|
549
|
+
async handleSSERequest(c, tunnelId, tunnel) {
|
|
489
550
|
const sseId = fastId();
|
|
551
|
+
const requestUrl = new URL(c.req.url);
|
|
552
|
+
let path = requestUrl.pathname.slice(`/${tunnelId}`.length) || "/";
|
|
553
|
+
if (requestUrl.search)
|
|
554
|
+
path += requestUrl.search;
|
|
490
555
|
// Extract headers
|
|
491
556
|
const reqHeaders = {};
|
|
492
557
|
c.req.raw.headers.forEach((value, key) => {
|
|
493
558
|
reqHeaders[key] = value;
|
|
494
559
|
});
|
|
495
560
|
const forwardedHeaders = this.buildForwardedHeaders(reqHeaders, tunnelId);
|
|
561
|
+
let bodyBase64;
|
|
562
|
+
if (c.req.method !== "GET" && c.req.method !== "HEAD") {
|
|
563
|
+
try {
|
|
564
|
+
const bodyBuffer = await c.req.arrayBuffer();
|
|
565
|
+
if (bodyBuffer.byteLength > 0) {
|
|
566
|
+
bodyBase64 = Buffer.from(bodyBuffer).toString("base64");
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
catch {
|
|
570
|
+
// Best-effort body forwarding for streaming requests
|
|
571
|
+
}
|
|
572
|
+
}
|
|
496
573
|
const origin = c.req.header("origin");
|
|
497
574
|
const responseHeaders = {
|
|
498
575
|
'Content-Type': 'text/event-stream',
|
|
@@ -540,9 +617,10 @@ class DainTunnelServer {
|
|
|
540
617
|
const sent = this.safeSend(tunnel.ws, {
|
|
541
618
|
type: "sse_connection",
|
|
542
619
|
id: sseId,
|
|
543
|
-
path
|
|
620
|
+
path,
|
|
544
621
|
method: c.req.method,
|
|
545
622
|
headers: forwardedHeaders,
|
|
623
|
+
body: bodyBase64,
|
|
546
624
|
});
|
|
547
625
|
if (!sent) {
|
|
548
626
|
this.cleanupSSEConnection(sseId, tunnelId);
|
|
@@ -577,7 +655,7 @@ class DainTunnelServer {
|
|
|
577
655
|
this.cleanupChallengesForSocket(ws);
|
|
578
656
|
}
|
|
579
657
|
catch (error) {
|
|
580
|
-
|
|
658
|
+
debugError("[Tunnel] Remove error:", error);
|
|
581
659
|
}
|
|
582
660
|
}
|
|
583
661
|
findAndRemoveTunnel(ws) {
|
|
@@ -604,7 +682,10 @@ class DainTunnelServer {
|
|
|
604
682
|
clearTimeout(pending.timeoutId);
|
|
605
683
|
pending.resolve(new Response(JSON.stringify({ error: "Bad Gateway", message: "Tunnel closed" }), {
|
|
606
684
|
status: 502,
|
|
607
|
-
headers: {
|
|
685
|
+
headers: {
|
|
686
|
+
"content-type": "application/json",
|
|
687
|
+
...this.buildCorsHeaders(pending.origin),
|
|
688
|
+
},
|
|
608
689
|
}));
|
|
609
690
|
this.pendingRequests.delete(requestId);
|
|
610
691
|
}
|
|
@@ -678,6 +759,13 @@ class DainTunnelServer {
|
|
|
678
759
|
reqHeaders[key] = value;
|
|
679
760
|
});
|
|
680
761
|
const forwardedHeaders = self.buildForwardedHeaders(reqHeaders, tunnelId);
|
|
762
|
+
self._pendingProxiedWs = self._pendingProxiedWs || new Map();
|
|
763
|
+
self._pendingProxiedWs.set(wsConnectionId, {
|
|
764
|
+
tunnelId,
|
|
765
|
+
path: remainingPath,
|
|
766
|
+
headers: forwardedHeaders,
|
|
767
|
+
});
|
|
768
|
+
self.pendingProxiedWSEvents.set(wsConnectionId, []);
|
|
681
769
|
const success = server.upgrade(req, {
|
|
682
770
|
data: {
|
|
683
771
|
isProxiedWebSocket: true,
|
|
@@ -686,13 +774,6 @@ class DainTunnelServer {
|
|
|
686
774
|
},
|
|
687
775
|
});
|
|
688
776
|
if (success) {
|
|
689
|
-
// Store setup info so open handler can register the connection
|
|
690
|
-
self._pendingProxiedWs = self._pendingProxiedWs || new Map();
|
|
691
|
-
self._pendingProxiedWs.set(wsConnectionId, {
|
|
692
|
-
tunnelId,
|
|
693
|
-
path: remainingPath,
|
|
694
|
-
headers: forwardedHeaders,
|
|
695
|
-
});
|
|
696
777
|
self.safeSend(tunnel.ws, {
|
|
697
778
|
type: "websocket_connection",
|
|
698
779
|
id: wsConnectionId,
|
|
@@ -701,6 +782,8 @@ class DainTunnelServer {
|
|
|
701
782
|
});
|
|
702
783
|
return undefined;
|
|
703
784
|
}
|
|
785
|
+
self._pendingProxiedWs?.delete(wsConnectionId);
|
|
786
|
+
self.pendingProxiedWSEvents.delete(wsConnectionId);
|
|
704
787
|
return new Response("WebSocket upgrade failed", { status: 400 });
|
|
705
788
|
}
|
|
706
789
|
// Regular HTTP request - pass to Hono
|
|
@@ -720,6 +803,7 @@ class DainTunnelServer {
|
|
|
720
803
|
tunnelId: pending.tunnelId,
|
|
721
804
|
});
|
|
722
805
|
pendingMap.delete(ws.data.proxiedWsId);
|
|
806
|
+
self.flushPendingWebSocketEvents(ws.data.proxiedWsId);
|
|
723
807
|
}
|
|
724
808
|
}
|
|
725
809
|
self.handleWsOpen(ws);
|
|
@@ -741,7 +825,7 @@ class DainTunnelServer {
|
|
|
741
825
|
backpressureLimit: LIMITS.BACKPRESSURE_THRESHOLD,
|
|
742
826
|
},
|
|
743
827
|
});
|
|
744
|
-
|
|
828
|
+
debugLog(`DainTunnel Server is running on ${this.hostname}:${this.port}`);
|
|
745
829
|
}
|
|
746
830
|
async stop() {
|
|
747
831
|
try {
|
|
@@ -752,7 +836,7 @@ class DainTunnelServer {
|
|
|
752
836
|
tunnel.ws.close(1001, "Server shutting down");
|
|
753
837
|
}
|
|
754
838
|
catch (error) {
|
|
755
|
-
|
|
839
|
+
debugError(`Error closing tunnel ${tunnel.id}:`, error);
|
|
756
840
|
}
|
|
757
841
|
}
|
|
758
842
|
this.tunnels.clear();
|
|
@@ -760,9 +844,15 @@ class DainTunnelServer {
|
|
|
760
844
|
for (const [requestId, pending] of this.pendingRequests.entries()) {
|
|
761
845
|
if (pending.timeoutId)
|
|
762
846
|
clearTimeout(pending.timeoutId);
|
|
763
|
-
pending.resolve(new Response(JSON.stringify({
|
|
847
|
+
pending.resolve(new Response(JSON.stringify({
|
|
848
|
+
error: "Service Unavailable",
|
|
849
|
+
message: "Server shutting down",
|
|
850
|
+
}), {
|
|
764
851
|
status: 503,
|
|
765
|
-
headers: {
|
|
852
|
+
headers: {
|
|
853
|
+
"content-type": "application/json",
|
|
854
|
+
...this.buildCorsHeaders(pending.origin),
|
|
855
|
+
},
|
|
766
856
|
}));
|
|
767
857
|
this.pendingRequests.delete(requestId);
|
|
768
858
|
}
|
|
@@ -774,7 +864,7 @@ class DainTunnelServer {
|
|
|
774
864
|
conn.controller.close();
|
|
775
865
|
}
|
|
776
866
|
catch (error) {
|
|
777
|
-
|
|
867
|
+
debugError(`Error closing SSE ${sseId}:`, error);
|
|
778
868
|
}
|
|
779
869
|
conn.closed = true;
|
|
780
870
|
this.sseConnections.delete(sseId);
|
|
@@ -784,7 +874,7 @@ class DainTunnelServer {
|
|
|
784
874
|
conn.clientSocket.close(1001, "Server shutting down");
|
|
785
875
|
}
|
|
786
876
|
catch (error) {
|
|
787
|
-
|
|
877
|
+
debugError(`Error closing WS ${wsId}:`, error);
|
|
788
878
|
}
|
|
789
879
|
this.wsConnections.delete(wsId);
|
|
790
880
|
}
|
|
@@ -794,7 +884,7 @@ class DainTunnelServer {
|
|
|
794
884
|
}
|
|
795
885
|
}
|
|
796
886
|
catch (error) {
|
|
797
|
-
|
|
887
|
+
debugError('Error during server shutdown:', error);
|
|
798
888
|
}
|
|
799
889
|
}
|
|
800
890
|
}
|
package/package.json
CHANGED
|
@@ -1,17 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dainprotocol/tunnel",
|
|
3
|
-
"version": "2.0.
|
|
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:
|
|
13
|
-
"
|
|
14
|
-
"
|
|
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",
|
|
15
20
|
"start": "bun dist/server/start.js",
|
|
16
21
|
"start-server": "bun src/server/start.ts"
|
|
17
22
|
},
|
|
@@ -19,22 +24,23 @@
|
|
|
19
24
|
"author": "Ryan",
|
|
20
25
|
"license": "ISC",
|
|
21
26
|
"dependencies": {
|
|
22
|
-
"@dainprotocol/service-sdk": "2.0.
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
"hono": "^4.7.0",
|
|
26
|
-
"ws": "^8.18.0"
|
|
27
|
+
"@dainprotocol/service-sdk": "^2.0.95",
|
|
28
|
+
"hono": "^4.12.14",
|
|
29
|
+
"ws": "^8.20.0"
|
|
27
30
|
},
|
|
28
31
|
"devDependencies": {
|
|
29
|
-
"@types/bun": "^1.
|
|
30
|
-
"
|
|
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"
|
|
31
37
|
},
|
|
32
38
|
"files": [
|
|
33
39
|
"dist",
|
|
34
40
|
"README.md"
|
|
35
41
|
],
|
|
36
42
|
"engines": {
|
|
37
|
-
"bun": ">=1.
|
|
43
|
+
"bun": ">=1.3.13"
|
|
38
44
|
},
|
|
39
45
|
"exports": {
|
|
40
46
|
".": "./dist/index.js",
|