wslog-proxy 0.0.0-stage → 0.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/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
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { connect } from 'node:net';
|
|
4
|
+
import { dirname } from 'node:path';
|
|
5
|
+
import { PROXY_PREFIX } from '../shared/protocol.js';
|
|
6
|
+
import { ensurePrivateFile } from './paths.js';
|
|
7
|
+
export class TargetError extends Error {
|
|
8
|
+
}
|
|
9
|
+
/** Normalize user input into a ws:// or wss:// URL, or throw a friendly error. */
|
|
10
|
+
export function normalizeTargetUrl(input) {
|
|
11
|
+
let raw = input.trim();
|
|
12
|
+
if (!raw)
|
|
13
|
+
throw new TargetError('Target URL is required');
|
|
14
|
+
if (!/^[a-z][a-z0-9+.-]*:\/\//i.test(raw))
|
|
15
|
+
raw = `ws://${raw}`;
|
|
16
|
+
let url;
|
|
17
|
+
try {
|
|
18
|
+
url = new URL(raw);
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
throw new TargetError(`Not a valid URL: ${input}`);
|
|
22
|
+
}
|
|
23
|
+
if (url.protocol === 'http:')
|
|
24
|
+
url.protocol = 'ws:';
|
|
25
|
+
else if (url.protocol === 'https:')
|
|
26
|
+
url.protocol = 'wss:';
|
|
27
|
+
if (url.protocol !== 'ws:' && url.protocol !== 'wss:') {
|
|
28
|
+
throw new TargetError(`Target must be a ws:// or wss:// URL, got ${url.protocol}//`);
|
|
29
|
+
}
|
|
30
|
+
url.hash = '';
|
|
31
|
+
return url.toString();
|
|
32
|
+
}
|
|
33
|
+
/** Stable id from the URL so the proxy URL survives restarts. */
|
|
34
|
+
export function targetIdFor(url) {
|
|
35
|
+
return createHash('sha256').update(url).digest('hex').slice(0, 8);
|
|
36
|
+
}
|
|
37
|
+
export class TargetRegistry {
|
|
38
|
+
bus;
|
|
39
|
+
targets = new Map();
|
|
40
|
+
constructor(bus) {
|
|
41
|
+
this.bus = bus;
|
|
42
|
+
}
|
|
43
|
+
add(input, options = {}) {
|
|
44
|
+
const url = normalizeTargetUrl(input);
|
|
45
|
+
const id = targetIdFor(url);
|
|
46
|
+
const existing = this.targets.get(id);
|
|
47
|
+
if (existing)
|
|
48
|
+
return existing;
|
|
49
|
+
const target = { id, url, createdAt: Date.now(), proxyPath: `${PROXY_PREFIX}${id}`, demo: options.demo };
|
|
50
|
+
this.targets.set(id, target);
|
|
51
|
+
this.bus.emit('targets', this.list());
|
|
52
|
+
return target;
|
|
53
|
+
}
|
|
54
|
+
remove(id) {
|
|
55
|
+
const removed = this.targets.delete(id);
|
|
56
|
+
if (removed)
|
|
57
|
+
this.bus.emit('targets', this.list());
|
|
58
|
+
return removed;
|
|
59
|
+
}
|
|
60
|
+
get(id) {
|
|
61
|
+
return this.targets.get(id);
|
|
62
|
+
}
|
|
63
|
+
list() {
|
|
64
|
+
return [...this.targets.values()];
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Can wslog reach the target's host and port right now? Resolves `undefined`
|
|
69
|
+
* if so, otherwise why not, in words. A plain TCP connect, not a WebSocket
|
|
70
|
+
* handshake, so the target never sees a fake client.
|
|
71
|
+
*/
|
|
72
|
+
export function probeTarget(url, timeoutMs = 1500) {
|
|
73
|
+
const { hostname, port, protocol } = new URL(url);
|
|
74
|
+
const host = hostname.replace(/^\[|\]$/g, '');
|
|
75
|
+
const portNumber = Number(port) || (protocol === 'wss:' ? 443 : 80);
|
|
76
|
+
return new Promise((resolve) => {
|
|
77
|
+
const socket = connect({ host, port: portNumber });
|
|
78
|
+
const done = (problem) => {
|
|
79
|
+
socket.destroy();
|
|
80
|
+
resolve(problem);
|
|
81
|
+
};
|
|
82
|
+
socket.setTimeout(timeoutMs, () => done(`${host}:${portNumber} did not answer within ${timeoutMs / 1000}s`));
|
|
83
|
+
socket.once('connect', () => done());
|
|
84
|
+
socket.once('error', (err) => {
|
|
85
|
+
if (err.code === 'ECONNREFUSED')
|
|
86
|
+
done(`nothing is listening on ${host}:${portNumber}`);
|
|
87
|
+
else if (err.code === 'ENOTFOUND' || err.code === 'EAI_AGAIN')
|
|
88
|
+
done(`${host} could not be found (DNS)`);
|
|
89
|
+
else
|
|
90
|
+
done(err.message);
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Re-register targets saved by an earlier run, then keep `file` in sync.
|
|
96
|
+
* Demo targets are skipped: their server gets a new port on every start.
|
|
97
|
+
* The file is private (0600) because target URLs can carry credentials.
|
|
98
|
+
*/
|
|
99
|
+
export function persistTargets(registry, bus, file, logger) {
|
|
100
|
+
try {
|
|
101
|
+
const saved = JSON.parse(readFileSync(file, 'utf8'));
|
|
102
|
+
const urls = Array.isArray(saved.targets) ? saved.targets : [];
|
|
103
|
+
urls.forEach((url, i) => {
|
|
104
|
+
try {
|
|
105
|
+
if (typeof url === 'string')
|
|
106
|
+
registry.add(url);
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
// Not the URL itself: it may carry credentials.
|
|
110
|
+
logger.error(`Skipping saved target #${i + 1} in ${file}: not a valid ws:// or wss:// URL`);
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
catch (err) {
|
|
115
|
+
if (err.code !== 'ENOENT')
|
|
116
|
+
logger.error(`Could not read saved targets from ${file}: ${err.message}`);
|
|
117
|
+
}
|
|
118
|
+
bus.on('targets', (targets) => {
|
|
119
|
+
const urls = targets.filter((t) => !t.demo).map((t) => t.url);
|
|
120
|
+
try {
|
|
121
|
+
mkdirSync(dirname(file), { recursive: true });
|
|
122
|
+
ensurePrivateFile(file);
|
|
123
|
+
writeFileSync(file, `${JSON.stringify({ targets: urls }, null, 2)}\n`);
|
|
124
|
+
}
|
|
125
|
+
catch (err) {
|
|
126
|
+
logger.error(`Could not save targets to ${file}: ${err.message}`);
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Build the upstream URL for a proxied request.
|
|
132
|
+
* `/p/<id>/chat?room=1` on target `ws://host:8080/ws` → `ws://host:8080/ws/chat?room=1`.
|
|
133
|
+
*
|
|
134
|
+
* The app's query string is passed through as sent (repeated keys, encoding,
|
|
135
|
+
* order), so signed URLs keep working. A key the app sends replaces the same
|
|
136
|
+
* key in the target URL.
|
|
137
|
+
*/
|
|
138
|
+
export function buildUpstreamUrl(target, requestPath) {
|
|
139
|
+
const url = new URL(target.url);
|
|
140
|
+
const incoming = new URL(requestPath, 'http://proxy.local');
|
|
141
|
+
const suffix = incoming.pathname.slice(target.proxyPath.length);
|
|
142
|
+
if (suffix && suffix !== '/') {
|
|
143
|
+
url.pathname = `${url.pathname.replace(/\/$/, '')}/${suffix.replace(/^\//, '')}`;
|
|
144
|
+
}
|
|
145
|
+
const query = incoming.search.slice(1);
|
|
146
|
+
if (query) {
|
|
147
|
+
const sent = new Set(incoming.searchParams.keys());
|
|
148
|
+
const kept = url.search
|
|
149
|
+
.slice(1)
|
|
150
|
+
.split('&')
|
|
151
|
+
.filter((pair) => pair && !sent.has(new URLSearchParams(pair).keys().next().value ?? ''));
|
|
152
|
+
url.search = [...kept, query].join('&');
|
|
153
|
+
}
|
|
154
|
+
return url.toString();
|
|
155
|
+
}
|
|
156
|
+
//# sourceMappingURL=targets.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"targets.js","sourceRoot":"","sources":["../../src/server/targets.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { WslogConfig } from '../config.js';
|
|
2
|
+
export type LogLevel = WslogConfig['logLevel'];
|
|
3
|
+
export interface Logger {
|
|
4
|
+
error(message: string, ...rest: unknown[]): void;
|
|
5
|
+
info(message: string, ...rest: unknown[]): void;
|
|
6
|
+
debug(message: string, ...rest: unknown[]): void;
|
|
7
|
+
}
|
|
8
|
+
export declare const color: {
|
|
9
|
+
dim: (s: string) => string;
|
|
10
|
+
bold: (s: string) => string;
|
|
11
|
+
cyan: (s: string) => string;
|
|
12
|
+
green: (s: string) => string;
|
|
13
|
+
yellow: (s: string) => string;
|
|
14
|
+
red: (s: string) => string;
|
|
15
|
+
};
|
|
16
|
+
export declare function createLogger(level: LogLevel): Logger;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
const order = { silent: 0, error: 1, info: 2, debug: 3 };
|
|
2
|
+
const useColor = process.stdout.isTTY && !process.env.NO_COLOR;
|
|
3
|
+
export const color = {
|
|
4
|
+
dim: (s) => (useColor ? `\x1b[2m${s}\x1b[22m` : s),
|
|
5
|
+
bold: (s) => (useColor ? `\x1b[1m${s}\x1b[22m` : s),
|
|
6
|
+
cyan: (s) => (useColor ? `\x1b[36m${s}\x1b[39m` : s),
|
|
7
|
+
green: (s) => (useColor ? `\x1b[32m${s}\x1b[39m` : s),
|
|
8
|
+
yellow: (s) => (useColor ? `\x1b[33m${s}\x1b[39m` : s),
|
|
9
|
+
red: (s) => (useColor ? `\x1b[31m${s}\x1b[39m` : s),
|
|
10
|
+
};
|
|
11
|
+
export function createLogger(level) {
|
|
12
|
+
const enabled = (l) => order[l] <= order[level];
|
|
13
|
+
return {
|
|
14
|
+
error: (m, ...r) => enabled('error') && console.error(color.red('✖'), m, ...r),
|
|
15
|
+
info: (m, ...r) => enabled('info') && console.log(m, ...r),
|
|
16
|
+
debug: (m, ...r) => enabled('debug') && console.log(color.dim(`[debug] ${m}`), ...r),
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=logger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../../../src/server/util/logger.ts"],"names":[],"mappings":"AAUA,MAAM,KAAK,GAA6B,EAAE,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;AACnF,MAAM,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC;AAE/D,MAAM,CAAC,MAAM,KAAK,GAAG;IACnB,GAAG,EAAE,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1D,IAAI,EAAE,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3D,IAAI,EAAE,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;IAC5D,KAAK,EAAE,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;IAC7D,MAAM,EAAE,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;IAC9D,GAAG,EAAE,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;CAC5D,CAAC;AAEF,MAAM,UAAU,YAAY,CAAC,KAAe;IAC1C,MAAM,OAAO,GAAG,CAAC,CAAW,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC;IAC1D,OAAO;QACL,KAAK,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,GAAG,CAAC,CAAC;QAC9E,IAAI,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;QAC1D,KAAK,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,WAAW,CAAC,EAAE,CAAC,EAAE,GAAG,CAAC,CAAC;KACrF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Direction, MessageRecord } from './protocol.js';
|
|
2
|
+
/** One filter definition used by the dashboard, the API, and exports. */
|
|
3
|
+
export interface MessageFilter {
|
|
4
|
+
/** Case-insensitive substring match on the payload. */
|
|
5
|
+
q?: string;
|
|
6
|
+
direction?: Direction;
|
|
7
|
+
connectionId?: string;
|
|
8
|
+
targetId?: string;
|
|
9
|
+
}
|
|
10
|
+
export declare function isEmptyFilter(filter: MessageFilter): boolean;
|
|
11
|
+
/** Build a predicate once, then apply it to many messages. */
|
|
12
|
+
export declare function createMatcher(filter: MessageFilter): (msg: MessageRecord) => boolean;
|
|
13
|
+
export declare function filterToSearchParams(filter: MessageFilter): URLSearchParams;
|
|
14
|
+
export declare function filterFromSearchParams(params: URLSearchParams): MessageFilter;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export function isEmptyFilter(filter) {
|
|
2
|
+
return !filter.q && !filter.direction && !filter.connectionId && !filter.targetId;
|
|
3
|
+
}
|
|
4
|
+
/** Build a predicate once, then apply it to many messages. */
|
|
5
|
+
export function createMatcher(filter) {
|
|
6
|
+
const needle = filter.q?.trim().toLowerCase() ?? '';
|
|
7
|
+
const { direction, connectionId, targetId } = filter;
|
|
8
|
+
return (msg) => {
|
|
9
|
+
if (direction && msg.direction !== direction)
|
|
10
|
+
return false;
|
|
11
|
+
if (connectionId && msg.connectionId !== connectionId)
|
|
12
|
+
return false;
|
|
13
|
+
if (targetId && msg.targetId !== targetId)
|
|
14
|
+
return false;
|
|
15
|
+
if (needle && !msg.payload.toLowerCase().includes(needle))
|
|
16
|
+
return false;
|
|
17
|
+
return true;
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
export function filterToSearchParams(filter) {
|
|
21
|
+
const params = new URLSearchParams();
|
|
22
|
+
if (filter.q)
|
|
23
|
+
params.set('q', filter.q);
|
|
24
|
+
if (filter.direction)
|
|
25
|
+
params.set('direction', filter.direction);
|
|
26
|
+
if (filter.connectionId)
|
|
27
|
+
params.set('connectionId', filter.connectionId);
|
|
28
|
+
if (filter.targetId)
|
|
29
|
+
params.set('targetId', filter.targetId);
|
|
30
|
+
return params;
|
|
31
|
+
}
|
|
32
|
+
export function filterFromSearchParams(params) {
|
|
33
|
+
const direction = params.get('direction');
|
|
34
|
+
return {
|
|
35
|
+
q: params.get('q') ?? undefined,
|
|
36
|
+
direction: direction === 'in' || direction === 'out' ? direction : undefined,
|
|
37
|
+
connectionId: params.get('connectionId') ?? undefined,
|
|
38
|
+
targetId: params.get('targetId') ?? undefined,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=filter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"filter.js","sourceRoot":"","sources":["../../src/shared/filter.ts"],"names":[],"mappings":"AAWA,MAAM,UAAU,aAAa,CAAC,MAAqB;IACjD,OAAO,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,IAAI,CAAC,MAAM,CAAC,YAAY,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC;AACpF,CAAC;AAED,8DAA8D;AAC9D,MAAM,UAAU,aAAa,CAAC,MAAqB;IACjD,MAAM,MAAM,GAAG,MAAM,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,WAAW,EAAE,IAAI,EAAE,CAAC;IACpD,MAAM,EAAE,SAAS,EAAE,YAAY,EAAE,QAAQ,EAAE,GAAG,MAAM,CAAC;IACrD,OAAO,CAAC,GAAG,EAAE,EAAE;QACb,IAAI,SAAS,IAAI,GAAG,CAAC,SAAS,KAAK,SAAS;YAAE,OAAO,KAAK,CAAC;QAC3D,IAAI,YAAY,IAAI,GAAG,CAAC,YAAY,KAAK,YAAY;YAAE,OAAO,KAAK,CAAC;QACpE,IAAI,QAAQ,IAAI,GAAG,CAAC,QAAQ,KAAK,QAAQ;YAAE,OAAO,KAAK,CAAC;QACxD,IAAI,MAAM,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC;YAAE,OAAO,KAAK,CAAC;QACxE,OAAO,IAAI,CAAC;IACd,CAAC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,MAAqB;IACxD,MAAM,MAAM,GAAG,IAAI,eAAe,EAAE,CAAC;IACrC,IAAI,MAAM,CAAC,CAAC;QAAE,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;IACxC,IAAI,MAAM,CAAC,SAAS;QAAE,MAAM,CAAC,GAAG,CAAC,WAAW,EAAE,MAAM,CAAC,SAAS,CAAC,CAAC;IAChE,IAAI,MAAM,CAAC,YAAY;QAAE,MAAM,CAAC,GAAG,CAAC,cAAc,EAAE,MAAM,CAAC,YAAY,CAAC,CAAC;IACzE,IAAI,MAAM,CAAC,QAAQ;QAAE,MAAM,CAAC,GAAG,CAAC,UAAU,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC;IAC7D,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,MAAuB;IAC5D,MAAM,SAAS,GAAG,MAAM,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IAC1C,OAAO;QACL,CAAC,EAAE,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,SAAS;QAC/B,SAAS,EAAE,SAAS,KAAK,IAAI,IAAI,SAAS,KAAK,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS;QAC5E,YAAY,EAAE,MAAM,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,SAAS;QACrD,QAAQ,EAAE,MAAM,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,SAAS;KAC9C,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/shared/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC"}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wire types shared by the server and the dashboard.
|
|
3
|
+
* Anything that crosses the HTTP / live-stream boundary is defined here.
|
|
4
|
+
*/
|
|
5
|
+
/** `out` = client → server (sent by your app), `in` = server → client. */
|
|
6
|
+
export type Direction = 'in' | 'out';
|
|
7
|
+
export type PayloadKind = 'text' | 'binary';
|
|
8
|
+
export interface MessageRecord {
|
|
9
|
+
/** Monotonic id, unique per wslog data store. */
|
|
10
|
+
id: number;
|
|
11
|
+
connectionId: string;
|
|
12
|
+
targetId: string;
|
|
13
|
+
direction: Direction;
|
|
14
|
+
/** Epoch milliseconds. */
|
|
15
|
+
time: number;
|
|
16
|
+
/** Size of the original frame in bytes (before masking or truncation). */
|
|
17
|
+
size: number;
|
|
18
|
+
kind: PayloadKind;
|
|
19
|
+
/** UTF-8 text for text frames, base64 for binary frames. Already masked. */
|
|
20
|
+
payload: string;
|
|
21
|
+
/** The stored payload was cut at the configured `maxPayloadBytes`. */
|
|
22
|
+
truncated: boolean;
|
|
23
|
+
/**
|
|
24
|
+
* Only on records sent to the dashboard: the payload was shortened for
|
|
25
|
+
* transport; fetch `GET /api/messages/:id` for the stored version.
|
|
26
|
+
*/
|
|
27
|
+
partial?: boolean;
|
|
28
|
+
}
|
|
29
|
+
export type ConnectionStatus = 'connecting' | 'open' | 'closed' | 'error';
|
|
30
|
+
/**
|
|
31
|
+
* Why a connection never opened: the server could not be reached, it refused
|
|
32
|
+
* the handshake, or the app used a proxy URL that has no target (removed, or
|
|
33
|
+
* a wslog that does not remember it).
|
|
34
|
+
*/
|
|
35
|
+
export type ConnectionFailure = 'unreachable' | 'rejected' | 'no-target';
|
|
36
|
+
export interface ConnectionInfo {
|
|
37
|
+
id: string;
|
|
38
|
+
targetId: string;
|
|
39
|
+
/** Path + query the client requested on the proxy (masked). */
|
|
40
|
+
path: string;
|
|
41
|
+
/** Upstream URL the proxy dialed (masked). */
|
|
42
|
+
upstreamUrl: string;
|
|
43
|
+
clientAddress: string;
|
|
44
|
+
userAgent?: string;
|
|
45
|
+
protocol?: string;
|
|
46
|
+
status: ConnectionStatus;
|
|
47
|
+
openedAt: number;
|
|
48
|
+
closedAt?: number;
|
|
49
|
+
closeCode?: number;
|
|
50
|
+
closeReason?: string;
|
|
51
|
+
/** Which side closed first. */
|
|
52
|
+
closedBy?: 'client' | 'server' | 'proxy';
|
|
53
|
+
error?: string;
|
|
54
|
+
/** Set when `status` is `error` because the connection never opened. */
|
|
55
|
+
failure?: ConnectionFailure;
|
|
56
|
+
counts: {
|
|
57
|
+
in: number;
|
|
58
|
+
out: number;
|
|
59
|
+
bytesIn: number;
|
|
60
|
+
bytesOut: number;
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
export interface TargetInfo {
|
|
64
|
+
/** Stable id derived from the URL, so the proxy URL survives restarts. */
|
|
65
|
+
id: string;
|
|
66
|
+
url: string;
|
|
67
|
+
createdAt: number;
|
|
68
|
+
/** e.g. `/p/3f9a1c2b` — combine with `AppInfo.proxyBaseUrl`. */
|
|
69
|
+
proxyPath: string;
|
|
70
|
+
demo?: boolean;
|
|
71
|
+
}
|
|
72
|
+
/** `POST /api/targets` answer. */
|
|
73
|
+
export interface AddedTarget extends TargetInfo {
|
|
74
|
+
/** Why wslog could not reach the target when it was added, if it could not. */
|
|
75
|
+
unreachable?: string;
|
|
76
|
+
}
|
|
77
|
+
export type Role = 'admin' | 'viewer';
|
|
78
|
+
export interface AppInfo {
|
|
79
|
+
name: 'wslog';
|
|
80
|
+
version: string;
|
|
81
|
+
role: Role;
|
|
82
|
+
/** e.g. `ws://127.0.0.1:4000` */
|
|
83
|
+
proxyBaseUrl: string;
|
|
84
|
+
maskFields: string[];
|
|
85
|
+
maxMessages: number;
|
|
86
|
+
storage: string;
|
|
87
|
+
demoRunning: boolean;
|
|
88
|
+
}
|
|
89
|
+
/** Events pushed to dashboards over `/api/live`. */
|
|
90
|
+
export type LiveEvent = {
|
|
91
|
+
type: 'hello';
|
|
92
|
+
info: AppInfo;
|
|
93
|
+
targets: TargetInfo[];
|
|
94
|
+
connections: ConnectionInfo[];
|
|
95
|
+
messages: MessageRecord[];
|
|
96
|
+
} | {
|
|
97
|
+
type: 'messages';
|
|
98
|
+
items: MessageRecord[];
|
|
99
|
+
} | {
|
|
100
|
+
type: 'connection';
|
|
101
|
+
connection: ConnectionInfo;
|
|
102
|
+
} | {
|
|
103
|
+
type: 'targets';
|
|
104
|
+
targets: TargetInfo[];
|
|
105
|
+
} | {
|
|
106
|
+
type: 'rates';
|
|
107
|
+
time: number;
|
|
108
|
+
rates: Record<string, number>;
|
|
109
|
+
}
|
|
110
|
+
/** Messages were deleted; `connections` is what remains (open ones, counters reset). */
|
|
111
|
+
| {
|
|
112
|
+
type: 'cleared';
|
|
113
|
+
connections: ConnectionInfo[];
|
|
114
|
+
} | {
|
|
115
|
+
type: 'info';
|
|
116
|
+
info: Omit<AppInfo, 'role'>;
|
|
117
|
+
};
|
|
118
|
+
export interface ApiError {
|
|
119
|
+
error: string;
|
|
120
|
+
}
|
|
121
|
+
/** Max characters of payload sent to the dashboard per message in lists/live. */
|
|
122
|
+
export declare const LIVE_PAYLOAD_LIMIT = 4096;
|
|
123
|
+
export declare const PROXY_PREFIX = "/p/";
|
|
124
|
+
export declare const LIVE_PATH = "/api/live";
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wire types shared by the server and the dashboard.
|
|
3
|
+
* Anything that crosses the HTTP / live-stream boundary is defined here.
|
|
4
|
+
*/
|
|
5
|
+
/** Max characters of payload sent to the dashboard per message in lists/live. */
|
|
6
|
+
export const LIVE_PAYLOAD_LIMIT = 4096;
|
|
7
|
+
export const PROXY_PREFIX = '/p/';
|
|
8
|
+
export const LIVE_PATH = '/api/live';
|
|
9
|
+
//# sourceMappingURL=protocol.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protocol.js","sourceRoot":"","sources":["../../src/shared/protocol.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAgHH,iFAAiF;AACjF,MAAM,CAAC,MAAM,kBAAkB,GAAG,IAAI,CAAC;AAEvC,MAAM,CAAC,MAAM,YAAY,GAAG,KAAK,CAAC;AAClC,MAAM,CAAC,MAAM,SAAS,GAAG,WAAW,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
:root{--lightningcss-light: ;--lightningcss-dark:initial;color-scheme:dark;--bg:#0d1117;--bg-raised:#131a23;--bg-hover:#1a2330;--bg-active:#1d2b3d;--border:#243040;--text:#e6edf3;--muted:#8b98a8;--accent:#34d399;--accent-ink:#052e1f;--out:#7aa2ff;--out-bg:#7aa2ff1f;--in:#4ade80;--in-bg:#4ade801a;--warn:#fbbf24;--danger:#f87171;--json-key:#c4b5fd;--json-string:#86efac;--json-number:#fcd34d;--json-bool:#f9a8d4;--radius:7px;--mono:ui-monospace, "SF Mono", "JetBrains Mono", Menlo, Consolas, monospace;--sans:system-ui, -apple-system, "Segoe UI", Roboto, sans-serif}@media (prefers-color-scheme:light){:root{--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light;--bg:#fff;--bg-raised:#f6f8fa;--bg-hover:#eef2f6;--bg-active:#e3ecfa;--border:#d8dee4;--text:#1f2328;--muted:#656d76;--accent:#059669;--accent-ink:#fff;--out:#2f5fd0;--out-bg:#2f5fd014;--in:#16803c;--in-bg:#16803c14;--warn:#b45309;--danger:#cf222e;--json-key:#6d28d9;--json-string:#0f7b3f;--json-number:#b45309;--json-bool:#be185d}}*{box-sizing:border-box}html,body,#root{height:100%;margin:0}body{background:var(--bg);color:var(--text);font:13px/1.45 var(--sans);-webkit-font-smoothing:antialiased}.mono,code,kbd,pre{font-family:var(--mono);font-size:12px}code{background:var(--bg-hover);border-radius:4px;padding:1px 5px}kbd{border:1px solid var(--border);border-bottom-width:2px;border-radius:4px;padding:0 4px}.muted{color:var(--muted)}.small{font-size:12px}h1{margin:0 0 6px;font-size:20px}.btn{border:1px solid var(--border);border-radius:var(--radius);background:var(--bg-raised);height:30px;color:var(--text);font:inherit;cursor:pointer;white-space:nowrap;align-items:center;gap:6px;padding:0 10px;display:inline-flex}.btn:hover:not(:disabled){background:var(--bg-hover)}.btn:disabled{opacity:.55;cursor:default}.btn:focus-visible,.input:focus-visible,.segmented button:focus-visible,.side-item-main:focus-visible,.table:focus-visible{outline:2px solid var(--accent);outline-offset:1px}.btn-primary{background:var(--accent);border-color:var(--accent);color:var(--accent-ink);font-weight:600}.btn-primary:hover:not(:disabled){background:var(--accent);filter:brightness(1.08)}.btn-warn{border-color:var(--warn);color:var(--warn)}.btn-danger,.btn-icon.btn-danger{border-color:var(--danger);color:var(--danger)}.btn-ghost,.btn-icon{background:0 0;border-color:#0000}.btn-icon{width:26px;height:26px;color:var(--muted);justify-content:center;padding:0}.btn-icon:hover:not(:disabled){color:var(--text)}.btn-small{height:26px;padding:0 8px;font-size:12px}.input{border:1px solid var(--border);border-radius:var(--radius);background:var(--bg);min-width:0;height:30px;color:var(--text);font:inherit;padding:0 10px}.input.mono{font-family:var(--mono);font-size:12px}.segmented{border:1px solid var(--border);border-radius:var(--radius);display:inline-flex;overflow:hidden}.segmented button{border:0;border-right:1px solid var(--border);height:28px;color:var(--muted);font:inherit;cursor:pointer;background:0 0;padding:0 10px}.segmented button:last-child{border-right:0}.segmented button.active{background:var(--bg-active);color:var(--text)}.badge{border:1px solid var(--border);color:var(--muted);border-radius:99px;padding:0 6px;font-size:11px;line-height:17px;display:inline-block}.chip{border:1px solid var(--out);background:var(--out-bg);height:26px;color:var(--text);font:inherit;cursor:pointer;border-radius:99px;align-items:center;gap:5px;padding:0 8px;font-size:12px;display:inline-flex}.form-error{color:var(--danger);flex-basis:100%;font-size:12px}.notice{border-left:3px solid var(--warn);background:var(--bg-raised);border-radius:4px;margin:8px 0;padding:8px 10px;font-size:12px}.problem{border-left:3px solid var(--danger);background:var(--bg-raised);border-radius:var(--radius);text-align:left;flex-direction:column;gap:8px;width:100%;max-width:560px;margin-bottom:28px;padding:14px 16px;display:flex}.problem h2{color:var(--danger);margin:0;font-size:15px}.problem code{overflow-wrap:anywhere;font-size:12px}.problem p{color:var(--muted);margin:0}.boot{height:100%;color:var(--muted);justify-content:center;align-items:center;gap:8px;display:flex}.boot-denied{text-align:center;max-width:520px;color:var(--text);flex-direction:column;margin:0 auto;padding:0 16px}.app{flex-direction:column;height:100%;display:flex}.topbar{border-bottom:1px solid var(--border);background:var(--bg-raised);flex-wrap:wrap;align-items:center;gap:10px 14px;padding:8px 14px;display:flex}.brand{align-items:center;gap:8px;display:flex}.brand-name{letter-spacing:-.01em;font-size:15px;font-weight:700}.brand-version{color:var(--muted);font-size:11px}.status-dot{background:var(--muted);border-radius:50%;width:8px;height:8px}.status-live{background:var(--accent);box-shadow:0 0 0 3px color-mix(in srgb, var(--accent) 25%, transparent)}.status-reconnecting{background:var(--warn)}.target-form{flex-wrap:wrap;flex:260px;gap:6px;max-width:460px;display:flex}.target-form .input{flex:1}.target-form-large{flex:none;width:100%;max-width:560px;margin:18px 0 8px}.target-form-large .input,.target-form-large .btn{height:38px}.proxy-pill{border:1px solid var(--border);border-radius:var(--radius);background:var(--bg);align-items:center;gap:8px;min-width:0;height:30px;padding:0 4px 0 10px;display:flex}.proxy-pill code{text-overflow:ellipsis;white-space:nowrap;background:0 0;padding:0;overflow:hidden}.proxy-label{color:var(--accent);text-transform:uppercase;letter-spacing:.04em;font-size:11px;font-weight:600}.topbar-actions{gap:6px;margin-left:auto;display:flex}.banner{background:color-mix(in srgb, var(--warn) 18%, var(--bg));color:var(--text);padding:6px 14px;font-size:12px}.layout{flex:1;grid-template-columns:260px minmax(0,1fr);min-height:0;display:grid}.layout.with-detail{grid-template-columns:260px minmax(0, 1fr) var(--detail-width,minmax(320px, 36%))}.layout.no-sidebar{grid-template-columns:minmax(0,1fr)}body.resizing{cursor:col-resize;-webkit-user-select:none;user-select:none}.app.offline .layout{opacity:.55;filter:grayscale(.6)}.sidebar{border-right:1px solid var(--border);background:var(--bg);flex-direction:column;min-height:0;display:flex}.side-section{border-bottom:1px solid var(--border);padding:10px 8px 6px}.side-section-grow{border-bottom:0;flex:1;min-height:0;overflow-y:auto}.side-title{text-transform:uppercase;letter-spacing:.05em;color:var(--muted);justify-content:space-between;margin:0 6px 6px;font-size:11px;font-weight:600;display:flex}.side-title .muted{text-transform:none;letter-spacing:0;font-weight:400}.side-list{margin:0;padding:0;list-style:none}.side-item{border-radius:var(--radius);align-items:center;display:flex}.side-item:hover{background:var(--bg-hover)}.side-item.active{background:var(--bg-active)}.side-item-main{border-radius:var(--radius);min-width:0;color:inherit;font:inherit;text-align:left;cursor:pointer;background:0 0;border:0;flex-direction:column;flex:1;gap:2px;padding:6px;display:flex}.side-item:first-child .side-item-main{flex-direction:row;justify-content:space-between}.side-title-action{text-transform:none;letter-spacing:0;gap:3px;height:20px;margin:-3px -6px -3px 0}.target-form-compact{flex:none;max-width:none;margin:0 4px 8px}.target-item{flex-direction:column;align-items:stretch}.target-line{align-items:center;min-width:0;display:flex}.target-item .side-item-main{flex-direction:row;align-items:center;gap:6px}.target-url{white-space:nowrap;min-width:0;display:flex}.target-host{text-overflow:ellipsis;min-width:3ch;color:var(--muted);overflow:hidden}.target-path{text-overflow:ellipsis;flex:none;max-width:75%;overflow:hidden}.target-confirm{color:var(--danger);margin:0 6px 6px;font-size:11px}.conn-path{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.conn-line{align-items:center;gap:8px;min-width:0;display:flex}.conn-path{color:var(--muted)}.conn-meta{font-family:var(--mono);font-size:11px}.conn-closed{opacity:.6}.conn-error{color:var(--danger);overflow-wrap:anywhere;font-size:11px;line-height:1.35}.conn-dot{background:var(--muted);border-radius:50%;flex:none;width:7px;height:7px}.conn-dot-open{background:var(--accent)}.conn-dot-connecting{background:var(--warn)}.conn-dot-error{background:var(--danger)}.side-empty{margin:8px 6px;font-size:12px}.sparkline{margin-left:auto;display:inline-flex}.sparkline-base{stroke:var(--border)}.sparkline-line{fill:none;stroke:var(--accent);stroke-width:1.5px;stroke-linejoin:round}.traffic{flex-direction:column;min-width:0;min-height:0;display:flex;container-type:inline-size}.paused-bar{border-bottom:1px solid var(--border);background:color-mix(in srgb, var(--warn) 16%, var(--bg));align-items:center;gap:10px;padding:5px 10px;font-size:12px;display:flex}.paused-bar strong{color:var(--warn)}.toolbar{border-bottom:1px solid var(--border);flex-wrap:wrap;align-items:center;gap:8px;padding:8px 10px;display:flex}.search{max-width:360px;color:var(--muted);flex:200px;align-items:center;display:flex;position:relative}.search svg{position:absolute;left:9px}.search .input{width:100%;padding-left:30px}.toolbar-count{margin-left:auto;font-size:12px}.dir-out{color:var(--out)}.dir-in{color:var(--in)}.table{outline:none;flex-direction:column;flex:1;min-height:0;display:flex}.table-head,.table-row{grid-template-columns:48px 92px 44px 58px 54px minmax(0,1fr);align-items:center;gap:8px;padding:0 10px;display:grid}.table-head{border-bottom:1px solid var(--border);height:28px;color:var(--muted);text-transform:uppercase;letter-spacing:.04em;font-size:11px;font-weight:600}.table-body{flex:1;min-height:0;overflow:hidden auto}.table-row{border-bottom:1px solid color-mix(in srgb, var(--border) 45%, transparent);cursor:pointer;white-space:nowrap;position:absolute;top:0;left:0;right:0}.table-row:hover{background:var(--bg-hover)}.table-row.selected{background:var(--bg-active);box-shadow:inset 2px 0 0 var(--accent)}.table-row>span{text-overflow:ellipsis;overflow:hidden}.dir-row-out .col-dir{color:var(--out)}.dir-row-in .col-dir{color:var(--in)}.col-dir{font-size:12px;font-weight:600}.col-id,.col-size{text-align:right}.event-row{cursor:default;color:var(--muted);background:color-mix(in srgb, var(--bg-raised) 70%, transparent);font-size:12px}.event-row:hover{background:color-mix(in srgb, var(--bg-raised) 70%, transparent)}.event-open .col-dir{color:var(--accent)}.event-close .col-dir,.event-fail .col-dir,.event-fail .col-preview{color:var(--danger)}@container (width<=640px){.col-id{display:none}.table-head,.table-row{grid-template-columns:92px 44px 58px 54px minmax(0,1fr)}}@container (width<=480px){.col-size,.col-conn{display:none}.table-head,.table-row{grid-template-columns:84px 44px minmax(0,1fr)}}.detail{border-left:1px solid var(--border);background:var(--bg);flex-direction:column;min-width:0;min-height:0;display:flex;position:relative}.detail-resize{z-index:2;cursor:col-resize;width:8px;position:absolute;top:0;bottom:0;left:-4px}.detail-resize:hover,.detail-resize:focus-visible,body.resizing .detail-resize{background:linear-gradient(to right, transparent 3px, var(--accent) 3px, var(--accent) 5px, transparent 5px);outline:none}.detail-empty{text-align:center;justify-content:center;align-items:center;padding:20px}.detail-head{border-bottom:1px solid var(--border);align-items:center;gap:8px;padding:10px 12px;display:flex}.detail-close{margin-left:auto}.dir-badge{border-radius:99px;padding:1px 7px;font-size:12px;font-weight:600}.dir-badge.dir-out{background:var(--out-bg)}.dir-badge.dir-in{background:var(--in-bg)}.detail-meta{border-bottom:1px solid var(--border);grid-template-columns:auto 1fr;gap:4px 12px;margin:0;padding:10px 12px;font-size:12px;display:grid}.detail-meta dt{color:var(--muted)}.detail-meta dd{text-overflow:ellipsis;white-space:nowrap;margin:0;overflow:hidden}.detail-body{flex-direction:column;flex:1;min-height:0;padding:10px 12px;display:flex}.detail-tabs{justify-content:space-between;align-items:center;margin-bottom:8px;display:flex}.payload{border:1px solid var(--border);border-radius:var(--radius);background:var(--bg-raised);flex:1;min-height:0;padding:8px 0;overflow:auto}.raw{white-space:pre-wrap;word-break:break-all;margin:0;padding:0 12px}.raw.hex{white-space:pre;word-break:normal;overflow-x:auto}.json-tree{padding-right:12px}.json-row{white-space:pre-wrap;word-break:break-all;padding-top:1px;padding-bottom:1px}.json-toggle{cursor:pointer;align-items:center;display:flex}.json-toggle:hover{background:var(--bg-hover)}.json-chevron{width:16px;color:var(--muted);flex:none;transition:transform .1s}.json-chevron.open{transform:rotate(90deg)}.json-key{color:var(--json-key)}.json-string{color:var(--json-string)}.json-number{color:var(--json-number)}.json-boolean,.json-null{color:var(--json-bool)}.json-punct,.json-summary{color:var(--muted)}.json-summary{margin:0 4px;font-style:italic}.json-encoded{border:1px solid var(--border);color:var(--muted);font-family:var(--sans);border-radius:4px;margin-right:6px;padding:0 5px;font-size:10px}mark{background:color-mix(in srgb, var(--warn) 35%, transparent);color:inherit;border-radius:2px}.json-more{color:var(--out);font:inherit;cursor:pointer;background:0 0;border:0;padding:2px 0}.empty{text-align:center;flex-direction:column;flex:1;justify-content:center;align-items:center;padding:32px 20px;display:flex}.steps{text-align:left;color:var(--muted);margin:12px 0 0;padding-left:20px}.steps li{margin:4px 0}.empty-divider{width:100%;max-width:320px;color:var(--muted);align-items:center;gap:10px;margin:22px 0 14px;display:flex}.empty-divider:before,.empty-divider:after{content:"";border-top:1px solid var(--border);flex:1}.code-block{border:1px solid var(--border);border-radius:var(--radius);background:var(--bg-raised);align-items:center;gap:10px;max-width:100%;margin-top:12px;padding:8px 8px 8px 14px;display:flex}.code-block code{white-space:nowrap;background:0 0;padding:0;overflow-x:auto}.modal-backdrop{z-index:10;background:#00000073;justify-content:center;align-items:flex-start;padding:12vh 16px 16px;display:flex;position:fixed;inset:0}.modal{border:1px solid var(--border);background:var(--bg-raised);border-radius:10px;width:100%;max-width:560px;padding:16px 18px;box-shadow:0 20px 50px #00000059}.modal-head{justify-content:space-between;align-items:center;display:flex}.modal-head h2{margin:0;font-size:15px}.share-row{gap:8px;display:flex}.share-row .input{flex:1}.toasts{z-index:20;flex-direction:column;gap:8px;display:flex;position:fixed;bottom:16px;right:16px}.toast{border:1px solid var(--border);border-radius:var(--radius);background:var(--bg-raised);max-width:380px;padding:8px 12px;box-shadow:0 8px 24px #00000040}.toast-error{border-color:var(--danger)}@media (width<=1200px){.layout{grid-template-columns:220px minmax(0,1fr)}.layout.with-detail{grid-template-columns:220px minmax(0, 1fr) var(--detail-width,minmax(300px, 34%))}}@media (width<=860px){.layout,.layout.with-detail{grid-template-rows:auto minmax(0,1fr) auto;grid-template-columns:minmax(0,1fr)}.sidebar{border-right:0;border-bottom:1px solid var(--border);max-height:40vh}.side-section-grow{min-height:120px}.detail{border-left:0;border-top:1px solid var(--border);max-height:45vh}.detail-resize,.topbar-actions .btn span{display:none}}
|