wslog-proxy 0.0.0-stage → 0.1.0
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/LICENSE +21 -0
- package/README.md +110 -2
- package/dist/cli/args.d.ts +14 -0
- package/dist/cli/args.js +98 -0
- package/dist/cli/args.js.map +1 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +78 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/server/app.d.ts +19 -0
- package/dist/server/app.js +96 -0
- package/dist/server/app.js.map +1 -0
- package/dist/server/config.d.ts +51 -0
- package/dist/server/config.js +62 -0
- package/dist/server/config.js.map +1 -0
- package/dist/server/connections.d.ts +21 -0
- package/dist/server/connections.js +81 -0
- package/dist/server/connections.js.map +1 -0
- package/dist/server/context.d.ts +37 -0
- package/dist/server/context.js +64 -0
- package/dist/server/context.js.map +1 -0
- package/dist/server/demo/bots.d.ts +4 -0
- package/dist/server/demo/bots.js +90 -0
- package/dist/server/demo/bots.js.map +1 -0
- package/dist/server/demo/chat-server.d.ts +9 -0
- package/dist/server/demo/chat-server.js +90 -0
- package/dist/server/demo/chat-server.js.map +1 -0
- package/dist/server/demo/controller.d.ts +21 -0
- package/dist/server/demo/controller.js +37 -0
- package/dist/server/demo/controller.js.map +1 -0
- package/dist/server/events.d.ts +19 -0
- package/dist/server/events.js +19 -0
- package/dist/server/events.js.map +1 -0
- package/dist/server/http/api.d.ts +6 -0
- package/dist/server/http/api.js +89 -0
- package/dist/server/http/api.js.map +1 -0
- package/dist/server/http/auth.d.ts +26 -0
- package/dist/server/http/auth.js +78 -0
- package/dist/server/http/auth.js.map +1 -0
- package/dist/server/http/live.d.ts +26 -0
- package/dist/server/http/live.js +117 -0
- package/dist/server/http/live.js.map +1 -0
- package/dist/server/http/router.d.ts +31 -0
- package/dist/server/http/router.js +89 -0
- package/dist/server/http/router.js.map +1 -0
- package/dist/server/http/server.d.ts +17 -0
- package/dist/server/http/server.js +82 -0
- package/dist/server/http/server.js.map +1 -0
- package/dist/server/http/static.d.ts +3 -0
- package/dist/server/http/static.js +56 -0
- package/dist/server/http/static.js.map +1 -0
- package/dist/server/http/upgrade.d.ts +3 -0
- package/dist/server/http/upgrade.js +13 -0
- package/dist/server/http/upgrade.js.map +1 -0
- package/dist/server/masking.d.ts +11 -0
- package/dist/server/masking.js +104 -0
- package/dist/server/masking.js.map +1 -0
- package/dist/server/paths.d.ts +12 -0
- package/dist/server/paths.js +30 -0
- package/dist/server/paths.js.map +1 -0
- package/dist/server/plugins.d.ts +20 -0
- package/dist/server/plugins.js +4 -0
- package/dist/server/plugins.js.map +1 -0
- package/dist/server/proxy/headers.d.ts +4 -0
- package/dist/server/proxy/headers.js +39 -0
- package/dist/server/proxy/headers.js.map +1 -0
- package/dist/server/proxy/proxy.d.ts +41 -0
- package/dist/server/proxy/proxy.js +188 -0
- package/dist/server/proxy/proxy.js.map +1 -0
- package/dist/server/recorder.d.ts +34 -0
- package/dist/server/recorder.js +65 -0
- package/dist/server/recorder.js.map +1 -0
- package/dist/server/storage/index.d.ts +14 -0
- package/dist/server/storage/index.js +40 -0
- package/dist/server/storage/index.js.map +1 -0
- package/dist/server/storage/jsonl.d.ts +30 -0
- package/dist/server/storage/jsonl.js +107 -0
- package/dist/server/storage/jsonl.js.map +1 -0
- package/dist/server/storage/memory.d.ts +24 -0
- package/dist/server/storage/memory.js +74 -0
- package/dist/server/storage/memory.js.map +1 -0
- package/dist/server/storage/sqlite.d.ts +23 -0
- package/dist/server/storage/sqlite.js +125 -0
- package/dist/server/storage/sqlite.js.map +1 -0
- package/dist/server/storage/types.d.ts +30 -0
- package/dist/server/storage/types.js +2 -0
- package/dist/server/storage/types.js.map +1 -0
- package/dist/server/targets.d.ts +41 -0
- package/dist/server/targets.js +156 -0
- package/dist/server/targets.js.map +1 -0
- package/dist/server/util/logger.d.ts +16 -0
- package/dist/server/util/logger.js +19 -0
- package/dist/server/util/logger.js.map +1 -0
- package/dist/shared/filter.d.ts +14 -0
- package/dist/shared/filter.js +41 -0
- package/dist/shared/filter.js.map +1 -0
- package/dist/shared/index.d.ts +2 -0
- package/dist/shared/index.js +3 -0
- package/dist/shared/index.js.map +1 -0
- package/dist/shared/protocol.d.ts +124 -0
- package/dist/shared/protocol.js +9 -0
- package/dist/shared/protocol.js.map +1 -0
- package/dist/web/assets/index-C3OwozRd.css +1 -0
- package/dist/web/assets/index-D2oUxHhH.js +11 -0
- package/dist/web/favicon.svg +1 -0
- package/dist/web/index.html +15 -0
- package/package.json +84 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Leangdev
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,111 @@
|
|
|
1
|
-
#
|
|
1
|
+
# wslog
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/wslog-proxy) [](https://github.com/hoysengleang/wslog/actions/workflows/ci.yml) [](LICENSE)
|
|
4
|
+
|
|
5
|
+
**Point your app at wslog, open the browser, watch every WebSocket message. No code changes.**
|
|
6
|
+
|
|
7
|
+
wslog is an open-source proxy that sits between your client and your WebSocket server, records every message, and streams it live to a browser dashboard.
|
|
8
|
+
|
|
9
|
+
<!-- TODO: demo GIF + screenshot -->
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npx wslog-proxy # dashboard at http://localhost:4000
|
|
13
|
+
npx wslog-proxy --demo # same, plus a fake chat server so you can try it
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
1. Open `http://localhost:4000`.
|
|
17
|
+
2. Paste the `ws://` or `wss://` URL your app connects to.
|
|
18
|
+
3. Copy the proxy URL (e.g. `ws://127.0.0.1:4000/p/3f9a1c2b`) and use it in your app instead.
|
|
19
|
+
4. Watch messages arrive live.
|
|
20
|
+
|
|
21
|
+
Path and query are passed through: `ws://127.0.0.1:4000/p/3f9a1c2b/chat?room=1` reaches `<target>/chat?room=1`. Handshake headers (cookies, `Authorization`, `Origin`) and the negotiated subprotocol are forwarded too. Proxy URLs are derived from the target URL, and wslog remembers your targets (in `~/.config/wslog/targets.json`), so the proxy URL in your app keeps working across restarts.
|
|
22
|
+
|
|
23
|
+
## Features
|
|
24
|
+
|
|
25
|
+
| | |
|
|
26
|
+
| --- | --- |
|
|
27
|
+
| **Proxy** | Forwards text and binary frames both ways; mirrors close codes; passes handshake rejections (401, 404, …) through |
|
|
28
|
+
| **Setup in the browser** | Paste a target URL, get a proxy URL. No flags to learn |
|
|
29
|
+
| **Multiple targets** | Proxy several servers at once, each with its own proxy URL; click one to see only its traffic |
|
|
30
|
+
| **Live traffic table** | Time, direction, size, connection, preview. Virtualized, stays fast with tens of thousands of rows |
|
|
31
|
+
| **Detail pane** | Collapsible JSON tree, raw view, hex/base64 for binary, copy |
|
|
32
|
+
| **Connections sidebar** | Status, close code, in/out counts, 30-second message-rate sparkline |
|
|
33
|
+
| **Controls** | Pause/resume, search, filter by direction, connection, or target, clear, export JSON |
|
|
34
|
+
| **Share link** | Read-only live view protected by a token |
|
|
35
|
+
| **Storage** | In memory (default), JSONL file, or SQLite, with a size limit |
|
|
36
|
+
| **Demo mode** | `--demo` starts a fake chat server with simulated users |
|
|
37
|
+
| **Safe defaults** | Binds to localhost; masks configured fields before anything is stored |
|
|
38
|
+
|
|
39
|
+
## Install
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
npx wslog-proxy # no install
|
|
43
|
+
npm i -g wslog-proxy && wslog # global; the command is `wslog`
|
|
44
|
+
docker run -p 4000:4000 ghcr.io/hoysengleang/wslog # Docker
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Requires Node.js 22 or newer.
|
|
48
|
+
|
|
49
|
+
In Docker, `localhost` means the container. Use `ws://host.docker.internal:8080` to reach a server on your machine; on Linux, also pass `--add-host=host.docker.internal:host-gateway` to `docker run`. The container listens on `0.0.0.0`, so open the **admin link** printed in the logs, or set a known token with `-e WSLOG_ADMIN_TOKEN=...`. The printed links assume `-p 4000:4000`; if you map another port, set `-e WSLOG_PUBLIC_URL=http://localhost:<port>`. To remember targets across container restarts, add `-v wslog:/home/node/.config/wslog`.
|
|
50
|
+
|
|
51
|
+
## Options
|
|
52
|
+
|
|
53
|
+
| Flag | Env | Default | Purpose |
|
|
54
|
+
| --- | --- | --- | --- |
|
|
55
|
+
| `[target-url...]` | `WSLOG_TARGET` | – | Register targets on startup. Pass several, or separate them with spaces or commas in the env var |
|
|
56
|
+
| `-p, --port` | `WSLOG_PORT` | `4000` | Dashboard and proxy port |
|
|
57
|
+
| `-H, --host` | `WSLOG_HOST` | `127.0.0.1` | Bind address. Use `0.0.0.0` only on a trusted network |
|
|
58
|
+
| `--public-url` | `WSLOG_PUBLIC_URL` | from host and port | Address people use to reach wslog, for printed and share links, e.g. `http://localhost:8080` when Docker maps port 8080 |
|
|
59
|
+
| `-m, --mask` | `WSLOG_MASK` | none | Field names to hide, e.g. `token,password` |
|
|
60
|
+
| `--max-messages` | `WSLOG_MAX_MESSAGES` | `10000` | Storage limit; oldest dropped first |
|
|
61
|
+
| `--max-payload` | – | `1048576` | Bytes kept per message; longer payloads are truncated (still forwarded in full) |
|
|
62
|
+
| `--store` | `WSLOG_STORE` | `memory` | `memory`, `./traffic.jsonl`, or `./traffic.db` (SQLite, needs Node.js 22.13+) |
|
|
63
|
+
| `--share-token` | `WSLOG_SHARE_TOKEN` | random | Token for the read-only share link |
|
|
64
|
+
| `--admin-token` | `WSLOG_ADMIN_TOKEN` | random | Token for full access from other machines |
|
|
65
|
+
| – | `WSLOG_TARGETS_FILE` | `~/.config/wslog/targets.json` | Where targets are remembered between runs. Set it empty to keep them in memory only |
|
|
66
|
+
| `--demo` | – | off | Start a fake chat server (also masks `token`, `password`) |
|
|
67
|
+
| `-o, --open` | – | off | Open the dashboard in your browser |
|
|
68
|
+
| `--verbose` / `-q, --quiet` | – | – | Debug logging / errors only |
|
|
69
|
+
|
|
70
|
+
Memory: the `memory` and `.jsonl` stores keep messages in RAM, up to `--max-messages` × `--max-payload` bytes in the worst case (about 10 GB with the defaults if every message were 1 MB). For heavy traffic, lower `--max-payload` or use a `.db` (SQLite) store.
|
|
71
|
+
|
|
72
|
+
## Security
|
|
73
|
+
|
|
74
|
+
WebSocket traffic often carries tokens and personal data, so wslog is safe by default:
|
|
75
|
+
|
|
76
|
+
- It listens on `127.0.0.1` only. Requests from this machine get full access; anything else needs a token.
|
|
77
|
+
- **Masking happens before storage.** Masked values never reach disk, the dashboard, or exports. JSON is masked at any depth, including JSON encoded inside strings; other text by `key=value` / `"key": "value"` patterns (escaped or not), and URLs by query parameter, including target URLs. Binary frames that contain UTF-8 text are masked the same way. Passwords in URLs (`ws://user:pass@host`) are always hidden.
|
|
78
|
+
- Handshake headers (cookies, `Authorization`) are forwarded to your server but never stored or shown. Only the `User-Agent` is recorded.
|
|
79
|
+
- Saved traffic (`.jsonl`, `.db`) and the targets file are readable only by your user. Fields you did not mask are stored as sent, so treat these files like logs that contain secrets.
|
|
80
|
+
- Share links are read-only and need the share token.
|
|
81
|
+
- When wslog listens beyond localhost (including in Docker), it prints the admin link in its logs. Anyone who can read those logs gets full access, so keep them private or set your own `--admin-token`.
|
|
82
|
+
- The dashboard API rejects cross-origin requests, including `Origin: null` (sandboxed iframes, local HTML files). Access without a token needs a local `Host` name, so DNS rebinding cannot reach it. Other sites cannot frame the dashboard.
|
|
83
|
+
- **v0.1 has no user accounts. Do not expose wslog to the internet.**
|
|
84
|
+
|
|
85
|
+
Found a vulnerability? Please report it privately; see [SECURITY.md](SECURITY.md).
|
|
86
|
+
|
|
87
|
+
## How it compares
|
|
88
|
+
|
|
89
|
+
| | wslog | Browser DevTools | wscat / websocat | mitmproxy |
|
|
90
|
+
| --- | --- | --- | --- | --- |
|
|
91
|
+
| Works for non-browser clients (Node, mobile, IoT) | ✅ | ❌ | ✅ (you are the client) | ✅ |
|
|
92
|
+
| Live UI built for WebSocket traffic | ✅ | ✅ | ❌ | partial |
|
|
93
|
+
| Search, filter, export across connections | ✅ | limited | ❌ | ✅ |
|
|
94
|
+
| Share a live view with a teammate | ✅ | ❌ | ❌ | ❌ |
|
|
95
|
+
| Setup | one command, no certs | none | none | proxy config + CA cert for TLS |
|
|
96
|
+
|
|
97
|
+
## Development
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
npm install
|
|
101
|
+
npm run dev # server on :4000 (with --demo) + Vite dashboard on :5173
|
|
102
|
+
npm run check # typecheck + lint + unit tests
|
|
103
|
+
npm run test:e2e # real browser against a real wslog (first time: npx playwright install chromium)
|
|
104
|
+
npm run build # dist/ (server) + dist/web (dashboard)
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
See [docs/architecture.md](docs/architecture.md) for how the code is organized and how to extend it, and [CONTRIBUTING.md](CONTRIBUTING.md) to get started.
|
|
108
|
+
|
|
109
|
+
## License
|
|
110
|
+
|
|
111
|
+
[MIT](LICENSE)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type WslogConfig } from '../server/config.js';
|
|
2
|
+
export declare const HELP: string;
|
|
3
|
+
export interface CliOptions {
|
|
4
|
+
config: Partial<WslogConfig>;
|
|
5
|
+
open: boolean;
|
|
6
|
+
help: boolean;
|
|
7
|
+
version: boolean;
|
|
8
|
+
}
|
|
9
|
+
export declare function parseCliArgs(argv: string[], env?: NodeJS.ProcessEnv): CliOptions;
|
|
10
|
+
/**
|
|
11
|
+
* `WSLOG_TARGET` may list several URLs, separated by spaces or commas. A comma
|
|
12
|
+
* only splits when a new `scheme://` follows, so `ws://host/?topics=a,b` stays whole.
|
|
13
|
+
*/
|
|
14
|
+
export declare function splitTargets(value: string): string[];
|
package/dist/cli/args.js
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { parseArgs } from 'node:util';
|
|
2
|
+
import { DEFAULT_CONFIG } from '../server/config.js';
|
|
3
|
+
import { defaultTargetsFile } from '../server/paths.js';
|
|
4
|
+
export const HELP = `
|
|
5
|
+
wslog — record WebSocket traffic and watch it live in your browser
|
|
6
|
+
|
|
7
|
+
Usage
|
|
8
|
+
wslog [target-url...] [options]
|
|
9
|
+
|
|
10
|
+
Examples
|
|
11
|
+
wslog start the dashboard at http://localhost:4000
|
|
12
|
+
wslog --demo start with a fake chat server to try it
|
|
13
|
+
wslog ws://localhost:8080 register a target from the command line
|
|
14
|
+
wslog ws://localhost:8080 ws://localhost:9000/socket
|
|
15
|
+
proxy several servers at once
|
|
16
|
+
wslog --mask token,password --store ./traffic.jsonl
|
|
17
|
+
|
|
18
|
+
Options
|
|
19
|
+
-p, --port <n> dashboard and proxy port (default ${DEFAULT_CONFIG.port})
|
|
20
|
+
-H, --host <addr> bind address (default ${DEFAULT_CONFIG.host}); use 0.0.0.0 only on a trusted network
|
|
21
|
+
--public-url <url> address people use to reach wslog, for printed and share links
|
|
22
|
+
(e.g. http://localhost:8080 when Docker maps port 8080)
|
|
23
|
+
-m, --mask <fields> comma-separated field names to hide, e.g. token,password (repeatable)
|
|
24
|
+
--max-messages <n> keep at most n messages, oldest dropped first (default ${DEFAULT_CONFIG.maxMessages})
|
|
25
|
+
--max-payload <n> keep at most n bytes per message (default ${DEFAULT_CONFIG.maxPayloadBytes})
|
|
26
|
+
--store <spec> memory (default), ./file.jsonl, or ./file.db (SQLite, Node 22.13+)
|
|
27
|
+
--share-token <tok> token for the read-only share link (default: random)
|
|
28
|
+
--admin-token <tok> token for full access from other machines (default: random)
|
|
29
|
+
--demo start a fake chat server with simulated users
|
|
30
|
+
-o, --open open the dashboard in your browser
|
|
31
|
+
--verbose debug logging
|
|
32
|
+
-q, --quiet only log errors
|
|
33
|
+
-v, --version print version
|
|
34
|
+
-h, --help show this help
|
|
35
|
+
|
|
36
|
+
Environment (handy for Docker; flags win)
|
|
37
|
+
WSLOG_PORT, WSLOG_HOST, WSLOG_PUBLIC_URL, WSLOG_MASK, WSLOG_STORE,
|
|
38
|
+
WSLOG_MAX_MESSAGES, WSLOG_SHARE_TOKEN, WSLOG_ADMIN_TOKEN, WSLOG_TARGET
|
|
39
|
+
WSLOG_TARGETS_FILE where targets are remembered between runs
|
|
40
|
+
(default ~/.config/wslog/targets.json; empty = don't remember)
|
|
41
|
+
`;
|
|
42
|
+
export function parseCliArgs(argv, env = process.env) {
|
|
43
|
+
const { values, positionals } = parseArgs({
|
|
44
|
+
args: argv,
|
|
45
|
+
allowPositionals: true,
|
|
46
|
+
strict: true,
|
|
47
|
+
options: {
|
|
48
|
+
port: { type: 'string', short: 'p' },
|
|
49
|
+
host: { type: 'string', short: 'H' },
|
|
50
|
+
'public-url': { type: 'string' },
|
|
51
|
+
mask: { type: 'string', short: 'm', multiple: true },
|
|
52
|
+
'max-messages': { type: 'string' },
|
|
53
|
+
'max-payload': { type: 'string' },
|
|
54
|
+
store: { type: 'string' },
|
|
55
|
+
'share-token': { type: 'string' },
|
|
56
|
+
'admin-token': { type: 'string' },
|
|
57
|
+
demo: { type: 'boolean' },
|
|
58
|
+
open: { type: 'boolean', short: 'o' },
|
|
59
|
+
verbose: { type: 'boolean' },
|
|
60
|
+
quiet: { type: 'boolean', short: 'q' },
|
|
61
|
+
version: { type: 'boolean', short: 'v' },
|
|
62
|
+
help: { type: 'boolean', short: 'h' },
|
|
63
|
+
},
|
|
64
|
+
});
|
|
65
|
+
const mask = values.mask ?? (env.WSLOG_MASK ? [env.WSLOG_MASK] : undefined);
|
|
66
|
+
const config = {
|
|
67
|
+
port: toInt(values.port ?? env.WSLOG_PORT, '--port'),
|
|
68
|
+
host: values.host ?? env.WSLOG_HOST,
|
|
69
|
+
publicUrl: values['public-url'] ?? env.WSLOG_PUBLIC_URL,
|
|
70
|
+
mask: mask?.flatMap((m) => m.split(',')),
|
|
71
|
+
maxMessages: toInt(values['max-messages'] ?? env.WSLOG_MAX_MESSAGES, '--max-messages'),
|
|
72
|
+
maxPayloadBytes: toInt(values['max-payload'], '--max-payload'),
|
|
73
|
+
store: values.store ?? env.WSLOG_STORE,
|
|
74
|
+
shareToken: values['share-token'] ?? env.WSLOG_SHARE_TOKEN,
|
|
75
|
+
adminToken: values['admin-token'] ?? env.WSLOG_ADMIN_TOKEN,
|
|
76
|
+
targets: positionals.length ? positionals : splitTargets(env.WSLOG_TARGET ?? ''),
|
|
77
|
+
targetsFile: env.WSLOG_TARGETS_FILE ?? defaultTargetsFile(env),
|
|
78
|
+
demo: values.demo,
|
|
79
|
+
logLevel: values.quiet ? 'error' : values.verbose ? 'debug' : undefined,
|
|
80
|
+
};
|
|
81
|
+
return { config, open: Boolean(values.open), help: Boolean(values.help), version: Boolean(values.version) };
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* `WSLOG_TARGET` may list several URLs, separated by spaces or commas. A comma
|
|
85
|
+
* only splits when a new `scheme://` follows, so `ws://host/?topics=a,b` stays whole.
|
|
86
|
+
*/
|
|
87
|
+
export function splitTargets(value) {
|
|
88
|
+
return value.split(/\s+|,(?=\s*[a-z][a-z0-9+.-]*:\/\/)/i).filter(Boolean);
|
|
89
|
+
}
|
|
90
|
+
function toInt(value, flag) {
|
|
91
|
+
if (value === undefined)
|
|
92
|
+
return undefined;
|
|
93
|
+
const n = Number(value);
|
|
94
|
+
if (!Number.isInteger(n))
|
|
95
|
+
throw new Error(`${flag} expects a whole number, got "${value}"`);
|
|
96
|
+
return n;
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=args.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"args.js","sourceRoot":"","sources":["../../src/cli/args.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { spawn } from 'node:child_process';
|
|
3
|
+
import { createWslog } from '../server/app.js';
|
|
4
|
+
import { isLoopbackHost } from '../server/config.js';
|
|
5
|
+
import { readVersion } from '../server/paths.js';
|
|
6
|
+
import { color } from '../server/util/logger.js';
|
|
7
|
+
import { HELP, parseCliArgs } from './args.js';
|
|
8
|
+
async function main() {
|
|
9
|
+
let options;
|
|
10
|
+
try {
|
|
11
|
+
options = parseCliArgs(process.argv.slice(2));
|
|
12
|
+
}
|
|
13
|
+
catch (err) {
|
|
14
|
+
console.error(color.red(`✖ ${err.message}`));
|
|
15
|
+
console.error('Run `wslog --help` for usage.');
|
|
16
|
+
process.exit(2);
|
|
17
|
+
}
|
|
18
|
+
if (options.help)
|
|
19
|
+
return void console.log(HELP);
|
|
20
|
+
if (options.version)
|
|
21
|
+
return void console.log(readVersion());
|
|
22
|
+
const wslog = await createWslog(options.config);
|
|
23
|
+
const { config, targets, store, masker, logger } = wslog.ctx;
|
|
24
|
+
try {
|
|
25
|
+
await wslog.listen();
|
|
26
|
+
}
|
|
27
|
+
catch (err) {
|
|
28
|
+
const e = err;
|
|
29
|
+
if (e.code === 'EADDRINUSE')
|
|
30
|
+
throw new Error(`Port ${config.port} is already in use. Try --port ${config.port + 1}.`, { cause: err });
|
|
31
|
+
throw err;
|
|
32
|
+
}
|
|
33
|
+
const url = wslog.dashboardUrl();
|
|
34
|
+
const { protocol, host, hostname } = new URL(url);
|
|
35
|
+
const proxyBase = config.publicUrl
|
|
36
|
+
? `${protocol === 'https:' ? 'wss' : 'ws'}://${host}`
|
|
37
|
+
: `ws://${isLoopbackHost(config.host) ? '127.0.0.1' : hostname}:${wslog.ctx.port()}`;
|
|
38
|
+
logger.info('');
|
|
39
|
+
logger.info(` ${color.bold('wslog')} ${color.dim(`v${wslog.ctx.version}`)}`);
|
|
40
|
+
logger.info('');
|
|
41
|
+
logger.info(` ${color.green('➜')} Dashboard ${color.cyan(url)}`);
|
|
42
|
+
for (const t of targets.list()) {
|
|
43
|
+
logger.info(` ${color.green('➜')} Proxy ${color.cyan(proxyBase + t.proxyPath)} ${color.dim(`→ ${masker.url(t.url)}`)}`);
|
|
44
|
+
}
|
|
45
|
+
logger.info(` Storage ${color.dim(store.kind)}${masker.fields.length ? color.dim(` · masking ${masker.fields.join(', ')}`) : ''}`);
|
|
46
|
+
if (!isLoopbackHost(config.host)) {
|
|
47
|
+
logger.info('');
|
|
48
|
+
logger.info(` ${color.yellow('!')} Listening on ${config.host}. Anyone on this network can reach the proxy.`);
|
|
49
|
+
logger.info(` Admin link (keep private): ${color.cyan(`${url}/?token=${config.adminToken}`)}`);
|
|
50
|
+
logger.info(` Read-only share link: ${color.cyan(wslog.shareUrl())}`);
|
|
51
|
+
}
|
|
52
|
+
logger.info('');
|
|
53
|
+
if (!targets.list().length)
|
|
54
|
+
logger.info(color.dim(' Open the dashboard and paste your WebSocket server URL to get started.\n'));
|
|
55
|
+
if (options.open)
|
|
56
|
+
openBrowser(url);
|
|
57
|
+
let closing = false;
|
|
58
|
+
const shutdown = async () => {
|
|
59
|
+
if (closing)
|
|
60
|
+
process.exit(1);
|
|
61
|
+
closing = true;
|
|
62
|
+
logger.info(color.dim('\n shutting down…'));
|
|
63
|
+
await wslog.close();
|
|
64
|
+
process.exit(0);
|
|
65
|
+
};
|
|
66
|
+
process.on('SIGINT', shutdown);
|
|
67
|
+
process.on('SIGTERM', shutdown);
|
|
68
|
+
}
|
|
69
|
+
function openBrowser(url) {
|
|
70
|
+
const cmd = process.platform === 'darwin' ? 'open' : process.platform === 'win32' ? 'cmd' : 'xdg-open';
|
|
71
|
+
const args = process.platform === 'win32' ? ['/c', 'start', '', url] : [url];
|
|
72
|
+
spawn(cmd, args, { stdio: 'ignore', detached: true }).on('error', () => { }).unref();
|
|
73
|
+
}
|
|
74
|
+
main().catch((err) => {
|
|
75
|
+
console.error(color.red(`✖ ${err instanceof Error ? err.message : String(err)}`));
|
|
76
|
+
process.exit(1);
|
|
77
|
+
});
|
|
78
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Programmatic API. Most people use the `wslog` CLI; import this to embed
|
|
3
|
+
* wslog in tests or tools, or to extend it with plugins and storage backends.
|
|
4
|
+
*/
|
|
5
|
+
export { createWslog, type CreateWslogOptions, type Wslog } from './server/app.js';
|
|
6
|
+
export { DEFAULT_CONFIG, type WslogConfig } from './server/config.js';
|
|
7
|
+
export type { WslogContext } from './server/context.js';
|
|
8
|
+
export type { WslogEvents } from './server/events.js';
|
|
9
|
+
export { definePlugin, type PluginApi, type WslogPlugin } from './server/plugins.js';
|
|
10
|
+
export type { DraftRecord, MessageTransform } from './server/recorder.js';
|
|
11
|
+
export { registerStore, type MessageQuery, type MessageStore, type StoreOptions } from './server/storage/index.js';
|
|
12
|
+
export * from './shared/index.js';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Programmatic API. Most people use the `wslog` CLI; import this to embed
|
|
3
|
+
* wslog in tests or tools, or to extend it with plugins and storage backends.
|
|
4
|
+
*/
|
|
5
|
+
export { createWslog } from './server/app.js';
|
|
6
|
+
export { DEFAULT_CONFIG } from './server/config.js';
|
|
7
|
+
export { definePlugin } from './server/plugins.js';
|
|
8
|
+
export { registerStore } from './server/storage/index.js';
|
|
9
|
+
export * from './shared/index.js';
|
|
10
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,WAAW,EAAuC,MAAM,iBAAiB,CAAC;AACnF,OAAO,EAAE,cAAc,EAAoB,MAAM,oBAAoB,CAAC;AAGtE,OAAO,EAAE,YAAY,EAAoC,MAAM,qBAAqB,CAAC;AAErF,OAAO,EAAE,aAAa,EAA2D,MAAM,2BAA2B,CAAC;AACnH,cAAc,mBAAmB,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type WslogConfig } from './config.js';
|
|
2
|
+
import { type WslogContext } from './context.js';
|
|
3
|
+
import type { WslogPlugin } from './plugins.js';
|
|
4
|
+
export interface CreateWslogOptions extends Partial<WslogConfig> {
|
|
5
|
+
plugins?: WslogPlugin[];
|
|
6
|
+
}
|
|
7
|
+
export interface Wslog {
|
|
8
|
+
readonly ctx: WslogContext;
|
|
9
|
+
/** Start listening. Resolves once the dashboard URL is reachable. */
|
|
10
|
+
listen(): Promise<{
|
|
11
|
+
port: number;
|
|
12
|
+
url: string;
|
|
13
|
+
}>;
|
|
14
|
+
close(): Promise<void>;
|
|
15
|
+
dashboardUrl(): string;
|
|
16
|
+
shareUrl(): string;
|
|
17
|
+
}
|
|
18
|
+
/** Composition root: wires every subsystem together. */
|
|
19
|
+
export declare function createWslog(options?: CreateWslogOptions): Promise<Wslog>;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { resolve } from 'node:path';
|
|
2
|
+
import { isWildcardHost, resolveConfig } from './config.js';
|
|
3
|
+
import { ConnectionRegistry } from './connections.js';
|
|
4
|
+
import { dashboardUrl, shareUrl } from './context.js';
|
|
5
|
+
import { DemoController } from './demo/controller.js';
|
|
6
|
+
import { EventBus } from './events.js';
|
|
7
|
+
import { registerApiRoutes } from './http/api.js';
|
|
8
|
+
import { LiveHub } from './http/live.js';
|
|
9
|
+
import { Router } from './http/router.js';
|
|
10
|
+
import { createHttpServer } from './http/server.js';
|
|
11
|
+
import { createMasker } from './masking.js';
|
|
12
|
+
import { defaultWebRoot, readVersion } from './paths.js';
|
|
13
|
+
import { WebSocketProxy } from './proxy/proxy.js';
|
|
14
|
+
import { maskingTransform, Recorder } from './recorder.js';
|
|
15
|
+
import { createStore } from './storage/index.js';
|
|
16
|
+
import { persistTargets, TargetRegistry } from './targets.js';
|
|
17
|
+
import { createLogger } from './util/logger.js';
|
|
18
|
+
/** Composition root: wires every subsystem together. */
|
|
19
|
+
export async function createWslog(options = {}) {
|
|
20
|
+
const { plugins = [], ...configInput } = options;
|
|
21
|
+
const config = resolveConfig(configInput);
|
|
22
|
+
const logger = createLogger(config.logLevel);
|
|
23
|
+
const bus = new EventBus();
|
|
24
|
+
const store = await createStore(config.store, { maxMessages: config.maxMessages });
|
|
25
|
+
const masker = createMasker(config.mask);
|
|
26
|
+
const targets = new TargetRegistry(bus);
|
|
27
|
+
const connections = new ConnectionRegistry(bus);
|
|
28
|
+
const recorder = new Recorder({ store, bus, connections, maxPayloadBytes: config.maxPayloadBytes });
|
|
29
|
+
recorder.use(maskingTransform(masker));
|
|
30
|
+
const router = new Router();
|
|
31
|
+
// `httpServer` is created below; this is only called once it exists.
|
|
32
|
+
const port = () => httpServer.address()?.port ?? config.port;
|
|
33
|
+
const demo = new DemoController({
|
|
34
|
+
targets,
|
|
35
|
+
logger,
|
|
36
|
+
localProxyBase: () => {
|
|
37
|
+
const host = isWildcardHost(config.host) ? '127.0.0.1' : config.host;
|
|
38
|
+
return `ws://${host.includes(':') ? `[${host}]` : host}:${port()}`;
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
const ctx = {
|
|
42
|
+
config,
|
|
43
|
+
version: readVersion(),
|
|
44
|
+
logger,
|
|
45
|
+
bus,
|
|
46
|
+
store,
|
|
47
|
+
targets,
|
|
48
|
+
connections,
|
|
49
|
+
recorder,
|
|
50
|
+
masker,
|
|
51
|
+
router,
|
|
52
|
+
demo,
|
|
53
|
+
port,
|
|
54
|
+
};
|
|
55
|
+
for (const plugin of plugins) {
|
|
56
|
+
await plugin.setup(ctx, { transform: (fn) => recorder.use(fn) });
|
|
57
|
+
logger.debug(`plugin loaded: ${plugin.name}`);
|
|
58
|
+
}
|
|
59
|
+
registerApiRoutes(router, ctx);
|
|
60
|
+
if (config.targetsFile)
|
|
61
|
+
persistTargets(targets, bus, config.targetsFile, logger);
|
|
62
|
+
for (const url of config.targets)
|
|
63
|
+
targets.add(url);
|
|
64
|
+
const proxy = new WebSocketProxy({ connections, recorder, masker, logger });
|
|
65
|
+
bus.on('targets', (list) => proxy.closeRemovedTargets(list));
|
|
66
|
+
const live = new LiveHub(ctx);
|
|
67
|
+
const httpServer = createHttpServer({ ctx, proxy, live, webRoot: resolve(config.webRoot ?? defaultWebRoot) });
|
|
68
|
+
return {
|
|
69
|
+
ctx,
|
|
70
|
+
async listen() {
|
|
71
|
+
await new Promise((resolveListen, reject) => {
|
|
72
|
+
httpServer.once('error', reject);
|
|
73
|
+
httpServer.listen(config.port, config.host, () => {
|
|
74
|
+
httpServer.off('error', reject);
|
|
75
|
+
resolveListen();
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
if (config.demo)
|
|
79
|
+
await demo.start();
|
|
80
|
+
return { port: port(), url: dashboardUrl(ctx) };
|
|
81
|
+
},
|
|
82
|
+
async close() {
|
|
83
|
+
await demo.stop();
|
|
84
|
+
live.close();
|
|
85
|
+
proxy.closeAll();
|
|
86
|
+
httpServer.closeAllConnections();
|
|
87
|
+
await new Promise((done) => httpServer.close(() => done()));
|
|
88
|
+
for (const plugin of [...plugins].reverse())
|
|
89
|
+
await plugin.teardown?.();
|
|
90
|
+
await store.close();
|
|
91
|
+
},
|
|
92
|
+
dashboardUrl: () => dashboardUrl(ctx),
|
|
93
|
+
shareUrl: () => shareUrl(ctx),
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=app.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app.js","sourceRoot":"","sources":["../../src/server/app.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,cAAc,EAAE,aAAa,EAAoB,MAAM,aAAa,CAAC;AAC9E,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAqB,MAAM,cAAc,CAAC;AACzE,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AACzC,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEzD,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,gBAAgB,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAC3D,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9D,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAehD,wDAAwD;AACxD,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,UAA8B,EAAE;IAChE,MAAM,EAAE,OAAO,GAAG,EAAE,EAAE,GAAG,WAAW,EAAE,GAAG,OAAO,CAAC;IACjD,MAAM,MAAM,GAAG,aAAa,CAAC,WAAW,CAAC,CAAC;IAC1C,MAAM,MAAM,GAAG,YAAY,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAC7C,MAAM,GAAG,GAAG,IAAI,QAAQ,EAAE,CAAC;IAC3B,MAAM,KAAK,GAAG,MAAM,WAAW,CAAC,MAAM,CAAC,KAAK,EAAE,EAAE,WAAW,EAAE,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC;IACnF,MAAM,MAAM,GAAG,YAAY,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACzC,MAAM,OAAO,GAAG,IAAI,cAAc,CAAC,GAAG,CAAC,CAAC;IACxC,MAAM,WAAW,GAAG,IAAI,kBAAkB,CAAC,GAAG,CAAC,CAAC;IAChD,MAAM,QAAQ,GAAG,IAAI,QAAQ,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,CAAC,eAAe,EAAE,CAAC,CAAC;IACpG,QAAQ,CAAC,GAAG,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC,CAAC;IACvC,MAAM,MAAM,GAAG,IAAI,MAAM,EAAE,CAAC;IAE5B,qEAAqE;IACrE,MAAM,IAAI,GAAG,GAAG,EAAE,CAAE,UAAU,CAAC,OAAO,EAAyB,EAAE,IAAI,IAAI,MAAM,CAAC,IAAI,CAAC;IAErF,MAAM,IAAI,GAAG,IAAI,cAAc,CAAC;QAC9B,OAAO;QACP,MAAM;QACN,cAAc,EAAE,GAAG,EAAE;YACnB,MAAM,IAAI,GAAG,cAAc,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC;YACrE,OAAO,QAAQ,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,GAAG,CAAC,CAAC,CAAC,IAAI,IAAI,IAAI,EAAE,EAAE,CAAC;QACrE,CAAC;KACF,CAAC,CAAC;IAEH,MAAM,GAAG,GAAiB;QACxB,MAAM;QACN,OAAO,EAAE,WAAW,EAAE;QACtB,MAAM;QACN,GAAG;QACH,KAAK;QACL,OAAO;QACP,WAAW;QACX,QAAQ;QACR,MAAM;QACN,MAAM;QACN,IAAI;QACJ,IAAI;KACL,CAAC;IAEF,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,MAAM,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;QACjE,MAAM,CAAC,KAAK,CAAC,kBAAkB,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;IAChD,CAAC;IACD,iBAAiB,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAE/B,IAAI,MAAM,CAAC,WAAW;QAAE,cAAc,CAAC,OAAO,EAAE,GAAG,EAAE,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;IACjF,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,OAAO;QAAE,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAEnD,MAAM,KAAK,GAAG,IAAI,cAAc,CAAC,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IAC5E,GAAG,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,mBAAmB,CAAC,IAAI,CAAC,CAAC,CAAC;IAC7D,MAAM,IAAI,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC,CAAC;IAC9B,MAAM,UAAU,GAAG,gBAAgB,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC,OAAO,IAAI,cAAc,CAAC,EAAE,CAAC,CAAC;IAE9G,OAAO;QACL,GAAG;QACH,KAAK,CAAC,MAAM;YACV,MAAM,IAAI,OAAO,CAAO,CAAC,aAAa,EAAE,MAAM,EAAE,EAAE;gBAChD,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;gBACjC,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,GAAG,EAAE;oBAC/C,UAAU,CAAC,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;oBAChC,aAAa,EAAE,CAAC;gBAClB,CAAC,CAAC,CAAC;YACL,CAAC,CAAC,CAAC;YACH,IAAI,MAAM,CAAC,IAAI;gBAAE,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC;YACpC,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,GAAG,EAAE,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC;QAClD,CAAC;QACD,KAAK,CAAC,KAAK;YACT,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;YAClB,IAAI,CAAC,KAAK,EAAE,CAAC;YACb,KAAK,CAAC,QAAQ,EAAE,CAAC;YACjB,UAAU,CAAC,mBAAmB,EAAE,CAAC;YACjC,MAAM,IAAI,OAAO,CAAO,CAAC,IAAI,EAAE,EAAE,CAAC,UAAU,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;YAClE,KAAK,MAAM,MAAM,IAAI,CAAC,GAAG,OAAO,CAAC,CAAC,OAAO,EAAE;gBAAE,MAAM,MAAM,CAAC,QAAQ,EAAE,EAAE,CAAC;YACvE,MAAM,KAAK,CAAC,KAAK,EAAE,CAAC;QACtB,CAAC;QACD,YAAY,EAAE,GAAG,EAAE,CAAC,YAAY,CAAC,GAAG,CAAC;QACrC,QAAQ,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC;KAC9B,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
export interface WslogConfig {
|
|
2
|
+
/** Dashboard + proxy port. `0` picks a free port. */
|
|
3
|
+
port: number;
|
|
4
|
+
/** Bind address. Loopback by default; `0.0.0.0` only on trusted networks. */
|
|
5
|
+
host: string;
|
|
6
|
+
/**
|
|
7
|
+
* Address people use to reach wslog, for the links it prints and the share
|
|
8
|
+
* link, e.g. `http://localhost:8080` in Docker. Default: derived from host and port.
|
|
9
|
+
*/
|
|
10
|
+
publicUrl?: string;
|
|
11
|
+
/** Field names to mask before anything is stored or shown. */
|
|
12
|
+
mask: string[];
|
|
13
|
+
/** Storage limit; oldest messages are dropped first. */
|
|
14
|
+
maxMessages: number;
|
|
15
|
+
/** Max bytes of payload kept per message; longer payloads are truncated. */
|
|
16
|
+
maxPayloadBytes: number;
|
|
17
|
+
/** `memory`, a `.jsonl` file path, or a `.db` / `.sqlite` file path. */
|
|
18
|
+
store: string;
|
|
19
|
+
/** Token for the read-only share link. */
|
|
20
|
+
shareToken: string;
|
|
21
|
+
/** Token for full access from non-loopback addresses. */
|
|
22
|
+
adminToken: string;
|
|
23
|
+
/** Target URLs to register on startup. */
|
|
24
|
+
targets: string[];
|
|
25
|
+
/**
|
|
26
|
+
* JSON file that remembers targets across restarts, so proxy URLs in your app
|
|
27
|
+
* keep working. The CLI sets one; unset keeps targets in memory only.
|
|
28
|
+
*/
|
|
29
|
+
targetsFile?: string;
|
|
30
|
+
demo: boolean;
|
|
31
|
+
/** Path to the built dashboard. Defaults to `dist/web` inside the package. */
|
|
32
|
+
webRoot?: string;
|
|
33
|
+
logLevel: 'silent' | 'error' | 'info' | 'debug';
|
|
34
|
+
}
|
|
35
|
+
export declare const DEFAULT_CONFIG: {
|
|
36
|
+
port: number;
|
|
37
|
+
host: string;
|
|
38
|
+
mask: never[];
|
|
39
|
+
maxMessages: number;
|
|
40
|
+
maxPayloadBytes: number;
|
|
41
|
+
store: string;
|
|
42
|
+
targets: never[];
|
|
43
|
+
demo: false;
|
|
44
|
+
logLevel: "info";
|
|
45
|
+
};
|
|
46
|
+
/** Fields masked automatically in demo mode so the feature is visible. */
|
|
47
|
+
export declare const DEMO_MASK_FIELDS: string[];
|
|
48
|
+
export declare function generateToken(): string;
|
|
49
|
+
export declare function resolveConfig(input?: Partial<WslogConfig>): WslogConfig;
|
|
50
|
+
export declare function isLoopbackHost(host: string): boolean;
|
|
51
|
+
export declare function isWildcardHost(host: string): boolean;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { randomBytes } from 'node:crypto';
|
|
2
|
+
export const DEFAULT_CONFIG = {
|
|
3
|
+
port: 4000,
|
|
4
|
+
host: '127.0.0.1',
|
|
5
|
+
mask: [],
|
|
6
|
+
maxMessages: 10_000,
|
|
7
|
+
maxPayloadBytes: 1024 * 1024,
|
|
8
|
+
store: 'memory',
|
|
9
|
+
targets: [],
|
|
10
|
+
demo: false,
|
|
11
|
+
logLevel: 'info',
|
|
12
|
+
};
|
|
13
|
+
/** Fields masked automatically in demo mode so the feature is visible. */
|
|
14
|
+
export const DEMO_MASK_FIELDS = ['token', 'password'];
|
|
15
|
+
export function generateToken() {
|
|
16
|
+
return randomBytes(18).toString('base64url');
|
|
17
|
+
}
|
|
18
|
+
export function resolveConfig(input = {}) {
|
|
19
|
+
const config = {
|
|
20
|
+
...DEFAULT_CONFIG,
|
|
21
|
+
shareToken: generateToken(),
|
|
22
|
+
adminToken: generateToken(),
|
|
23
|
+
...stripUndefined(input),
|
|
24
|
+
};
|
|
25
|
+
config.mask = [...new Set(config.mask.map((f) => f.trim()).filter(Boolean))];
|
|
26
|
+
if (config.demo && config.mask.length === 0)
|
|
27
|
+
config.mask = [...DEMO_MASK_FIELDS];
|
|
28
|
+
if (!Number.isInteger(config.port) || config.port < 0 || config.port > 65535) {
|
|
29
|
+
throw new Error(`Invalid port: ${config.port}`);
|
|
30
|
+
}
|
|
31
|
+
if (!Number.isInteger(config.maxMessages) || config.maxMessages < 1) {
|
|
32
|
+
throw new Error(`--max-messages must be a positive integer, got ${config.maxMessages}`);
|
|
33
|
+
}
|
|
34
|
+
if (!Number.isInteger(config.maxPayloadBytes) || config.maxPayloadBytes < 1) {
|
|
35
|
+
throw new Error(`--max-payload must be a positive integer, got ${config.maxPayloadBytes}`);
|
|
36
|
+
}
|
|
37
|
+
config.publicUrl = config.publicUrl?.trim() ? normalizePublicUrl(config.publicUrl) : undefined;
|
|
38
|
+
return config;
|
|
39
|
+
}
|
|
40
|
+
function normalizePublicUrl(input) {
|
|
41
|
+
let url;
|
|
42
|
+
try {
|
|
43
|
+
url = new URL(input.trim());
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
throw new Error(`--public-url must be a URL like http://localhost:4000, got "${input}"`);
|
|
47
|
+
}
|
|
48
|
+
if (url.protocol !== 'http:' && url.protocol !== 'https:') {
|
|
49
|
+
throw new Error(`--public-url must start with http:// or https://, got "${input}"`);
|
|
50
|
+
}
|
|
51
|
+
return url.origin;
|
|
52
|
+
}
|
|
53
|
+
function stripUndefined(obj) {
|
|
54
|
+
return Object.fromEntries(Object.entries(obj).filter(([, v]) => v !== undefined));
|
|
55
|
+
}
|
|
56
|
+
export function isLoopbackHost(host) {
|
|
57
|
+
return host === '127.0.0.1' || host === '::1' || host === 'localhost' || host.startsWith('127.');
|
|
58
|
+
}
|
|
59
|
+
export function isWildcardHost(host) {
|
|
60
|
+
return host === '0.0.0.0' || host === '::' || host === '';
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/server/config.ts"],"names":[],"mappings":"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"}
|