Skip to content

Commit 8d5fb54

Browse files
authored
Merge pull request #28 from JacobLinCool/tunnel-class
Tunnel class
2 parents 9681e9c + 063c96f commit 8d5fb54

File tree

10 files changed

+429
-163
lines changed

10 files changed

+429
-163
lines changed

.changeset/tall-gorillas-taste.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"cloudflared": minor
3+
---
4+
5+
Tunnel class with custom output parser

.github/workflows/test.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,10 @@ jobs:
2020
- macos-latest
2121
version:
2222
- "latest"
23-
- "2024.8.2"
23+
- "2024.12.1"
24+
- "2024.10.1"
25+
- "2024.8.3"
2426
- "2024.6.1"
25-
- "2024.4.1"
26-
- "2024.2.1"
2727

2828
name: "${{ matrix.os }} - ${{ matrix.version }}"
2929
runs-on: ${{ matrix.os }}

README.md

Lines changed: 16 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -77,29 +77,30 @@ spawn(bin, ["--version"], { stdio: "inherit" });
7777

7878
Checkout [`examples/tunnel.js`](examples/tunnel.js).
7979

80+
`Tunnel` is inherited from `EventEmitter`, so you can listen to the events it emits, checkout [`examples/events.mjs`](examples/events.mjs).
81+
8082
```js
81-
import { tunnel } from "cloudflared";
83+
import { Tunnel } from "cloudflared";
8284

8385
console.log("Cloudflared Tunnel Example.");
8486
main();
8587

8688
async function main() {
8789
// run: cloudflared tunnel --hello-world
88-
const { url, connections, child, stop } = tunnel({ "--hello-world": null });
90+
const tunnel = Tunnel.quick();
8991

9092
// show the url
93+
const url = new Promise((resolve) => tunnel.once("url", resolve));
9194
console.log("LINK:", await url);
9295

93-
// wait for the all 4 connections to be established
94-
const conns = await Promise.all(connections);
95-
96-
// show the connections
97-
console.log("Connections Ready!", conns);
96+
// wait for connection to be established
97+
const conn = new Promise((resolve) => tunnel.once("connected", resolve));
98+
console.log("CONN:", await conn);
9899

99100
// stop the tunnel after 15 seconds
100-
setTimeout(stop, 15_000);
101+
setTimeout(tunnel.stop, 15_000);
101102

102-
child.on("exit", (code) => {
103+
tunnel.on("exit", (code) => {
103104
console.log("tunnel process exited with code", code);
104105
});
105106
}
@@ -108,29 +109,12 @@ async function main() {
108109
```sh
109110
❯ node examples/tunnel.js
110111
Cloudflared Tunnel Example.
111-
LINK: https://aimed-our-bite-brought.trycloudflare.com
112-
Connections Ready! [
113-
{
114-
id: 'd4681cd9-217d-40e2-9e15-427f9fb77856',
115-
ip: '198.41.200.23',
116-
location: 'MIA'
117-
},
118-
{
119-
id: 'b40d2cdd-0b99-4838-b1eb-9a58a6999123',
120-
ip: '198.41.192.107',
121-
location: 'LAX'
122-
},
123-
{
124-
id: '55545211-3f63-4722-99f1-d5fea688dabf',
125-
ip: '198.41.200.53',
126-
location: 'MIA'
127-
},
128-
{
129-
id: 'f3d5938a-d48c-463c-a4f7-a158782a0ddb',
130-
ip: '198.41.192.77',
131-
location: 'LAX'
132-
}
133-
]
112+
LINK: https://mailto-davis-wilderness-facts.trycloudflare.com
113+
CONN: {
114+
id: 'df1b8330-44ea-4ecb-bb93-8a32400f6d1c',
115+
ip: '198.41.200.193',
116+
location: 'tpe01'
117+
}
134118
tunnel process exited with code 0
135119
```
136120

examples/events.mjs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { Tunnel, ConfigHandler } from "cloudflared";
2+
3+
const token = process.env.CLOUDFLARED_TOKEN;
4+
if (!token) {
5+
throw new Error("CLOUDFLARED_TOKEN is not set");
6+
}
7+
8+
const tunnel = Tunnel.withToken(token);
9+
const handler = new ConfigHandler(tunnel);
10+
11+
handler.on("config", ({ config }) => {
12+
console.log("Config", config);
13+
});
14+
15+
tunnel.on("url", (url) => {
16+
console.log("Tunnel is ready at", url);
17+
});
18+
19+
tunnel.on("connected", (connection) => {
20+
console.log("Connected to", connection);
21+
});
22+
23+
tunnel.on("disconnected", (connection) => {
24+
console.log("Disconnected from", connection);
25+
});
26+
27+
tunnel.on("stdout", (data) => {
28+
console.log("Tunnel stdout", data);
29+
});
30+
31+
tunnel.on("stderr", (data) => {
32+
console.error("Tunnel stderr", data);
33+
});
34+
35+
tunnel.on("exit", (code, signal) => {
36+
console.log("Tunnel exited with code", code, "and signal", signal);
37+
});
38+
39+
tunnel.on("error", (error) => {
40+
console.error("Error", error);
41+
});
42+
43+
process.on("SIGINT", () => {
44+
console.log("Tunnel stopped", tunnel.stop());
45+
});

examples/tunnel.js

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,23 @@
1-
const { tunnel } = require("cloudflared");
1+
const { Tunnel } = require("cloudflared");
22

33
console.log("Cloudflared Tunnel Example.");
44
main();
55

66
async function main() {
77
// run: cloudflared tunnel --hello-world
8-
const { url, connections, child, stop } = tunnel({ "--hello-world": null });
8+
const tunnel = Tunnel.quick();
99

1010
// show the url
11+
const url = new Promise((resolve) => tunnel.once("url", resolve));
1112
console.log("LINK:", await url);
1213

13-
// wait for the all 4 connections to be established
14-
const conns = await Promise.all(connections);
15-
16-
// show the connections
17-
console.log("Connections Ready!", conns);
14+
const conn = new Promise((resolve) => tunnel.once("connected", resolve));
15+
console.log("CONN:", await conn);
1816

1917
// stop the tunnel after 15 seconds
20-
setTimeout(stop, 15_000);
18+
setTimeout(tunnel.stop, 15_000);
2119

22-
child.on("exit", (code) => {
20+
tunnel.on("exit", (code) => {
2321
console.log("tunnel process exited with code", code);
2422
});
2523
}

examples/tunnel.mjs

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,23 @@
1-
import { tunnel } from "cloudflared";
1+
import { Tunnel } from "cloudflared";
22

33
console.log("Cloudflared Tunnel Example.");
44
main();
55

66
async function main() {
77
// run: cloudflared tunnel --hello-world
8-
const { url, connections, child, stop } = tunnel({ "--hello-world": null });
8+
const tunnel = Tunnel.quick();
99

1010
// show the url
11+
const url = new Promise((resolve) => tunnel.once("url", resolve));
1112
console.log("LINK:", await url);
1213

13-
// wait for the all 4 connections to be established
14-
const conns = await Promise.all(connections);
15-
16-
// show the connections
17-
console.log("Connections Ready!", conns);
14+
const conn = new Promise((resolve) => tunnel.once("connected", resolve));
15+
console.log("CONN:", await conn);
1816

1917
// stop the tunnel after 15 seconds
20-
setTimeout(stop, 15_000);
18+
setTimeout(tunnel.stop, 15_000);
2119

22-
child.on("exit", (code) => {
20+
tunnel.on("exit", (code) => {
2321
console.log("tunnel process exited with code", code);
2422
});
2523
}

src/_tests/index.test.ts

Lines changed: 39 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import { ChildProcess } from "node:child_process";
22
import fs from "node:fs";
3-
import { bin, install, tunnel, service } from "../lib.js";
3+
import { Tunnel, bin, install, service } from "../lib.js";
44
import { describe, it, expect, beforeAll } from "vitest";
55

66
process.env.VERBOSE = "1";
77

88
describe(
99
"install",
10+
{ timeout: 60_000 },
1011
() => {
1112
it("should install binary", async () => {
1213
if (fs.existsSync(bin)) {
@@ -18,61 +19,55 @@ describe(
1819
expect(fs.existsSync(bin)).toBe(true);
1920
});
2021
},
21-
{ timeout: 60_000 },
2222
);
2323

2424
describe(
2525
"tunnel",
26+
{ timeout: 60_000 },
2627
() => {
2728
it("should create a tunnel", async () => {
28-
const { url, connections, child, stop } = tunnel({
29-
"--url": "localhost:8080",
30-
"--no-autoupdate": "true",
31-
});
29+
const tunnel = new Tunnel(["tunnel", "--url", "localhost:8080", "--no-autoupdate"]);
30+
const url = new Promise((resolve) => tunnel.once("url", resolve));
3231
expect(await url).toMatch(/https?:\/\/[^\s]+/);
33-
await connections[0]; // quick tunnel only has one connection
34-
expect(child).toBeInstanceOf(ChildProcess);
35-
stop();
32+
const conn = new Promise((resolve) => tunnel.once("connected", resolve));
33+
await conn; // quick tunnel only has one connection
34+
expect(tunnel.process).toBeInstanceOf(ChildProcess);
35+
tunnel.stop();
3636
});
3737
},
38-
{ timeout: 60_000 },
3938
);
4039

41-
describe(
42-
"service",
43-
() => {
44-
const TOKEN = process.env.TUNNEL_TOKEN;
45-
const should_run =
46-
TOKEN &&
47-
["darwin", "linux"].includes(process.platform) &&
48-
!(process.platform === "linux" && process.getuid?.() !== 0);
49-
if (should_run) {
50-
beforeAll(() => {
51-
if (service.exists()) {
52-
service.uninstall();
53-
}
54-
});
55-
}
56-
57-
it("should work", async (ctx) => {
58-
if (!should_run) {
59-
ctx.skip();
40+
describe("service", { timeout: 60_000 }, () => {
41+
const TOKEN = process.env.TUNNEL_TOKEN;
42+
const should_run =
43+
TOKEN &&
44+
["darwin", "linux"].includes(process.platform) &&
45+
!(process.platform === "linux" && process.getuid?.() !== 0);
46+
if (should_run) {
47+
beforeAll(() => {
48+
if (service.exists()) {
49+
service.uninstall();
6050
}
61-
expect(service.exists()).toBe(false);
62-
service.install(TOKEN);
51+
});
52+
}
6353

64-
await new Promise((r) => setTimeout(r, 15_000));
54+
it("should work", async (ctx) => {
55+
if (!should_run) {
56+
ctx.skip();
57+
}
58+
expect(service.exists()).toBe(false);
59+
service.install(TOKEN);
6560

66-
expect(service.exists()).toBe(true);
67-
const current = service.current();
68-
expect(current.tunnelID.length).toBeGreaterThan(0);
69-
expect(current.connectorID.length).toBeGreaterThan(0);
70-
expect(current.connections.length).toBeGreaterThan(0);
71-
expect(current.metrics.length).toBeGreaterThan(0);
72-
expect(current.config.ingress?.length).toBeGreaterThan(0);
61+
await new Promise((r) => setTimeout(r, 15_000));
7362

74-
service.uninstall();
75-
});
76-
},
77-
{ timeout: 60_000 },
78-
);
63+
expect(service.exists()).toBe(true);
64+
const current = service.current();
65+
expect(current.tunnelID.length).toBeGreaterThan(0);
66+
expect(current.connectorID.length).toBeGreaterThan(0);
67+
expect(current.connections.length).toBeGreaterThan(0);
68+
expect(current.metrics.length).toBeGreaterThan(0);
69+
expect(current.config.ingress?.length).toBeGreaterThan(0);
70+
71+
service.uninstall();
72+
});
73+
});

0 commit comments

Comments
 (0)