@limrun/api 0.13.1 → 0.13.2
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/CHANGELOG.md +4 -0
- package/index.d.mts +1 -1
- package/index.d.mts.map +1 -1
- package/index.d.ts +1 -1
- package/index.d.ts.map +1 -1
- package/index.js +3 -3
- package/index.js.map +1 -1
- package/index.mjs +1 -1
- package/index.mjs.map +1 -1
- package/instance-client.d.mts +32 -0
- package/instance-client.d.mts.map +1 -1
- package/instance-client.d.ts +32 -0
- package/instance-client.d.ts.map +1 -1
- package/instance-client.js +193 -87
- package/instance-client.js.map +1 -1
- package/instance-client.mjs +193 -87
- package/instance-client.mjs.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +1 -1
- package/src/instance-client.ts +269 -101
- package/src/tunnel.ts +262 -43
- package/src/version.ts +1 -1
- package/tunnel.d.mts +50 -7
- package/tunnel.d.mts.map +1 -1
- package/tunnel.d.ts +50 -7
- package/tunnel.d.ts.map +1 -1
- package/tunnel.js +195 -44
- package/tunnel.js.map +1 -1
- package/tunnel.mjs +195 -44
- package/tunnel.mjs.map +1 -1
- package/version.d.mts +1 -1
- package/version.d.ts +1 -1
- package/version.js +1 -1
- package/version.mjs +1 -1
package/src/tunnel.ts
CHANGED
|
@@ -1,93 +1,237 @@
|
|
|
1
1
|
import * as net from 'net';
|
|
2
2
|
import { WebSocket } from 'ws';
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* Controls the verbosity of logging in the tunnel
|
|
6
|
+
*/
|
|
7
|
+
export type LogLevel = 'none' | 'error' | 'warn' | 'info' | 'debug';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Connection state of the tunnel
|
|
11
|
+
*/
|
|
12
|
+
export type TunnelConnectionState = 'connecting' | 'connected' | 'disconnected' | 'reconnecting';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Callback function for tunnel connection state changes
|
|
16
|
+
*/
|
|
17
|
+
export type TunnelConnectionStateCallback = (state: TunnelConnectionState) => void;
|
|
18
|
+
|
|
4
19
|
export interface Tunnel {
|
|
5
20
|
address: {
|
|
6
21
|
address: string;
|
|
7
22
|
port: number;
|
|
8
23
|
};
|
|
9
24
|
close: () => void;
|
|
25
|
+
/**
|
|
26
|
+
* Get current WebSocket connection state
|
|
27
|
+
*/
|
|
28
|
+
getConnectionState: () => TunnelConnectionState;
|
|
29
|
+
/**
|
|
30
|
+
* Register callback for WebSocket connection state changes
|
|
31
|
+
* @returns A function to unregister the callback
|
|
32
|
+
*/
|
|
33
|
+
onConnectionStateChange: (callback: TunnelConnectionStateCallback) => () => void;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface TcpTunnelOptions {
|
|
37
|
+
/**
|
|
38
|
+
* Maximum number of reconnection attempts
|
|
39
|
+
* @default 6
|
|
40
|
+
*/
|
|
41
|
+
maxReconnectAttempts?: number;
|
|
42
|
+
/**
|
|
43
|
+
* Initial reconnection delay in milliseconds
|
|
44
|
+
* @default 1000
|
|
45
|
+
*/
|
|
46
|
+
reconnectDelay?: number;
|
|
47
|
+
/**
|
|
48
|
+
* Maximum reconnection delay in milliseconds
|
|
49
|
+
* @default 30000
|
|
50
|
+
*/
|
|
51
|
+
maxReconnectDelay?: number;
|
|
52
|
+
/**
|
|
53
|
+
* Controls logging verbosity
|
|
54
|
+
* @default 'info'
|
|
55
|
+
*/
|
|
56
|
+
logLevel?: LogLevel;
|
|
10
57
|
}
|
|
11
58
|
|
|
12
59
|
/**
|
|
13
|
-
* Starts a
|
|
60
|
+
* Starts a persistent TCP → WebSocket proxy.
|
|
14
61
|
*
|
|
15
62
|
* The function creates a local TCP server that listens on an ephemeral port on
|
|
16
|
-
* 127.0.0.1.
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
* proxy instance.
|
|
63
|
+
* 127.0.0.1. When a TCP client connects, it forwards all traffic between that
|
|
64
|
+
* client and `remoteURL` through an authenticated WebSocket. The server remains
|
|
65
|
+
* active even after the client disconnects, allowing reconnection without
|
|
66
|
+
* recreating the tunnel.
|
|
21
67
|
*
|
|
22
68
|
* @param remoteURL Remote WebSocket endpoint (e.g. wss://example.com/instance)
|
|
23
69
|
* @param token Bearer token sent as `Authorization` header
|
|
24
70
|
* @param hostname Optional IP address to listen on. Default is 127.0.0.1
|
|
25
71
|
* @param port Optional port number to listen on. Default is to ask Node.js
|
|
26
72
|
* to find an available non-privileged port.
|
|
73
|
+
* @param options Optional reconnection configuration
|
|
27
74
|
*/
|
|
28
75
|
export async function startTcpTunnel(
|
|
29
76
|
remoteURL: string,
|
|
30
77
|
token: string,
|
|
31
78
|
hostname: string,
|
|
32
79
|
port: number,
|
|
80
|
+
options?: TcpTunnelOptions,
|
|
33
81
|
): Promise<Tunnel> {
|
|
82
|
+
const maxReconnectAttempts = options?.maxReconnectAttempts ?? 6;
|
|
83
|
+
const reconnectDelay = options?.reconnectDelay ?? 1000;
|
|
84
|
+
const maxReconnectDelay = options?.maxReconnectDelay ?? 30000;
|
|
85
|
+
const logLevel = options?.logLevel ?? 'info';
|
|
86
|
+
|
|
87
|
+
// Logger functions
|
|
88
|
+
const logger = {
|
|
89
|
+
debug: (...args: any[]) => {
|
|
90
|
+
if (logLevel === 'debug') console.log('[Tunnel]', ...args);
|
|
91
|
+
},
|
|
92
|
+
info: (...args: any[]) => {
|
|
93
|
+
if (logLevel === 'info' || logLevel === 'debug') console.log('[Tunnel]', ...args);
|
|
94
|
+
},
|
|
95
|
+
warn: (...args: any[]) => {
|
|
96
|
+
if (logLevel === 'warn' || logLevel === 'info' || logLevel === 'debug')
|
|
97
|
+
console.warn('[Tunnel]', ...args);
|
|
98
|
+
},
|
|
99
|
+
error: (...args: any[]) => {
|
|
100
|
+
if (logLevel !== 'none') console.error('[Tunnel]', ...args);
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
|
|
34
104
|
return new Promise((resolve, reject) => {
|
|
35
105
|
const server = net.createServer();
|
|
36
106
|
|
|
37
107
|
let ws: WebSocket | undefined;
|
|
38
108
|
let pingInterval: NodeJS.Timeout | undefined;
|
|
109
|
+
let reconnectTimeout: NodeJS.Timeout | undefined;
|
|
110
|
+
let reconnectAttempts = 0;
|
|
111
|
+
let intentionalDisconnect = false;
|
|
112
|
+
let tcpSocket: net.Socket | undefined;
|
|
113
|
+
let connectionState: TunnelConnectionState = 'connecting';
|
|
39
114
|
|
|
40
|
-
|
|
41
|
-
|
|
115
|
+
const stateChangeCallbacks: Set<TunnelConnectionStateCallback> = new Set();
|
|
116
|
+
|
|
117
|
+
const updateConnectionState = (newState: TunnelConnectionState): void => {
|
|
118
|
+
if (connectionState !== newState) {
|
|
119
|
+
connectionState = newState;
|
|
120
|
+
logger.debug(`Connection state changed to: ${newState}`);
|
|
121
|
+
stateChangeCallbacks.forEach((callback) => {
|
|
122
|
+
try {
|
|
123
|
+
callback(newState);
|
|
124
|
+
} catch (err) {
|
|
125
|
+
logger.error('Error in connection state callback:', err);
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
const cleanup = () => {
|
|
132
|
+
if (reconnectTimeout) {
|
|
133
|
+
clearTimeout(reconnectTimeout);
|
|
134
|
+
reconnectTimeout = undefined;
|
|
135
|
+
}
|
|
42
136
|
if (pingInterval) {
|
|
43
137
|
clearInterval(pingInterval);
|
|
44
138
|
pingInterval = undefined;
|
|
45
139
|
}
|
|
46
|
-
if (ws
|
|
47
|
-
ws.
|
|
140
|
+
if (ws) {
|
|
141
|
+
ws.removeAllListeners();
|
|
142
|
+
if (ws.readyState === WebSocket.OPEN || ws.readyState === WebSocket.CONNECTING) {
|
|
143
|
+
ws.close(1000, 'close');
|
|
144
|
+
}
|
|
145
|
+
ws = undefined;
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
const close = () => {
|
|
150
|
+
intentionalDisconnect = true;
|
|
151
|
+
cleanup();
|
|
152
|
+
updateConnectionState('disconnected');
|
|
153
|
+
if (tcpSocket && !tcpSocket.destroyed) {
|
|
154
|
+
tcpSocket.destroy();
|
|
48
155
|
}
|
|
49
156
|
if (server.listening) {
|
|
50
157
|
server.close();
|
|
51
158
|
}
|
|
52
159
|
};
|
|
53
160
|
|
|
54
|
-
|
|
161
|
+
const scheduleReconnect = (): void => {
|
|
162
|
+
if (intentionalDisconnect) {
|
|
163
|
+
logger.debug('Skipping reconnection (intentional disconnect)');
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
55
166
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
});
|
|
167
|
+
if (!tcpSocket || tcpSocket.destroyed) {
|
|
168
|
+
logger.debug('Skipping reconnection (TCP socket closed)');
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
61
171
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
const address = server.address();
|
|
65
|
-
if (!address || typeof address === 'string') {
|
|
172
|
+
if (reconnectAttempts >= maxReconnectAttempts) {
|
|
173
|
+
logger.error(`Max reconnection attempts (${maxReconnectAttempts}) reached. Closing tunnel.`);
|
|
66
174
|
close();
|
|
67
|
-
return
|
|
175
|
+
return;
|
|
68
176
|
}
|
|
69
|
-
resolve({ address, close });
|
|
70
|
-
});
|
|
71
177
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
178
|
+
const currentDelay = Math.min(reconnectDelay * Math.pow(2, reconnectAttempts), maxReconnectDelay);
|
|
179
|
+
|
|
180
|
+
reconnectAttempts++;
|
|
181
|
+
logger.debug(`Scheduling reconnection attempt ${reconnectAttempts} in ${currentDelay}ms...`);
|
|
182
|
+
updateConnectionState('reconnecting');
|
|
183
|
+
|
|
184
|
+
reconnectTimeout = setTimeout(() => {
|
|
185
|
+
logger.debug(`Attempting to reconnect (attempt ${reconnectAttempts})...`);
|
|
186
|
+
setupWebSocket();
|
|
187
|
+
}, currentDelay);
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
const setupWebSocket = (): void => {
|
|
191
|
+
if (!tcpSocket || tcpSocket.destroyed) {
|
|
192
|
+
logger.error('Cannot setup WebSocket: TCP socket is closed');
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
cleanup();
|
|
197
|
+
updateConnectionState('connecting');
|
|
76
198
|
|
|
77
199
|
ws = new WebSocket(remoteURL, {
|
|
78
200
|
headers: { Authorization: `Bearer ${token}` },
|
|
79
201
|
perMessageDeflate: false,
|
|
80
202
|
});
|
|
81
203
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
console.error('WebSocket error:', err);
|
|
85
|
-
tcpSocket.destroy();
|
|
86
|
-
close();
|
|
204
|
+
ws.on('error', (err: any) => {
|
|
205
|
+
logger.error('WebSocket error:', err.message || err);
|
|
87
206
|
});
|
|
88
207
|
|
|
89
|
-
ws.
|
|
90
|
-
|
|
208
|
+
ws.on('close', () => {
|
|
209
|
+
if (pingInterval) {
|
|
210
|
+
clearInterval(pingInterval);
|
|
211
|
+
pingInterval = undefined;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const shouldReconnect = !intentionalDisconnect && connectionState !== 'disconnected';
|
|
215
|
+
updateConnectionState('disconnected');
|
|
216
|
+
|
|
217
|
+
logger.debug('WebSocket disconnected');
|
|
218
|
+
|
|
219
|
+
// Pause TCP socket to apply backpressure - TCP will handle buffering
|
|
220
|
+
if (tcpSocket && !tcpSocket.destroyed && !tcpSocket.isPaused()) {
|
|
221
|
+
logger.debug('Pausing TCP socket (applying backpressure)');
|
|
222
|
+
tcpSocket.pause();
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
if (shouldReconnect && tcpSocket && !tcpSocket.destroyed) {
|
|
226
|
+
scheduleReconnect();
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
ws.on('open', () => {
|
|
231
|
+
const socket = ws as WebSocket;
|
|
232
|
+
logger.debug('WebSocket connected');
|
|
233
|
+
reconnectAttempts = 0;
|
|
234
|
+
updateConnectionState('connected');
|
|
91
235
|
|
|
92
236
|
pingInterval = setInterval(() => {
|
|
93
237
|
if (socket.readyState === WebSocket.OPEN) {
|
|
@@ -95,29 +239,104 @@ export async function startTcpTunnel(
|
|
|
95
239
|
}
|
|
96
240
|
}, 30_000);
|
|
97
241
|
|
|
98
|
-
// TCP
|
|
99
|
-
tcpSocket.
|
|
242
|
+
// Resume TCP socket - queued data will flow through
|
|
243
|
+
if (tcpSocket && tcpSocket.isPaused()) {
|
|
244
|
+
logger.debug('Resuming TCP socket (releasing backpressure)');
|
|
245
|
+
tcpSocket.resume();
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// TCP → WS: Forward data directly
|
|
249
|
+
const onTcpData = (chunk: Buffer) => {
|
|
100
250
|
if (socket.readyState === WebSocket.OPEN) {
|
|
101
251
|
socket.send(chunk);
|
|
102
252
|
}
|
|
103
|
-
|
|
253
|
+
// If WebSocket is not ready, data will queue in TCP buffers (backpressure)
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
// Remove old listener if exists and add new one
|
|
257
|
+
tcpSocket!.removeListener('data', onTcpData);
|
|
258
|
+
tcpSocket!.on('data', onTcpData);
|
|
104
259
|
|
|
105
260
|
// WS → TCP
|
|
106
261
|
socket.on('message', (data: any) => {
|
|
107
|
-
if (!tcpSocket.destroyed) {
|
|
262
|
+
if (tcpSocket && !tcpSocket.destroyed) {
|
|
108
263
|
tcpSocket.write(data as Buffer);
|
|
109
264
|
}
|
|
110
265
|
});
|
|
111
266
|
});
|
|
267
|
+
};
|
|
112
268
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
269
|
+
// TCP server error
|
|
270
|
+
server.once('error', (err) => {
|
|
271
|
+
close();
|
|
272
|
+
reject(new Error(`TCP server error: ${err.message}`));
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
const getConnectionState = (): TunnelConnectionState => {
|
|
276
|
+
return connectionState;
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
const onConnectionStateChange = (callback: TunnelConnectionStateCallback): (() => void) => {
|
|
280
|
+
stateChangeCallbacks.add(callback);
|
|
281
|
+
return () => {
|
|
282
|
+
stateChangeCallbacks.delete(callback);
|
|
283
|
+
};
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
// Listening
|
|
287
|
+
server.once('listening', () => {
|
|
288
|
+
const address = server.address();
|
|
289
|
+
if (!address || typeof address === 'string') {
|
|
117
290
|
close();
|
|
291
|
+
return reject(new Error('Failed to obtain listening address'));
|
|
292
|
+
}
|
|
293
|
+
resolve({
|
|
294
|
+
address,
|
|
295
|
+
close,
|
|
296
|
+
getConnectionState,
|
|
297
|
+
onConnectionStateChange,
|
|
298
|
+
});
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
// Helper to clean up current connection but keep server alive
|
|
302
|
+
const cleanupConnection = () => {
|
|
303
|
+
cleanup();
|
|
304
|
+
updateConnectionState('disconnected');
|
|
305
|
+
if (tcpSocket && !tcpSocket.destroyed) {
|
|
306
|
+
tcpSocket.destroy();
|
|
307
|
+
tcpSocket = undefined;
|
|
308
|
+
}
|
|
309
|
+
// Reset reconnection state for next connection
|
|
310
|
+
reconnectAttempts = 0;
|
|
311
|
+
intentionalDisconnect = false;
|
|
312
|
+
logger.debug('Connection cleaned up, ready for new TCP connection');
|
|
313
|
+
};
|
|
314
|
+
|
|
315
|
+
// On TCP connection (can happen multiple times)
|
|
316
|
+
server.on('connection', (socket) => {
|
|
317
|
+
// If there's already an active connection, reject the new one
|
|
318
|
+
if (tcpSocket && !tcpSocket.destroyed) {
|
|
319
|
+
logger.debug('Rejecting new TCP connection - already have an active connection');
|
|
320
|
+
socket.destroy();
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
logger.debug('TCP client connected');
|
|
325
|
+
tcpSocket = socket;
|
|
326
|
+
|
|
327
|
+
// TCP socket handlers
|
|
328
|
+
tcpSocket.on('close', () => {
|
|
329
|
+
logger.debug('TCP socket closed by client');
|
|
330
|
+
cleanupConnection();
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
tcpSocket.on('error', (err: any) => {
|
|
334
|
+
logger.error('TCP socket error:', err);
|
|
335
|
+
cleanupConnection();
|
|
118
336
|
});
|
|
119
337
|
|
|
120
|
-
|
|
338
|
+
// Start WebSocket connection
|
|
339
|
+
setupWebSocket();
|
|
121
340
|
});
|
|
122
341
|
|
|
123
342
|
// Start listening
|
package/src/version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const VERSION = '0.13.
|
|
1
|
+
export const VERSION = '0.13.2'; // x-release-please-version
|
package/tunnel.d.mts
CHANGED
|
@@ -1,25 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Controls the verbosity of logging in the tunnel
|
|
3
|
+
*/
|
|
4
|
+
export type LogLevel = 'none' | 'error' | 'warn' | 'info' | 'debug';
|
|
5
|
+
/**
|
|
6
|
+
* Connection state of the tunnel
|
|
7
|
+
*/
|
|
8
|
+
export type TunnelConnectionState = 'connecting' | 'connected' | 'disconnected' | 'reconnecting';
|
|
9
|
+
/**
|
|
10
|
+
* Callback function for tunnel connection state changes
|
|
11
|
+
*/
|
|
12
|
+
export type TunnelConnectionStateCallback = (state: TunnelConnectionState) => void;
|
|
1
13
|
export interface Tunnel {
|
|
2
14
|
address: {
|
|
3
15
|
address: string;
|
|
4
16
|
port: number;
|
|
5
17
|
};
|
|
6
18
|
close: () => void;
|
|
19
|
+
/**
|
|
20
|
+
* Get current WebSocket connection state
|
|
21
|
+
*/
|
|
22
|
+
getConnectionState: () => TunnelConnectionState;
|
|
23
|
+
/**
|
|
24
|
+
* Register callback for WebSocket connection state changes
|
|
25
|
+
* @returns A function to unregister the callback
|
|
26
|
+
*/
|
|
27
|
+
onConnectionStateChange: (callback: TunnelConnectionStateCallback) => () => void;
|
|
28
|
+
}
|
|
29
|
+
export interface TcpTunnelOptions {
|
|
30
|
+
/**
|
|
31
|
+
* Maximum number of reconnection attempts
|
|
32
|
+
* @default 6
|
|
33
|
+
*/
|
|
34
|
+
maxReconnectAttempts?: number;
|
|
35
|
+
/**
|
|
36
|
+
* Initial reconnection delay in milliseconds
|
|
37
|
+
* @default 1000
|
|
38
|
+
*/
|
|
39
|
+
reconnectDelay?: number;
|
|
40
|
+
/**
|
|
41
|
+
* Maximum reconnection delay in milliseconds
|
|
42
|
+
* @default 30000
|
|
43
|
+
*/
|
|
44
|
+
maxReconnectDelay?: number;
|
|
45
|
+
/**
|
|
46
|
+
* Controls logging verbosity
|
|
47
|
+
* @default 'info'
|
|
48
|
+
*/
|
|
49
|
+
logLevel?: LogLevel;
|
|
7
50
|
}
|
|
8
51
|
/**
|
|
9
|
-
* Starts a
|
|
52
|
+
* Starts a persistent TCP → WebSocket proxy.
|
|
10
53
|
*
|
|
11
54
|
* The function creates a local TCP server that listens on an ephemeral port on
|
|
12
|
-
* 127.0.0.1.
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* proxy instance.
|
|
55
|
+
* 127.0.0.1. When a TCP client connects, it forwards all traffic between that
|
|
56
|
+
* client and `remoteURL` through an authenticated WebSocket. The server remains
|
|
57
|
+
* active even after the client disconnects, allowing reconnection without
|
|
58
|
+
* recreating the tunnel.
|
|
17
59
|
*
|
|
18
60
|
* @param remoteURL Remote WebSocket endpoint (e.g. wss://example.com/instance)
|
|
19
61
|
* @param token Bearer token sent as `Authorization` header
|
|
20
62
|
* @param hostname Optional IP address to listen on. Default is 127.0.0.1
|
|
21
63
|
* @param port Optional port number to listen on. Default is to ask Node.js
|
|
22
64
|
* to find an available non-privileged port.
|
|
65
|
+
* @param options Optional reconnection configuration
|
|
23
66
|
*/
|
|
24
|
-
export declare function startTcpTunnel(remoteURL: string, token: string, hostname: string, port: number): Promise<Tunnel>;
|
|
67
|
+
export declare function startTcpTunnel(remoteURL: string, token: string, hostname: string, port: number, options?: TcpTunnelOptions): Promise<Tunnel>;
|
|
25
68
|
//# sourceMappingURL=tunnel.d.mts.map
|
package/tunnel.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tunnel.d.mts","sourceRoot":"","sources":["src/tunnel.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,MAAM;IACrB,OAAO,EAAE;QACP,OAAO,EAAE,MAAM,CAAC;QAChB,IAAI,EAAE,MAAM,CAAC;KACd,CAAC;IACF,KAAK,EAAE,MAAM,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"tunnel.d.mts","sourceRoot":"","sources":["src/tunnel.ts"],"names":[],"mappings":"AAGA;;GAEG;AACH,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;AAEpE;;GAEG;AACH,MAAM,MAAM,qBAAqB,GAAG,YAAY,GAAG,WAAW,GAAG,cAAc,GAAG,cAAc,CAAC;AAEjG;;GAEG;AACH,MAAM,MAAM,6BAA6B,GAAG,CAAC,KAAK,EAAE,qBAAqB,KAAK,IAAI,CAAC;AAEnF,MAAM,WAAW,MAAM;IACrB,OAAO,EAAE;QACP,OAAO,EAAE,MAAM,CAAC;QAChB,IAAI,EAAE,MAAM,CAAC;KACd,CAAC;IACF,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB;;OAEG;IACH,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;IAChD;;;OAGG;IACH,uBAAuB,EAAE,CAAC,QAAQ,EAAE,6BAA6B,KAAK,MAAM,IAAI,CAAC;CAClF;AAED,MAAM,WAAW,gBAAgB;IAC/B;;;OAGG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;OAGG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B;;;OAGG;IACH,QAAQ,CAAC,EAAE,QAAQ,CAAC;CACrB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,cAAc,CAClC,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,gBAAgB,GACzB,OAAO,CAAC,MAAM,CAAC,CAwQjB"}
|
package/tunnel.d.ts
CHANGED
|
@@ -1,25 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Controls the verbosity of logging in the tunnel
|
|
3
|
+
*/
|
|
4
|
+
export type LogLevel = 'none' | 'error' | 'warn' | 'info' | 'debug';
|
|
5
|
+
/**
|
|
6
|
+
* Connection state of the tunnel
|
|
7
|
+
*/
|
|
8
|
+
export type TunnelConnectionState = 'connecting' | 'connected' | 'disconnected' | 'reconnecting';
|
|
9
|
+
/**
|
|
10
|
+
* Callback function for tunnel connection state changes
|
|
11
|
+
*/
|
|
12
|
+
export type TunnelConnectionStateCallback = (state: TunnelConnectionState) => void;
|
|
1
13
|
export interface Tunnel {
|
|
2
14
|
address: {
|
|
3
15
|
address: string;
|
|
4
16
|
port: number;
|
|
5
17
|
};
|
|
6
18
|
close: () => void;
|
|
19
|
+
/**
|
|
20
|
+
* Get current WebSocket connection state
|
|
21
|
+
*/
|
|
22
|
+
getConnectionState: () => TunnelConnectionState;
|
|
23
|
+
/**
|
|
24
|
+
* Register callback for WebSocket connection state changes
|
|
25
|
+
* @returns A function to unregister the callback
|
|
26
|
+
*/
|
|
27
|
+
onConnectionStateChange: (callback: TunnelConnectionStateCallback) => () => void;
|
|
28
|
+
}
|
|
29
|
+
export interface TcpTunnelOptions {
|
|
30
|
+
/**
|
|
31
|
+
* Maximum number of reconnection attempts
|
|
32
|
+
* @default 6
|
|
33
|
+
*/
|
|
34
|
+
maxReconnectAttempts?: number;
|
|
35
|
+
/**
|
|
36
|
+
* Initial reconnection delay in milliseconds
|
|
37
|
+
* @default 1000
|
|
38
|
+
*/
|
|
39
|
+
reconnectDelay?: number;
|
|
40
|
+
/**
|
|
41
|
+
* Maximum reconnection delay in milliseconds
|
|
42
|
+
* @default 30000
|
|
43
|
+
*/
|
|
44
|
+
maxReconnectDelay?: number;
|
|
45
|
+
/**
|
|
46
|
+
* Controls logging verbosity
|
|
47
|
+
* @default 'info'
|
|
48
|
+
*/
|
|
49
|
+
logLevel?: LogLevel;
|
|
7
50
|
}
|
|
8
51
|
/**
|
|
9
|
-
* Starts a
|
|
52
|
+
* Starts a persistent TCP → WebSocket proxy.
|
|
10
53
|
*
|
|
11
54
|
* The function creates a local TCP server that listens on an ephemeral port on
|
|
12
|
-
* 127.0.0.1.
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* proxy instance.
|
|
55
|
+
* 127.0.0.1. When a TCP client connects, it forwards all traffic between that
|
|
56
|
+
* client and `remoteURL` through an authenticated WebSocket. The server remains
|
|
57
|
+
* active even after the client disconnects, allowing reconnection without
|
|
58
|
+
* recreating the tunnel.
|
|
17
59
|
*
|
|
18
60
|
* @param remoteURL Remote WebSocket endpoint (e.g. wss://example.com/instance)
|
|
19
61
|
* @param token Bearer token sent as `Authorization` header
|
|
20
62
|
* @param hostname Optional IP address to listen on. Default is 127.0.0.1
|
|
21
63
|
* @param port Optional port number to listen on. Default is to ask Node.js
|
|
22
64
|
* to find an available non-privileged port.
|
|
65
|
+
* @param options Optional reconnection configuration
|
|
23
66
|
*/
|
|
24
|
-
export declare function startTcpTunnel(remoteURL: string, token: string, hostname: string, port: number): Promise<Tunnel>;
|
|
67
|
+
export declare function startTcpTunnel(remoteURL: string, token: string, hostname: string, port: number, options?: TcpTunnelOptions): Promise<Tunnel>;
|
|
25
68
|
//# sourceMappingURL=tunnel.d.ts.map
|
package/tunnel.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tunnel.d.ts","sourceRoot":"","sources":["src/tunnel.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,MAAM;IACrB,OAAO,EAAE;QACP,OAAO,EAAE,MAAM,CAAC;QAChB,IAAI,EAAE,MAAM,CAAC;KACd,CAAC;IACF,KAAK,EAAE,MAAM,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"tunnel.d.ts","sourceRoot":"","sources":["src/tunnel.ts"],"names":[],"mappings":"AAGA;;GAEG;AACH,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;AAEpE;;GAEG;AACH,MAAM,MAAM,qBAAqB,GAAG,YAAY,GAAG,WAAW,GAAG,cAAc,GAAG,cAAc,CAAC;AAEjG;;GAEG;AACH,MAAM,MAAM,6BAA6B,GAAG,CAAC,KAAK,EAAE,qBAAqB,KAAK,IAAI,CAAC;AAEnF,MAAM,WAAW,MAAM;IACrB,OAAO,EAAE;QACP,OAAO,EAAE,MAAM,CAAC;QAChB,IAAI,EAAE,MAAM,CAAC;KACd,CAAC;IACF,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB;;OAEG;IACH,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;IAChD;;;OAGG;IACH,uBAAuB,EAAE,CAAC,QAAQ,EAAE,6BAA6B,KAAK,MAAM,IAAI,CAAC;CAClF;AAED,MAAM,WAAW,gBAAgB;IAC/B;;;OAGG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;OAGG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B;;;OAGG;IACH,QAAQ,CAAC,EAAE,QAAQ,CAAC;CACrB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,cAAc,CAClC,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,gBAAgB,GACzB,OAAO,CAAC,MAAM,CAAC,CAwQjB"}
|