@dpkrn/nodetunnel 1.0.10 → 1.1.1
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 +63 -0
- package/README.md +39 -43
- package/cmd/test-lib/main.js +20 -1
- package/internal/inspector/index.js +1012 -0
- package/internal/inspector/inspector.css +1085 -0
- package/internal/inspector/inspector.html +263 -0
- package/internal/inspector/inspector.js +597 -0
- package/internal/{tunnel → inspector}/logstore.js +24 -0
- package/internal/inspector/theme-postman.css +38 -0
- package/internal/inspector/theme-terminal.css +38 -0
- package/internal/tunnel/tunnel.js +35 -23
- package/package.json +3 -1
- package/pkg/tunnel/tunnel.js +2 -3
- package/internal/tunnel/inspector-page.html +0 -482
- package/internal/tunnel/inspector.js +0 -266
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
|
+
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
Nothing unreleased yet.
|
|
10
|
+
|
|
11
|
+
## [1.0.10] - 2026-04-12
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
|
|
15
|
+
- Expanded README with clearer coverage of what nodetunnel does and how to use it.
|
|
16
|
+
|
|
17
|
+
## [1.0.9] - 2026-04-12
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
|
|
21
|
+
- Makefile with `pkg` (publish) and `test` targets for maintainers.
|
|
22
|
+
- Contributor Covenant Code of Conduct.
|
|
23
|
+
- Local traffic inspector: HTTP UI plus WebSocket streaming of request/response logs (via `ws`).
|
|
24
|
+
- Structured logging store for tunneled HTTP exchanges.
|
|
25
|
+
|
|
26
|
+
### Changed
|
|
27
|
+
|
|
28
|
+
- Refactor of the published tunnel package layout and dependencies.
|
|
29
|
+
- Clearer logging during tunnel setup and while forwarding requests.
|
|
30
|
+
|
|
31
|
+
## [1.0.8] - 2026-04-12
|
|
32
|
+
|
|
33
|
+
### Changed
|
|
34
|
+
|
|
35
|
+
- Maintenance release (version alignment).
|
|
36
|
+
|
|
37
|
+
## [1.0.7] - 2026-04-12
|
|
38
|
+
|
|
39
|
+
### Changed
|
|
40
|
+
|
|
41
|
+
- Default tunnel server host set to `clickly.cv`.
|
|
42
|
+
- Public URL string returned by the tunnel client updated for consistency with the server.
|
|
43
|
+
- Tunnel handshake and messaging updated (connection UUID and success-line formatting for the assigned URL).
|
|
44
|
+
|
|
45
|
+
## [1.0.4] - 2026-04-03
|
|
46
|
+
|
|
47
|
+
### Added
|
|
48
|
+
|
|
49
|
+
- Node.js tunnel library: expose a local HTTP server through a gotunnel/yamux-compatible remote (`startTunnel` and related APIs).
|
|
50
|
+
|
|
51
|
+
## [1.0.2] - 2026-04-03
|
|
52
|
+
|
|
53
|
+
### Added
|
|
54
|
+
|
|
55
|
+
- Initial `@dpkrn/nodetunnel` package scaffold.
|
|
56
|
+
|
|
57
|
+
[Unreleased]: https://github.com/DpkRn/nodetunnel/compare/v1.0.10...HEAD
|
|
58
|
+
[1.0.10]: https://github.com/DpkRn/nodetunnel/compare/v1.0.9...v1.0.10
|
|
59
|
+
[1.0.9]: https://github.com/DpkRn/nodetunnel/compare/v1.0.8...v1.0.9
|
|
60
|
+
[1.0.8]: https://github.com/DpkRn/nodetunnel/compare/v1.0.7...v1.0.8
|
|
61
|
+
[1.0.7]: https://github.com/DpkRn/nodetunnel/compare/v1.0.4...v1.0.7
|
|
62
|
+
[1.0.4]: https://github.com/DpkRn/nodetunnel/compare/v1.0.2...v1.0.4
|
|
63
|
+
[1.0.2]: https://github.com/DpkRn/nodetunnel/releases/tag/v1.0.2
|
package/README.md
CHANGED
|
@@ -16,7 +16,7 @@ From **Node.js**, you get a **public URL** for webhooks, demos, sharing a dev se
|
|
|
16
16
|
- No port forwarding or firewall configuration needed
|
|
17
17
|
- Works behind NAT or private networks
|
|
18
18
|
- Simple integration with existing Node.js HTTP servers (Express, Fastify, plain `http`, etc.)
|
|
19
|
-
-
|
|
19
|
+
- Optional **traffic inspector**: local dashboard to capture tunneled requests, inspect them, and replay against your app
|
|
20
20
|
|
|
21
21
|
Incoming traffic reaches the public URL, is forwarded through the tunnel, and is proxied to your local HTTP server (e.g., `localhost:8080`).
|
|
22
22
|
|
|
@@ -40,7 +40,7 @@ This enables exposing local development servers without port forwarding, firewal
|
|
|
40
40
|
- **No separate tunnel process** — call one function from your app.
|
|
41
41
|
- **Works with your existing server** — Express, Fastify, or plain `http`.
|
|
42
42
|
- **Simple API** — you get a public `url` and a `stop()` when you are done.
|
|
43
|
-
- **Optional traffic inspector** — local
|
|
43
|
+
- **Optional traffic inspector** — pass `inspector: true` to start a local HTTP UI on loopback (captures, replay, headers/bodies). By default the inspector is **off**. Themes are chosen **inside the inspector UI** (not via `startTunnel` options).
|
|
44
44
|
|
|
45
45
|
---
|
|
46
46
|
|
|
@@ -54,8 +54,6 @@ This package is **ESM** (`import` / `export`).
|
|
|
54
54
|
|
|
55
55
|
---
|
|
56
56
|
|
|
57
|
-
|
|
58
|
-
|
|
59
57
|
## Quick Example
|
|
60
58
|
|
|
61
59
|
```js
|
|
@@ -69,8 +67,8 @@ app.get("/", (req, res) => res.send("OK"));
|
|
|
69
67
|
|
|
70
68
|
app.listen(PORT, async () => {
|
|
71
69
|
const { url, stop } = await startTunnel(String(PORT));
|
|
72
|
-
//url
|
|
73
|
-
//stop()
|
|
70
|
+
// `url` — public URL for your server
|
|
71
|
+
// `stop()` — closes the tunnel connection
|
|
74
72
|
});
|
|
75
73
|
```
|
|
76
74
|
|
|
@@ -107,29 +105,33 @@ Run with `node app.js`. Open the printed URL in a browser or share it for webhoo
|
|
|
107
105
|
|
|
108
106
|
---
|
|
109
107
|
|
|
110
|
-
## Traffic inspector (local dashboard)
|
|
108
|
+
## Traffic inspector (optional local dashboard)
|
|
109
|
+
|
|
110
|
+
**Default:** `inspector` is **`false`** if you omit options or do not set `inspector`. No inspector server, no capture buffer, no extra listen port.
|
|
111
111
|
|
|
112
|
-
When
|
|
112
|
+
When **`inspector: true`**:
|
|
113
113
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
- **Inspect** — request/response headers and bodies for each capture.
|
|
117
|
-
- **Modify** — modify request header/path and can replay.
|
|
118
|
-
- **Replay** — send a capture again to your local app, or edit method/path/headers/body and replay (aligned with the **gotunnel** inspector behavior).
|
|
114
|
+
1. Starts a small **HTTP server on your machine** (default listen **`http://127.0.0.1:4040`**) that serves the inspector UI (override with `inspectorAddr`).
|
|
115
|
+
2. **Captures** each tunneled request/response in memory (up to **`logs`** entries) so the UI can list them and push updates over WebSocket.
|
|
119
116
|
|
|
120
|
-
|
|
117
|
+
When **`inspector` stays false** (the default):
|
|
121
118
|
|
|
122
|
-
|
|
119
|
+
- No inspector process runs — nothing listens on the inspector port.
|
|
120
|
+
- No traffic is stored for inspection (`logs` and `inspectorAddr` are ignored).
|
|
121
|
+
- The startup banner omits the **Inspector →** line.
|
|
123
122
|
|
|
124
|
-
|
|
123
|
+
### What you get with the inspector enabled
|
|
125
124
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
| **`"light"`** | Light gray/white background, high-contrast text for bright environments. |
|
|
125
|
+
- **Live traffic** — tunneled requests appear in the UI (WebSocket updates).
|
|
126
|
+
- **History** — recent captures in memory (size limited by `logs`).
|
|
127
|
+
- **Inspect** — request/response headers and bodies (when captured).
|
|
128
|
+
- **Replay** — send a capture again to your local app, or edit method/path/headers/body and replay.
|
|
131
129
|
|
|
132
|
-
###
|
|
130
|
+
### Themes (inspector UI only)
|
|
131
|
+
|
|
132
|
+
Postman-style and Terminal-style palettes are available from the **Theme** dropdown in the inspector page; the choice is stored in the browser (localStorage). You do **not** configure themes on `startTunnel`.
|
|
133
|
+
|
|
134
|
+
### Example: inspector enabled with custom port and log limit
|
|
133
135
|
|
|
134
136
|
```js
|
|
135
137
|
import http from "node:http";
|
|
@@ -144,16 +146,11 @@ const server = http.createServer((req, res) => {
|
|
|
144
146
|
|
|
145
147
|
server.listen(PORT, async () => {
|
|
146
148
|
const { url, stop } = await startTunnel(String(PORT), {
|
|
147
|
-
// Inspector (defaults: enabled, :4040, dark theme, 100 logs)
|
|
148
149
|
inspector: true,
|
|
149
150
|
inspectorAddr: ":4040",
|
|
150
|
-
themes: "terminal", // try: "dark" | "terminal" | "light"
|
|
151
151
|
logs: 100,
|
|
152
152
|
});
|
|
153
153
|
|
|
154
|
-
// console.log("Public:", url);
|
|
155
|
-
// Open the Inspector URL from stderr in a browser (e.g. http://localhost:4040)
|
|
156
|
-
|
|
157
154
|
process.once("SIGINT", () => {
|
|
158
155
|
stop();
|
|
159
156
|
server.close(() => process.exit(0));
|
|
@@ -161,14 +158,14 @@ server.listen(PORT, async () => {
|
|
|
161
158
|
});
|
|
162
159
|
```
|
|
163
160
|
|
|
164
|
-
|
|
161
|
+
Open the **Inspector →** URL printed on startup (e.g. `http://127.0.0.1:4040`).
|
|
165
162
|
|
|
166
|
-
|
|
167
|
-
import { startTunnel } from "@dpkrn/nodetunnel";
|
|
163
|
+
### Example: tunnel only (default — same as omitting options)
|
|
168
164
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
165
|
+
`startTunnel(port)` / `startTunnel(port, {})` already keeps the inspector off. You only need `inspector: false` if you merge options from elsewhere and want to force it off:
|
|
166
|
+
|
|
167
|
+
```js
|
|
168
|
+
const { url, stop } = await startTunnel("8080"); // no inspector
|
|
172
169
|
```
|
|
173
170
|
|
|
174
171
|
---
|
|
@@ -207,27 +204,26 @@ app.listen(PORT, async () => {
|
|
|
207
204
|
| Argument | Description |
|
|
208
205
|
|----------|-------------|
|
|
209
206
|
| `port` | String, e.g. `"8080"` — must match the port your HTTP server uses. |
|
|
210
|
-
| `options` | Optional.
|
|
207
|
+
| `options` | Optional. See **Options** below. |
|
|
211
208
|
|
|
212
209
|
**Returns:** `{ url, stop }`
|
|
213
210
|
|
|
214
211
|
| Field | Description |
|
|
215
212
|
|-------|-------------|
|
|
216
213
|
| `url` | Public URL people can hit. |
|
|
217
|
-
| `stop` | Call to tear down the tunnel. |
|
|
214
|
+
| `stop` | Call to tear down the tunnel (and the inspector, if it was started). |
|
|
218
215
|
|
|
219
216
|
Errors **reject** the promise — use `try/catch`.
|
|
220
217
|
|
|
221
218
|
### Options (`startTunnel` second argument)
|
|
222
219
|
|
|
223
|
-
| Field | Type | Default |
|
|
224
|
-
|
|
225
|
-
| `host` | `string` | `'clickly.cv'` |
|
|
226
|
-
| `serverPort` | `number` | `9000` | TCP port of the tunnel server. |
|
|
227
|
-
| `inspector` | `boolean` | `
|
|
228
|
-
| `
|
|
229
|
-
| `
|
|
230
|
-
| `inspectorAddr` | `string` | `':4040'` | Listen address for the inspector (e.g. `':4040'`, `'localhost:9090'`). Display URL follows the same rules as the public banner. |
|
|
220
|
+
| Field | Type | Default | Applies when |
|
|
221
|
+
|-------|------|---------|----------------|
|
|
222
|
+
| `host` | `string` | `'clickly.cv'` | Always — tunnel control server hostname. |
|
|
223
|
+
| `serverPort` | `number` | `9000` | Always — TCP port of the tunnel server. |
|
|
224
|
+
| `inspector` | `boolean` | `false` | Always — if `false` (default), no inspector UI, no capture store, and inspector-only options below are ignored. Set `true` to enable the local inspector. |
|
|
225
|
+
| `logs` | `number` | `100` | **`inspector: true` only** — max request/response captures kept in memory. |
|
|
226
|
+
| `inspectorAddr` | `string` | `':4040'` | **`inspector: true` only** — listen address for the inspector (e.g. `':4040'`, `'localhost:9090'`). |
|
|
231
227
|
|
|
232
228
|
---
|
|
233
229
|
|
package/cmd/test-lib/main.js
CHANGED
|
@@ -4,6 +4,7 @@ import { startTunnel } from "../../pkg/tunnel/tunnel.js";
|
|
|
4
4
|
|
|
5
5
|
const app = express();
|
|
6
6
|
app.set("etag", false);
|
|
7
|
+
app.use(express.json());
|
|
7
8
|
const PORT = process.env.PORT || 8080;
|
|
8
9
|
/** Random id per process — if this doesn’t match your terminal on each restart, another process is bound to the port. */
|
|
9
10
|
const INSTANCE = Math.random().toString(36).slice(2, 10);
|
|
@@ -26,10 +27,28 @@ app.get("/", async (req, res) => {
|
|
|
26
27
|
res.send("Backend is running");
|
|
27
28
|
});
|
|
28
29
|
|
|
30
|
+
/**
|
|
31
|
+
* POST echo: path params (`:category`, `:itemId`), query string, and JSON body.
|
|
32
|
+
* Example: POST /test/widgets/42?verbose=1&tag=a with body `{"name":"x"}`
|
|
33
|
+
*/
|
|
34
|
+
app.post("/test/:category/:itemId", (req, res) => {
|
|
35
|
+
console.log("server received request");
|
|
36
|
+
console.log("req.body",req.body);
|
|
37
|
+
res.status(200).json({
|
|
38
|
+
pathParams: req.params,
|
|
39
|
+
query: req.query,
|
|
40
|
+
body: req.body,
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
|
|
29
44
|
app.listen(PORT, async () => {
|
|
30
45
|
console.log(`listening on http://localhost:${PORT}`);
|
|
31
46
|
try {
|
|
32
|
-
const { url, stop } = await startTunnel(String(PORT)
|
|
47
|
+
const { url, stop } = await startTunnel(String(PORT),{
|
|
48
|
+
inspector: true,
|
|
49
|
+
inspectorAddr: ':5040',
|
|
50
|
+
logs: 100,
|
|
51
|
+
});
|
|
33
52
|
process.once("SIGINT", () => {
|
|
34
53
|
stop();
|
|
35
54
|
process.exit(0);
|