@geekmidas/telescope 0.0.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/README.md +521 -0
- package/dist/Telescope-B3Wd82yk.cjs +602 -0
- package/dist/Telescope-B3Wd82yk.cjs.map +1 -0
- package/dist/Telescope-C5dyDYYB.d.cts +133 -0
- package/dist/Telescope-D-uoZB6b.mjs +596 -0
- package/dist/Telescope-D-uoZB6b.mjs.map +1 -0
- package/dist/Telescope-DyIWgh9-.d.mts +133 -0
- package/dist/Telescope.cjs +3 -0
- package/dist/Telescope.d.cts +3 -0
- package/dist/Telescope.d.mts +3 -0
- package/dist/Telescope.mjs +3 -0
- package/dist/chunk-CUT6urMc.cjs +30 -0
- package/dist/index.cjs +5 -0
- package/dist/index.d.cts +4 -0
- package/dist/index.d.mts +4 -0
- package/dist/index.mjs +4 -0
- package/dist/logger/console.cjs +161 -0
- package/dist/logger/console.cjs.map +1 -0
- package/dist/logger/console.d.cts +109 -0
- package/dist/logger/console.d.mts +109 -0
- package/dist/logger/console.mjs +159 -0
- package/dist/logger/console.mjs.map +1 -0
- package/dist/logger/pino.cjs +118 -0
- package/dist/logger/pino.cjs.map +1 -0
- package/dist/logger/pino.d.cts +89 -0
- package/dist/logger/pino.d.mts +89 -0
- package/dist/logger/pino.mjs +116 -0
- package/dist/logger/pino.mjs.map +1 -0
- package/dist/memory-9-B9WACq.cjs +110 -0
- package/dist/memory-9-B9WACq.cjs.map +1 -0
- package/dist/memory-Cm0eevCS.d.mts +38 -0
- package/dist/memory-DiP1a-pp.d.cts +38 -0
- package/dist/memory-SdN5vtG9.mjs +104 -0
- package/dist/memory-SdN5vtG9.mjs.map +1 -0
- package/dist/server/hono.cjs +180 -0
- package/dist/server/hono.cjs.map +1 -0
- package/dist/server/hono.d.cts +26 -0
- package/dist/server/hono.d.mts +26 -0
- package/dist/server/hono.mjs +176 -0
- package/dist/server/hono.mjs.map +1 -0
- package/dist/storage/kysely.cjs +336 -0
- package/dist/storage/kysely.cjs.map +1 -0
- package/dist/storage/kysely.d.cts +161 -0
- package/dist/storage/kysely.d.mts +161 -0
- package/dist/storage/kysely.mjs +334 -0
- package/dist/storage/kysely.mjs.map +1 -0
- package/dist/storage/memory.cjs +3 -0
- package/dist/storage/memory.d.cts +3 -0
- package/dist/storage/memory.d.mts +3 -0
- package/dist/storage/memory.mjs +3 -0
- package/dist/types-BGDhFv4R.d.cts +170 -0
- package/dist/types-CZbzz8kx.d.mts +170 -0
- package/dist/types.cjs +0 -0
- package/dist/types.d.cts +2 -0
- package/dist/types.d.mts +2 -0
- package/dist/types.mjs +0 -0
- package/dist/ui-assets-D6-8TAr_.mjs +30 -0
- package/dist/ui-assets-D6-8TAr_.mjs.map +1 -0
- package/dist/ui-assets-ulevVble.cjs +48 -0
- package/dist/ui-assets-ulevVble.cjs.map +1 -0
- package/dist/ui-assets.cjs +5 -0
- package/dist/ui-assets.d.cts +12 -0
- package/dist/ui-assets.d.mts +12 -0
- package/dist/ui-assets.mjs +3 -0
- package/package.json +83 -0
- package/scripts/embed-ui.ts +90 -0
- package/src/Telescope.ts +714 -0
- package/src/__tests__/Telescope.spec.ts +356 -0
- package/src/index.ts +23 -0
- package/src/logger/__tests__/console.spec.ts +266 -0
- package/src/logger/__tests__/pino.spec.ts +217 -0
- package/src/logger/console.ts +230 -0
- package/src/logger/pino.ts +191 -0
- package/src/server/__tests__/hono.spec.ts +340 -0
- package/src/server/hono.ts +247 -0
- package/src/storage/__tests__/kysely.spec.ts +715 -0
- package/src/storage/__tests__/memory.spec.ts +411 -0
- package/src/storage/kysely.ts +572 -0
- package/src/storage/memory.ts +168 -0
- package/src/types.ts +188 -0
- package/src/ui-assets.ts +40 -0
- package/ui/index.html +12 -0
- package/ui/node_modules/.bin/browserslist +21 -0
- package/ui/node_modules/.bin/jiti +21 -0
- package/ui/node_modules/.bin/terser +21 -0
- package/ui/node_modules/.bin/tsc +21 -0
- package/ui/node_modules/.bin/tsserver +21 -0
- package/ui/node_modules/.bin/tsx +21 -0
- package/ui/node_modules/.bin/vite +21 -0
- package/ui/package.json +24 -0
- package/ui/src/App.tsx +342 -0
- package/ui/src/api.ts +75 -0
- package/ui/src/components/ExceptionDetail.tsx +100 -0
- package/ui/src/components/LogDetail.tsx +91 -0
- package/ui/src/components/RequestDetail.tsx +143 -0
- package/ui/src/main.tsx +10 -0
- package/ui/src/styles.css +10 -0
- package/ui/src/types.ts +63 -0
- package/ui/src/vite-env.d.ts +1 -0
- package/ui/src/vite-plugin-gkm-config.ts +54 -0
- package/ui/tsconfig.json +20 -0
- package/ui/tsconfig.tsbuildinfo +14 -0
- package/ui/vite.config.ts +13 -0
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import type { ExceptionEntry } from '../types';
|
|
2
|
+
|
|
3
|
+
interface ExceptionDetailProps {
|
|
4
|
+
exception: ExceptionEntry;
|
|
5
|
+
onClose: () => void;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function ExceptionDetail({ exception, onClose }: ExceptionDetailProps) {
|
|
9
|
+
return (
|
|
10
|
+
<div className="fixed top-0 right-0 bottom-0 w-1/2 max-w-3xl bg-bg-secondary border-l border-border flex flex-col z-50 shadow-2xl">
|
|
11
|
+
<div className="flex items-center justify-between p-4 border-b border-border">
|
|
12
|
+
<h2 className="text-base font-semibold text-red-400">
|
|
13
|
+
{exception.name}
|
|
14
|
+
</h2>
|
|
15
|
+
<button
|
|
16
|
+
className="text-slate-400 hover:text-slate-100 p-2 text-xl leading-none"
|
|
17
|
+
onClick={onClose}
|
|
18
|
+
>
|
|
19
|
+
×
|
|
20
|
+
</button>
|
|
21
|
+
</div>
|
|
22
|
+
|
|
23
|
+
<div className="flex-1 overflow-y-auto p-4">
|
|
24
|
+
<section className="mb-6">
|
|
25
|
+
<h3 className="text-xs font-semibold uppercase text-slate-500 mb-2">
|
|
26
|
+
Overview
|
|
27
|
+
</h3>
|
|
28
|
+
<div className="space-y-2 text-sm">
|
|
29
|
+
<div className="flex py-2 border-b border-border">
|
|
30
|
+
<span className="text-slate-500 min-w-32">Message</span>
|
|
31
|
+
<span className="wrap-break-word">{exception.message}</span>
|
|
32
|
+
</div>
|
|
33
|
+
<div className="flex py-2 border-b border-border">
|
|
34
|
+
<span className="text-slate-500 min-w-32">Handled</span>
|
|
35
|
+
<span>{exception.handled ? 'Yes' : 'No'}</span>
|
|
36
|
+
</div>
|
|
37
|
+
<div className="flex py-2 border-b border-border">
|
|
38
|
+
<span className="text-slate-500 min-w-32">Timestamp</span>
|
|
39
|
+
<span>{new Date(exception.timestamp).toLocaleString()}</span>
|
|
40
|
+
</div>
|
|
41
|
+
{exception.requestId && (
|
|
42
|
+
<div className="flex py-2 border-b border-border">
|
|
43
|
+
<span className="text-slate-500 min-w-32">Request ID</span>
|
|
44
|
+
<span className="font-mono text-xs">{exception.requestId}</span>
|
|
45
|
+
</div>
|
|
46
|
+
)}
|
|
47
|
+
{exception.tags && exception.tags.length > 0 && (
|
|
48
|
+
<div className="flex py-2">
|
|
49
|
+
<span className="text-slate-500 min-w-32">Tags</span>
|
|
50
|
+
<span>{exception.tags.join(', ')}</span>
|
|
51
|
+
</div>
|
|
52
|
+
)}
|
|
53
|
+
</div>
|
|
54
|
+
</section>
|
|
55
|
+
|
|
56
|
+
{exception.source && (
|
|
57
|
+
<section className="mb-6">
|
|
58
|
+
<h3 className="text-xs font-semibold uppercase text-slate-500 mb-2">
|
|
59
|
+
Source
|
|
60
|
+
</h3>
|
|
61
|
+
<div className="text-sm mb-2">
|
|
62
|
+
<span className="text-slate-500">File: </span>
|
|
63
|
+
<span>
|
|
64
|
+
{exception.source.file}
|
|
65
|
+
{exception.source.line && `:${exception.source.line}`}
|
|
66
|
+
</span>
|
|
67
|
+
</div>
|
|
68
|
+
{exception.source.code && (
|
|
69
|
+
<pre className="bg-bg-primary border border-border rounded-lg p-4 overflow-x-auto text-xs leading-relaxed">
|
|
70
|
+
{exception.source.code}
|
|
71
|
+
</pre>
|
|
72
|
+
)}
|
|
73
|
+
</section>
|
|
74
|
+
)}
|
|
75
|
+
|
|
76
|
+
<section className="mb-6">
|
|
77
|
+
<h3 className="text-xs font-semibold uppercase text-slate-500 mb-2">
|
|
78
|
+
Stack Trace
|
|
79
|
+
</h3>
|
|
80
|
+
<div className="text-xs leading-relaxed space-y-1">
|
|
81
|
+
{exception.stack.map((frame, index) => (
|
|
82
|
+
<div key={index} className="text-slate-400">
|
|
83
|
+
<span className="text-blue-400">
|
|
84
|
+
{frame.function || '<anonymous>'}
|
|
85
|
+
</span>
|
|
86
|
+
{frame.file && (
|
|
87
|
+
<span className="text-slate-500 ml-2">
|
|
88
|
+
at {frame.file}
|
|
89
|
+
{frame.line !== undefined && `:${frame.line}`}
|
|
90
|
+
{frame.column !== undefined && `:${frame.column}`}
|
|
91
|
+
</span>
|
|
92
|
+
)}
|
|
93
|
+
</div>
|
|
94
|
+
))}
|
|
95
|
+
</div>
|
|
96
|
+
</section>
|
|
97
|
+
</div>
|
|
98
|
+
</div>
|
|
99
|
+
);
|
|
100
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import type { LogEntry } from '../types';
|
|
2
|
+
|
|
3
|
+
interface LogDetailProps {
|
|
4
|
+
log: LogEntry;
|
|
5
|
+
onClose: () => void;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function LogDetail({ log, onClose }: LogDetailProps) {
|
|
9
|
+
const formatJson = (data: unknown) => {
|
|
10
|
+
try {
|
|
11
|
+
return JSON.stringify(data, null, 2);
|
|
12
|
+
} catch {
|
|
13
|
+
return String(data);
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const getLogLevelColor = (level: string) => {
|
|
18
|
+
const colors: Record<string, string> = {
|
|
19
|
+
debug: 'bg-slate-500/20 text-slate-400',
|
|
20
|
+
info: 'bg-blue-500/20 text-blue-400',
|
|
21
|
+
warn: 'bg-amber-500/20 text-amber-400',
|
|
22
|
+
error: 'bg-red-500/20 text-red-400',
|
|
23
|
+
};
|
|
24
|
+
return colors[level] || 'bg-slate-500/20 text-slate-400';
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
<div className="fixed top-0 right-0 bottom-0 w-1/2 max-w-3xl bg-bg-secondary border-l border-border flex flex-col z-50 shadow-2xl">
|
|
29
|
+
<div className="flex items-center justify-between p-4 border-b border-border">
|
|
30
|
+
<h2 className="text-base font-semibold flex items-center gap-2">
|
|
31
|
+
<span
|
|
32
|
+
className={`px-2 py-1 rounded text-xs ${getLogLevelColor(log.level)}`}
|
|
33
|
+
>
|
|
34
|
+
{log.level}
|
|
35
|
+
</span>
|
|
36
|
+
Log Entry
|
|
37
|
+
</h2>
|
|
38
|
+
<button
|
|
39
|
+
className="text-slate-400 hover:text-slate-100 p-2 text-xl leading-none"
|
|
40
|
+
onClick={onClose}
|
|
41
|
+
>
|
|
42
|
+
×
|
|
43
|
+
</button>
|
|
44
|
+
</div>
|
|
45
|
+
|
|
46
|
+
<div className="flex-1 overflow-y-auto p-4">
|
|
47
|
+
<section className="mb-6">
|
|
48
|
+
<h3 className="text-xs font-semibold uppercase text-slate-500 mb-2">
|
|
49
|
+
Overview
|
|
50
|
+
</h3>
|
|
51
|
+
<div className="space-y-2 text-sm">
|
|
52
|
+
<div className="flex py-2 border-b border-border">
|
|
53
|
+
<span className="text-slate-500 min-w-32">Level</span>
|
|
54
|
+
<span>{log.level}</span>
|
|
55
|
+
</div>
|
|
56
|
+
<div className="flex py-2 border-b border-border">
|
|
57
|
+
<span className="text-slate-500 min-w-32">Timestamp</span>
|
|
58
|
+
<span>{new Date(log.timestamp).toLocaleString()}</span>
|
|
59
|
+
</div>
|
|
60
|
+
{log.requestId && (
|
|
61
|
+
<div className="flex py-2">
|
|
62
|
+
<span className="text-slate-500 min-w-32">Request ID</span>
|
|
63
|
+
<span className="font-mono text-xs">{log.requestId}</span>
|
|
64
|
+
</div>
|
|
65
|
+
)}
|
|
66
|
+
</div>
|
|
67
|
+
</section>
|
|
68
|
+
|
|
69
|
+
<section className="mb-6">
|
|
70
|
+
<h3 className="text-xs font-semibold uppercase text-slate-500 mb-2">
|
|
71
|
+
Message
|
|
72
|
+
</h3>
|
|
73
|
+
<pre className="bg-bg-primary border border-border rounded-lg p-4 overflow-x-auto text-xs leading-relaxed whitespace-pre-wrap">
|
|
74
|
+
{log.message}
|
|
75
|
+
</pre>
|
|
76
|
+
</section>
|
|
77
|
+
|
|
78
|
+
{log.context && Object.keys(log.context).length > 0 && (
|
|
79
|
+
<section className="mb-6">
|
|
80
|
+
<h3 className="text-xs font-semibold uppercase text-slate-500 mb-2">
|
|
81
|
+
Context
|
|
82
|
+
</h3>
|
|
83
|
+
<pre className="bg-bg-primary border border-border rounded-lg p-4 overflow-x-auto text-xs leading-relaxed">
|
|
84
|
+
{formatJson(log.context)}
|
|
85
|
+
</pre>
|
|
86
|
+
</section>
|
|
87
|
+
)}
|
|
88
|
+
</div>
|
|
89
|
+
</div>
|
|
90
|
+
);
|
|
91
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import type { RequestEntry } from '../types';
|
|
2
|
+
|
|
3
|
+
interface RequestDetailProps {
|
|
4
|
+
request: RequestEntry;
|
|
5
|
+
onClose: () => void;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function RequestDetail({ request, onClose }: RequestDetailProps) {
|
|
9
|
+
const formatJson = (data: unknown) => {
|
|
10
|
+
try {
|
|
11
|
+
return JSON.stringify(data, null, 2);
|
|
12
|
+
} catch {
|
|
13
|
+
return String(data);
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const getMethodColor = (method: string) => {
|
|
18
|
+
const colors: Record<string, string> = {
|
|
19
|
+
GET: 'bg-green-500/20 text-green-400',
|
|
20
|
+
POST: 'bg-blue-500/20 text-blue-400',
|
|
21
|
+
PUT: 'bg-amber-500/20 text-amber-400',
|
|
22
|
+
PATCH: 'bg-purple-500/20 text-purple-400',
|
|
23
|
+
DELETE: 'bg-red-500/20 text-red-400',
|
|
24
|
+
};
|
|
25
|
+
return colors[method] || 'bg-slate-500/20 text-slate-400';
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<div className="fixed top-0 right-0 bottom-0 w-1/2 max-w-3xl bg-bg-secondary border-l border-border flex flex-col z-50 shadow-2xl">
|
|
30
|
+
<div className="flex items-center justify-between p-4 border-b border-border">
|
|
31
|
+
<h2 className="text-base font-semibold flex items-center gap-2">
|
|
32
|
+
<span
|
|
33
|
+
className={`px-2 py-1 rounded text-xs ${getMethodColor(request.method)}`}
|
|
34
|
+
>
|
|
35
|
+
{request.method}
|
|
36
|
+
</span>
|
|
37
|
+
<span className="truncate">{request.path}</span>
|
|
38
|
+
</h2>
|
|
39
|
+
<button
|
|
40
|
+
className="text-slate-400 hover:text-slate-100 p-2 text-xl leading-none"
|
|
41
|
+
onClick={onClose}
|
|
42
|
+
>
|
|
43
|
+
×
|
|
44
|
+
</button>
|
|
45
|
+
</div>
|
|
46
|
+
|
|
47
|
+
<div className="flex-1 overflow-y-auto p-4">
|
|
48
|
+
<section className="mb-6">
|
|
49
|
+
<h3 className="text-xs font-semibold uppercase text-slate-500 mb-2">
|
|
50
|
+
Overview
|
|
51
|
+
</h3>
|
|
52
|
+
<div className="space-y-2 text-sm">
|
|
53
|
+
<div className="flex py-2 border-b border-border">
|
|
54
|
+
<span className="text-slate-500 min-w-32">Status</span>
|
|
55
|
+
<span>{request.status}</span>
|
|
56
|
+
</div>
|
|
57
|
+
<div className="flex py-2 border-b border-border">
|
|
58
|
+
<span className="text-slate-500 min-w-32">Duration</span>
|
|
59
|
+
<span>{request.duration.toFixed(2)}ms</span>
|
|
60
|
+
</div>
|
|
61
|
+
<div className="flex py-2 border-b border-border">
|
|
62
|
+
<span className="text-slate-500 min-w-32">URL</span>
|
|
63
|
+
<span className="break-all">{request.url}</span>
|
|
64
|
+
</div>
|
|
65
|
+
<div className="flex py-2 border-b border-border">
|
|
66
|
+
<span className="text-slate-500 min-w-32">Timestamp</span>
|
|
67
|
+
<span>{new Date(request.timestamp).toLocaleString()}</span>
|
|
68
|
+
</div>
|
|
69
|
+
{request.ip && (
|
|
70
|
+
<div className="flex py-2 border-b border-border">
|
|
71
|
+
<span className="text-slate-500 min-w-32">IP</span>
|
|
72
|
+
<span>{request.ip}</span>
|
|
73
|
+
</div>
|
|
74
|
+
)}
|
|
75
|
+
{request.userId && (
|
|
76
|
+
<div className="flex py-2 border-b border-border">
|
|
77
|
+
<span className="text-slate-500 min-w-32">User ID</span>
|
|
78
|
+
<span>{request.userId}</span>
|
|
79
|
+
</div>
|
|
80
|
+
)}
|
|
81
|
+
{request.tags && request.tags.length > 0 && (
|
|
82
|
+
<div className="flex py-2">
|
|
83
|
+
<span className="text-slate-500 min-w-32">Tags</span>
|
|
84
|
+
<span>{request.tags.join(', ')}</span>
|
|
85
|
+
</div>
|
|
86
|
+
)}
|
|
87
|
+
</div>
|
|
88
|
+
</section>
|
|
89
|
+
|
|
90
|
+
<section className="mb-6">
|
|
91
|
+
<h3 className="text-xs font-semibold uppercase text-slate-500 mb-2">
|
|
92
|
+
Request Headers
|
|
93
|
+
</h3>
|
|
94
|
+
<pre className="bg-bg-primary border border-border rounded-lg p-4 overflow-x-auto text-xs leading-relaxed">
|
|
95
|
+
{formatJson(request.headers)}
|
|
96
|
+
</pre>
|
|
97
|
+
</section>
|
|
98
|
+
|
|
99
|
+
{request.query && Object.keys(request.query).length > 0 && (
|
|
100
|
+
<section className="mb-6">
|
|
101
|
+
<h3 className="text-xs font-semibold uppercase text-slate-500 mb-2">
|
|
102
|
+
Query Parameters
|
|
103
|
+
</h3>
|
|
104
|
+
<pre className="bg-bg-primary border border-border rounded-lg p-4 overflow-x-auto text-xs leading-relaxed">
|
|
105
|
+
{formatJson(request.query)}
|
|
106
|
+
</pre>
|
|
107
|
+
</section>
|
|
108
|
+
)}
|
|
109
|
+
|
|
110
|
+
{request.body !== undefined && (
|
|
111
|
+
<section className="mb-6">
|
|
112
|
+
<h3 className="text-xs font-semibold uppercase text-slate-500 mb-2">
|
|
113
|
+
Request Body
|
|
114
|
+
</h3>
|
|
115
|
+
<pre className="bg-bg-primary border border-border rounded-lg p-4 overflow-x-auto text-xs leading-relaxed">
|
|
116
|
+
{formatJson(request.body)}
|
|
117
|
+
</pre>
|
|
118
|
+
</section>
|
|
119
|
+
)}
|
|
120
|
+
|
|
121
|
+
<section className="mb-6">
|
|
122
|
+
<h3 className="text-xs font-semibold uppercase text-slate-500 mb-2">
|
|
123
|
+
Response Headers
|
|
124
|
+
</h3>
|
|
125
|
+
<pre className="bg-bg-primary border border-border rounded-lg p-4 overflow-x-auto text-xs leading-relaxed">
|
|
126
|
+
{formatJson(request.responseHeaders)}
|
|
127
|
+
</pre>
|
|
128
|
+
</section>
|
|
129
|
+
|
|
130
|
+
{request.responseBody !== undefined && (
|
|
131
|
+
<section className="mb-6">
|
|
132
|
+
<h3 className="text-xs font-semibold uppercase text-slate-500 mb-2">
|
|
133
|
+
Response Body
|
|
134
|
+
</h3>
|
|
135
|
+
<pre className="bg-bg-primary border border-border rounded-lg p-4 overflow-x-auto text-xs leading-relaxed">
|
|
136
|
+
{formatJson(request.responseBody)}
|
|
137
|
+
</pre>
|
|
138
|
+
</section>
|
|
139
|
+
)}
|
|
140
|
+
</div>
|
|
141
|
+
</div>
|
|
142
|
+
);
|
|
143
|
+
}
|
package/ui/src/main.tsx
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
@import "tailwindcss";
|
|
2
|
+
|
|
3
|
+
@theme {
|
|
4
|
+
--color-bg-primary: #0f172a;
|
|
5
|
+
--color-bg-secondary: #1e293b;
|
|
6
|
+
--color-bg-tertiary: #334155;
|
|
7
|
+
--color-border: #334155;
|
|
8
|
+
--font-family-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas,
|
|
9
|
+
"Liberation Mono", monospace;
|
|
10
|
+
}
|
package/ui/src/types.ts
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
export interface RequestEntry {
|
|
2
|
+
id: string;
|
|
3
|
+
method: string;
|
|
4
|
+
path: string;
|
|
5
|
+
url: string;
|
|
6
|
+
headers: Record<string, string>;
|
|
7
|
+
body?: unknown;
|
|
8
|
+
query?: Record<string, string>;
|
|
9
|
+
status: number;
|
|
10
|
+
responseHeaders: Record<string, string>;
|
|
11
|
+
responseBody?: unknown;
|
|
12
|
+
duration: number;
|
|
13
|
+
timestamp: string;
|
|
14
|
+
ip?: string;
|
|
15
|
+
userId?: string;
|
|
16
|
+
tags?: string[];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface ExceptionEntry {
|
|
20
|
+
id: string;
|
|
21
|
+
name: string;
|
|
22
|
+
message: string;
|
|
23
|
+
stack: Array<{
|
|
24
|
+
file?: string;
|
|
25
|
+
line?: number;
|
|
26
|
+
column?: number;
|
|
27
|
+
function?: string;
|
|
28
|
+
}>;
|
|
29
|
+
source?: {
|
|
30
|
+
file?: string;
|
|
31
|
+
line?: number;
|
|
32
|
+
code?: string;
|
|
33
|
+
};
|
|
34
|
+
requestId?: string;
|
|
35
|
+
timestamp: string;
|
|
36
|
+
handled: boolean;
|
|
37
|
+
tags?: string[];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface LogEntry {
|
|
41
|
+
id: string;
|
|
42
|
+
level: 'debug' | 'info' | 'warn' | 'error';
|
|
43
|
+
message: string;
|
|
44
|
+
context?: Record<string, unknown>;
|
|
45
|
+
requestId?: string;
|
|
46
|
+
timestamp: string;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface TelescopeStats {
|
|
50
|
+
requests: number;
|
|
51
|
+
exceptions: number;
|
|
52
|
+
logs: number;
|
|
53
|
+
oldestEntry?: string;
|
|
54
|
+
newestEntry?: string;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export type Tab = 'requests' | 'exceptions' | 'logs';
|
|
58
|
+
|
|
59
|
+
export interface WebSocketMessage {
|
|
60
|
+
type: 'request' | 'exception' | 'log' | 'connected';
|
|
61
|
+
payload: unknown;
|
|
62
|
+
timestamp: number;
|
|
63
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
/// <reference types="vite/client" />
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { existsSync } from 'fs';
|
|
2
|
+
import { resolve } from 'path';
|
|
3
|
+
import type { Plugin } from 'vite';
|
|
4
|
+
|
|
5
|
+
interface GkmConfig {
|
|
6
|
+
server?: boolean | { port?: number };
|
|
7
|
+
telescope?: { port?: number };
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Vite plugin that reads configuration from gkm.config.ts
|
|
12
|
+
* to configure the dev server proxy.
|
|
13
|
+
*/
|
|
14
|
+
export function gkmConfigPlugin(): Plugin {
|
|
15
|
+
let serverPort = 3000;
|
|
16
|
+
|
|
17
|
+
return {
|
|
18
|
+
name: 'gkm-config',
|
|
19
|
+
async config() {
|
|
20
|
+
const cwd = process.cwd();
|
|
21
|
+
const configPath = resolve(cwd, 'gkm.config.ts');
|
|
22
|
+
|
|
23
|
+
if (existsSync(configPath)) {
|
|
24
|
+
try {
|
|
25
|
+
const config = (await import(configPath)).default as GkmConfig;
|
|
26
|
+
|
|
27
|
+
// Check for telescope.port first, then server.port
|
|
28
|
+
if (config.telescope?.port) {
|
|
29
|
+
serverPort = config.telescope.port;
|
|
30
|
+
} else if (typeof config.server === 'object' && config.server.port) {
|
|
31
|
+
serverPort = config.server.port;
|
|
32
|
+
}
|
|
33
|
+
} catch {
|
|
34
|
+
// Silently fall back to default port
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return {
|
|
39
|
+
server: {
|
|
40
|
+
proxy: {
|
|
41
|
+
'/__telescope/api': {
|
|
42
|
+
target: `http://localhost:${serverPort}`,
|
|
43
|
+
changeOrigin: true,
|
|
44
|
+
},
|
|
45
|
+
'/__telescope/ws': {
|
|
46
|
+
target: `ws://localhost:${serverPort}`,
|
|
47
|
+
ws: true,
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
}
|
package/ui/tsconfig.json
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
|
5
|
+
"module": "ESNext",
|
|
6
|
+
"skipLibCheck": true,
|
|
7
|
+
"moduleResolution": "bundler",
|
|
8
|
+
"allowImportingTsExtensions": true,
|
|
9
|
+
"isolatedModules": true,
|
|
10
|
+
"moduleDetection": "force",
|
|
11
|
+
"noEmit": true,
|
|
12
|
+
"jsx": "react-jsx",
|
|
13
|
+
"strict": true,
|
|
14
|
+
"noUnusedLocals": true,
|
|
15
|
+
"noUnusedParameters": true,
|
|
16
|
+
"noFallthroughCasesInSwitch": true,
|
|
17
|
+
"noUncheckedSideEffectImports": true
|
|
18
|
+
},
|
|
19
|
+
"include": ["src"]
|
|
20
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"root": [
|
|
3
|
+
"./src/app.tsx",
|
|
4
|
+
"./src/api.ts",
|
|
5
|
+
"./src/main.tsx",
|
|
6
|
+
"./src/types.ts",
|
|
7
|
+
"./src/vite-env.d.ts",
|
|
8
|
+
"./src/vite-plugin-gkm-config.ts",
|
|
9
|
+
"./src/components/exceptiondetail.tsx",
|
|
10
|
+
"./src/components/logdetail.tsx",
|
|
11
|
+
"./src/components/requestdetail.tsx"
|
|
12
|
+
],
|
|
13
|
+
"version": "5.8.2"
|
|
14
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import tailwindcss from '@tailwindcss/vite';
|
|
2
|
+
import react from '@vitejs/plugin-react';
|
|
3
|
+
import { defineConfig } from 'vite';
|
|
4
|
+
import { gkmConfigPlugin } from './src/vite-plugin-gkm-config';
|
|
5
|
+
|
|
6
|
+
export default defineConfig({
|
|
7
|
+
plugins: [react(), tailwindcss(), gkmConfigPlugin()],
|
|
8
|
+
base: '/__telescope/',
|
|
9
|
+
build: {
|
|
10
|
+
outDir: '../dist/ui',
|
|
11
|
+
emptyOutDir: true,
|
|
12
|
+
},
|
|
13
|
+
});
|