Skip to content

Commit 1ac48e2

Browse files
authored
feat: tunnel package (#115)
1 parent ba0868b commit 1ac48e2

24 files changed

+5229
-35
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@ dist/
55
.sonda
66
.next
77
**/tsconfig.tsbuildinfo
8+
.wrangler

biome.json

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
{
2+
"$schema": "https://biomejs.dev/schemas/2.3.2/schema.json",
3+
"vcs": {
4+
"enabled": true,
5+
"clientKind": "git",
6+
"useIgnoreFile": true
7+
},
8+
"files": {
9+
"ignoreUnknown": false
10+
},
11+
"formatter": {
12+
"enabled": false
13+
},
14+
"linter": {
15+
"enabled": true,
16+
"rules": {
17+
"recommended": true,
18+
"suspicious": {
19+
"noConsole": "warn"
20+
}
21+
}
22+
},
23+
"overrides": [
24+
{
25+
"includes": ["**/*.test.ts", "**/*.test-suite.ts"],
26+
"linter": {
27+
"rules": {
28+
"style": {
29+
"noNonNullAssertion": "off"
30+
}
31+
}
32+
}
33+
}
34+
],
35+
36+
"javascript": {
37+
"formatter": {
38+
"quoteStyle": "double"
39+
}
40+
},
41+
"assist": {
42+
"enabled": true,
43+
"actions": {
44+
"source": {
45+
"organizeImports": "on"
46+
}
47+
}
48+
}
49+
}

bun.lock

Lines changed: 239 additions & 3 deletions
Large diffs are not rendered by default.

packages/tunnel/examples/client.ts

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/**
2+
* Example tunnel client that proxies HTTP and WebSocket requests to localhost:8000.
3+
*
4+
* Run with: npx tsx examples/client.ts
5+
*
6+
* Make sure you have:
7+
* 1. The tunnel server running (npx tsx examples/server.ts)
8+
* 2. A local server running on port 8000 (e.g., python -m http.server 8000)
9+
*
10+
* For WebSocket testing, you can use a simple WebSocket server like:
11+
* npx wscat -l 8000
12+
* Then connect via the tunnel URL using wscat or a browser.
13+
*/
14+
/** biome-ignore-all lint/suspicious/noConsole: this is an example file */
15+
16+
import { TunnelClient } from "../src/client";
17+
18+
const SERVER_URL = "http://localhost:8080";
19+
const CLIENT_SECRET = crypto.randomUUID();
20+
const LOCAL_SERVER_PORT = 8000;
21+
22+
const client = new TunnelClient({
23+
serverUrl: SERVER_URL,
24+
secret: CLIENT_SECRET,
25+
transformRequest: async ({ method, url, headers }) => {
26+
url.protocol = "http";
27+
url.host = `localhost:${LOCAL_SERVER_PORT}`;
28+
return { method, url, headers };
29+
},
30+
onConnect: ({ url, id }) => {
31+
console.log(`Connected to tunnel server!`);
32+
console.log(`Client secret: ${CLIENT_SECRET}`);
33+
console.log(`Public URL: ${url}`);
34+
console.log(`Tunnel ID: ${id}`);
35+
console.log(
36+
`\nHTTP requests to ${url}/* will be proxied to http://localhost:${LOCAL_SERVER_PORT}/*`
37+
);
38+
console.log(
39+
`WebSocket connections to ${url.replace("http", "ws")}/* will be proxied to ws://localhost:${LOCAL_SERVER_PORT}/*`
40+
);
41+
},
42+
onDisconnect: () => {
43+
console.log("Disconnected from tunnel server");
44+
},
45+
onError: (error) => {
46+
console.error("Tunnel error:", error);
47+
},
48+
});
49+
50+
console.log(`Connecting to tunnel server at ${SERVER_URL}...`);
51+
console.log(
52+
`Will proxy HTTP and WebSocket requests to localhost:${LOCAL_SERVER_PORT}`
53+
);
54+
55+
const disposable = client.connect();
56+
57+
// Handle graceful shutdown
58+
process.on("SIGINT", () => {
59+
console.log("\nDisconnecting...");
60+
disposable.dispose();
61+
process.exit(0);
62+
});
63+
64+
process.on("SIGTERM", () => {
65+
disposable.dispose();
66+
process.exit(0);
67+
});

packages/tunnel/examples/server.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/**
2+
* Example tunnel server for local testing.
3+
*
4+
* Run with: npx tsx examples/server.ts
5+
*/
6+
/** biome-ignore-all lint/suspicious/noConsole: this is an example file */
7+
8+
import { createLocalServer } from "../src/server/local";
9+
10+
const PORT = 8080;
11+
const SERVER_SECRET = "example-server-secret";
12+
13+
const { close } = createLocalServer({
14+
port: PORT,
15+
secret: SERVER_SECRET,
16+
baseUrl: `http://localhost:${PORT}`,
17+
mode: "subpath",
18+
onReady: (port) => {
19+
console.log(`Tunnel server running on http://localhost:${port}`);
20+
console.log(`Waiting for clients to connect...`);
21+
},
22+
onClientConnect: (id) => {
23+
console.log(`Client connected: ${id}`);
24+
console.log(`Public URL: http://localhost:${PORT}/tunnel/${id}`);
25+
},
26+
onClientDisconnect: (id) => {
27+
console.log(`Client disconnected: ${id}`);
28+
},
29+
});
30+
31+
// Handle graceful shutdown
32+
process.on("SIGINT", () => {
33+
console.log("\nShutting down server...");
34+
close();
35+
process.exit(0);
36+
});
37+
38+
process.on("SIGTERM", () => {
39+
close();
40+
process.exit(0);
41+
});

packages/tunnel/package.json

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
{
2+
"name": "@blink-sdk/tunnel",
3+
"description": "Tunnel - expose local servers via a public URL",
4+
"version": "0.0.1",
5+
"type": "module",
6+
"keywords": [
7+
"blink",
8+
"tunnel",
9+
"proxy",
10+
"webhook"
11+
],
12+
"publishConfig": {
13+
"access": "public"
14+
},
15+
"author": "Coder",
16+
"license": "MIT",
17+
"repository": {
18+
"type": "git",
19+
"url": "git+https://github.com/coder/blink.git"
20+
},
21+
"homepage": "https://github.com/coder/blink/tree/main/packages/tunnel",
22+
"bugs": {
23+
"url": "https://github.com/coder/blink/issues"
24+
},
25+
"files": [
26+
"dist"
27+
],
28+
"scripts": {
29+
"build": "tsdown",
30+
"typecheck": "tsgo --noEmit"
31+
},
32+
"exports": {
33+
".": {
34+
"import": "./dist/index.js",
35+
"types": "./dist/index.d.ts"
36+
}
37+
},
38+
"dependencies": {
39+
"@blink-sdk/events": "workspace:*",
40+
"@blink-sdk/multiplexer": "workspace:*",
41+
"ws": "^8.18.0"
42+
},
43+
"devDependencies": {
44+
"@cloudflare/workers-types": "^4.20250109.0",
45+
"@types/ws": "^8.5.10",
46+
"hono": "^4.7.10",
47+
"tsdown": "^0.11.12",
48+
"wrangler": "^4.22.0"
49+
}
50+
}

0 commit comments

Comments
 (0)