effect-start 0.26.0 → 0.27.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/package.json +4 -2
- package/src/Entity.ts +6 -6
- package/src/FileRouterCodegen.ts +4 -4
- package/src/FileSystem.ts +4 -8
- package/src/RouteHook.ts +1 -1
- package/src/RouteSse.ts +3 -3
- package/src/SqlIntrospect.ts +2 -2
- package/src/Start.ts +102 -2
- package/src/Values.ts +11 -0
- package/src/bun/BunRoute.ts +1 -1
- package/src/bun/BunRuntime.ts +5 -5
- package/src/hyper/HyperHtml.ts +11 -7
- package/src/hyper/jsx.d.ts +1 -1
- package/src/lint/plugin.js +174 -4
- package/src/sql/SqlClient.ts +355 -0
- package/src/sql/bun/index.ts +117 -50
- package/src/sql/index.ts +1 -1
- package/src/sql/libsql/index.ts +91 -77
- package/src/sql/libsql/libsql.d.ts +4 -1
- package/src/sql/mssql/index.ts +141 -108
- package/src/sql/mssql/mssql.d.ts +1 -0
- package/src/testing/TestLogger.ts +4 -4
- package/src/x/tailwind/compile.ts +6 -14
- package/src/console/Console.ts +0 -42
- package/src/console/ConsoleErrors.ts +0 -213
- package/src/console/ConsoleLogger.ts +0 -56
- package/src/console/ConsoleMetrics.ts +0 -72
- package/src/console/ConsoleProcess.ts +0 -59
- package/src/console/ConsoleStore.ts +0 -187
- package/src/console/ConsoleTracer.ts +0 -107
- package/src/console/Simulation.ts +0 -814
- package/src/console/console.html +0 -340
- package/src/console/index.ts +0 -3
- package/src/console/routes/errors/route.tsx +0 -97
- package/src/console/routes/fiberDetail.tsx +0 -54
- package/src/console/routes/fibers/route.tsx +0 -45
- package/src/console/routes/git/route.tsx +0 -64
- package/src/console/routes/layout.tsx +0 -4
- package/src/console/routes/logs/route.tsx +0 -77
- package/src/console/routes/metrics/route.tsx +0 -36
- package/src/console/routes/route.tsx +0 -8
- package/src/console/routes/routes/route.tsx +0 -30
- package/src/console/routes/services/route.tsx +0 -21
- package/src/console/routes/system/route.tsx +0 -43
- package/src/console/routes/traceDetail.tsx +0 -22
- package/src/console/routes/traces/route.tsx +0 -81
- package/src/console/routes/tree.ts +0 -30
- package/src/console/ui/Errors.tsx +0 -76
- package/src/console/ui/Fibers.tsx +0 -321
- package/src/console/ui/Git.tsx +0 -182
- package/src/console/ui/Logs.tsx +0 -46
- package/src/console/ui/Metrics.tsx +0 -78
- package/src/console/ui/Routes.tsx +0 -125
- package/src/console/ui/Services.tsx +0 -273
- package/src/console/ui/Shell.tsx +0 -62
- package/src/console/ui/System.tsx +0 -131
- package/src/console/ui/Traces.tsx +0 -426
- package/src/sql/Sql.ts +0 -51
package/src/console/ui/Git.tsx
DELETED
|
@@ -1,182 +0,0 @@
|
|
|
1
|
-
export interface GitFileEntry {
|
|
2
|
-
readonly status: string
|
|
3
|
-
readonly path: string
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
export interface GitStatus {
|
|
7
|
-
readonly oid: string
|
|
8
|
-
readonly head: string
|
|
9
|
-
readonly upstream: string | undefined
|
|
10
|
-
readonly ahead: number
|
|
11
|
-
readonly behind: number
|
|
12
|
-
readonly tag: string | undefined
|
|
13
|
-
readonly tagDistance: number
|
|
14
|
-
readonly staged: Array<GitFileEntry>
|
|
15
|
-
readonly unstaged: Array<GitFileEntry>
|
|
16
|
-
readonly untracked: Array<string>
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const xyToLabel: Record<string, string> = {
|
|
20
|
-
M: "modified",
|
|
21
|
-
A: "new file",
|
|
22
|
-
D: "deleted",
|
|
23
|
-
R: "renamed",
|
|
24
|
-
C: "copied",
|
|
25
|
-
T: "typechange",
|
|
26
|
-
U: "unmerged",
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
function statusLabel(code: string): string {
|
|
30
|
-
return xyToLabel[code] ?? code
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function gitStatusColor(code: string): string {
|
|
34
|
-
if (code === "D") return "#ef4444"
|
|
35
|
-
if (code === "A") return "#4ade80"
|
|
36
|
-
if (code === "R" || code === "C") return "#60a5fa"
|
|
37
|
-
if (code === "M") return "#fbbf24"
|
|
38
|
-
return "#94a3b8"
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export function parseGitStatus(porcelain: string, tagLine: string): GitStatus {
|
|
42
|
-
const lines = porcelain.split("\n")
|
|
43
|
-
let oid = ""
|
|
44
|
-
let head = ""
|
|
45
|
-
let upstream: string | undefined
|
|
46
|
-
let ahead = 0
|
|
47
|
-
let behind = 0
|
|
48
|
-
const staged: Array<GitFileEntry> = []
|
|
49
|
-
const unstaged: Array<GitFileEntry> = []
|
|
50
|
-
const untracked: Array<string> = []
|
|
51
|
-
|
|
52
|
-
for (const line of lines) {
|
|
53
|
-
if (line.startsWith("# branch.oid ")) {
|
|
54
|
-
oid = line.slice("# branch.oid ".length, "# branch.oid ".length + 7)
|
|
55
|
-
} else if (line.startsWith("# branch.head ")) {
|
|
56
|
-
head = line.slice("# branch.head ".length)
|
|
57
|
-
} else if (line.startsWith("# branch.upstream ")) {
|
|
58
|
-
upstream = line.slice("# branch.upstream ".length)
|
|
59
|
-
} else if (line.startsWith("# branch.ab ")) {
|
|
60
|
-
const parts = line.slice("# branch.ab ".length).split(" ")
|
|
61
|
-
ahead = parseInt(parts[0]?.slice(1) ?? "0", 10)
|
|
62
|
-
behind = parseInt(parts[1]?.slice(1) ?? "0", 10)
|
|
63
|
-
} else if (line.startsWith("1 ") || line.startsWith("2 ")) {
|
|
64
|
-
const xy = line.slice(2, 4)
|
|
65
|
-
const x = xy[0]
|
|
66
|
-
const y = xy[1]
|
|
67
|
-
const parts = line.split("\t")
|
|
68
|
-
const firstParts = parts[0].split(" ")
|
|
69
|
-
const path = parts.length > 1 ? parts[1] : firstParts[firstParts.length - 1]
|
|
70
|
-
if (x !== "." && x !== "?") staged.push({ status: x, path })
|
|
71
|
-
if (y !== "." && y !== "?") unstaged.push({ status: y, path })
|
|
72
|
-
} else if (line.startsWith("u ")) {
|
|
73
|
-
const parts = line.split("\t")
|
|
74
|
-
const path = parts.length > 1 ? parts[1] : line.split(" ").pop()!
|
|
75
|
-
staged.push({ status: "U", path })
|
|
76
|
-
} else if (line.startsWith("? ")) {
|
|
77
|
-
untracked.push(line.slice(2))
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
let tag: string | undefined
|
|
82
|
-
let tagDistance = 0
|
|
83
|
-
const tagParts = tagLine.trim().split("\n")
|
|
84
|
-
if (tagParts[0] && !tagParts[0].startsWith("fatal")) {
|
|
85
|
-
tag = tagParts[0]
|
|
86
|
-
tagDistance = parseInt(tagParts[1] ?? "0", 10)
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
return { oid, head, upstream, ahead, behind, tag, tagDistance, staged, unstaged, untracked }
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
function GitKV({ label, children }: { label: string; children: any }) {
|
|
93
|
-
return (
|
|
94
|
-
<div style="display:flex;gap:8px;padding:3px 0;font-size:13px;font-family:monospace">
|
|
95
|
-
<span style="color:#6b7280;min-width:80px">{label}</span>
|
|
96
|
-
<span style="color:#e5e7eb">{children}</span>
|
|
97
|
-
</div>
|
|
98
|
-
)
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
function GitFileRow({ entry }: { entry: GitFileEntry }) {
|
|
102
|
-
const label = statusLabel(entry.status)
|
|
103
|
-
const color = gitStatusColor(entry.status)
|
|
104
|
-
return (
|
|
105
|
-
<div style="display:flex;gap:12px;padding:2px 0;font-size:13px;font-family:monospace">
|
|
106
|
-
<span style={`color:${color};font-style:italic;min-width:80px`}>{label}</span>
|
|
107
|
-
<span style="color:#d1d5db">{entry.path}</span>
|
|
108
|
-
</div>
|
|
109
|
-
)
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
function SectionHeader({ title, count }: { title: string; count: number }) {
|
|
113
|
-
return (
|
|
114
|
-
<div style="color:#fbbf24;font-size:14px;font-weight:600;padding:16px 0 6px;font-family:monospace">
|
|
115
|
-
{title} ({count})
|
|
116
|
-
</div>
|
|
117
|
-
)
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
export function GitStatusView({ status }: { status: GitStatus }) {
|
|
121
|
-
return (
|
|
122
|
-
<div style="padding:12px 16px">
|
|
123
|
-
<GitKV label="Head:">
|
|
124
|
-
<span style="color:#94a3b8;font-style:italic">{status.oid}</span>{" "}
|
|
125
|
-
<span style="color:#4ade80;font-weight:600">{status.head}</span>
|
|
126
|
-
</GitKV>
|
|
127
|
-
{status.upstream && (
|
|
128
|
-
<GitKV label="Upstream:">
|
|
129
|
-
<span style="color:#94a3b8;font-style:italic">{status.oid}</span>{" "}
|
|
130
|
-
<span style="color:#60a5fa;font-weight:600">{status.upstream}</span>
|
|
131
|
-
{(status.ahead > 0 || status.behind > 0) && (
|
|
132
|
-
<span style="color:#94a3b8">
|
|
133
|
-
{status.ahead > 0 ? ` +${status.ahead}` : ""}
|
|
134
|
-
{status.behind > 0 ? ` -${status.behind}` : ""}
|
|
135
|
-
</span>
|
|
136
|
-
)}
|
|
137
|
-
</GitKV>
|
|
138
|
-
)}
|
|
139
|
-
{status.tag && (
|
|
140
|
-
<GitKV label="Tag:">
|
|
141
|
-
<span style="color:#e5e7eb">{status.tag}</span>
|
|
142
|
-
{status.tagDistance > 0 && <span style="color:#60a5fa"> ({status.tagDistance})</span>}
|
|
143
|
-
</GitKV>
|
|
144
|
-
)}
|
|
145
|
-
|
|
146
|
-
{status.untracked.length > 0 && (
|
|
147
|
-
<>
|
|
148
|
-
<SectionHeader title="Untracked files" count={status.untracked.length} />
|
|
149
|
-
{status.untracked.map((f) => (
|
|
150
|
-
<div style="padding:2px 0;font-size:13px;font-family:monospace;color:#d1d5db">{f}</div>
|
|
151
|
-
))}
|
|
152
|
-
</>
|
|
153
|
-
)}
|
|
154
|
-
|
|
155
|
-
{status.unstaged.length > 0 && (
|
|
156
|
-
<>
|
|
157
|
-
<SectionHeader title="Unstaged changes" count={status.unstaged.length} />
|
|
158
|
-
{status.unstaged.map((e) => (
|
|
159
|
-
<GitFileRow entry={e} />
|
|
160
|
-
))}
|
|
161
|
-
</>
|
|
162
|
-
)}
|
|
163
|
-
|
|
164
|
-
{status.staged.length > 0 && (
|
|
165
|
-
<>
|
|
166
|
-
<SectionHeader title="Staged changes" count={status.staged.length} />
|
|
167
|
-
{status.staged.map((e) => (
|
|
168
|
-
<GitFileRow entry={e} />
|
|
169
|
-
))}
|
|
170
|
-
</>
|
|
171
|
-
)}
|
|
172
|
-
|
|
173
|
-
{status.untracked.length === 0 &&
|
|
174
|
-
status.unstaged.length === 0 &&
|
|
175
|
-
status.staged.length === 0 && (
|
|
176
|
-
<div style="color:#4ade80;font-size:13px;font-family:monospace;padding:16px 0">
|
|
177
|
-
Clean working tree
|
|
178
|
-
</div>
|
|
179
|
-
)}
|
|
180
|
-
</div>
|
|
181
|
-
)
|
|
182
|
-
}
|
package/src/console/ui/Logs.tsx
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import * as ConsoleStore from "../ConsoleStore.ts"
|
|
2
|
-
|
|
3
|
-
function levelColor(level: string): string {
|
|
4
|
-
if (level === "DEBUG") return "#94a3b8"
|
|
5
|
-
if (level === "INFO") return "#60a5fa"
|
|
6
|
-
if (level === "WARNING") return "#fbbf24"
|
|
7
|
-
if (level === "ERROR") return "#ef4444"
|
|
8
|
-
if (level === "FATAL") return "#dc2626"
|
|
9
|
-
return "#e5e7eb"
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export function LogLine({ log }: { log: ConsoleStore.ConsoleLog }) {
|
|
13
|
-
const color = levelColor(log.level)
|
|
14
|
-
const time = log.date.toLocaleTimeString("en", {
|
|
15
|
-
hour12: false,
|
|
16
|
-
hour: "2-digit",
|
|
17
|
-
minute: "2-digit",
|
|
18
|
-
second: "2-digit",
|
|
19
|
-
})
|
|
20
|
-
|
|
21
|
-
return (
|
|
22
|
-
<div
|
|
23
|
-
id={`log-${log.id}`}
|
|
24
|
-
style="padding:3px 8px;border-bottom:1px solid #1f2937;font-family:monospace;font-size:12px;display:flex;align-items:baseline"
|
|
25
|
-
>
|
|
26
|
-
<span style="color:#6b7280;white-space:nowrap">{time}</span>
|
|
27
|
-
<span style={`color:${color};font-weight:600;width:56px;text-align:center;flex-shrink:0`}>
|
|
28
|
-
{log.level}
|
|
29
|
-
</span>
|
|
30
|
-
<span style="color:#e5e7eb;flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis">
|
|
31
|
-
{log.message}
|
|
32
|
-
</span>
|
|
33
|
-
<a
|
|
34
|
-
href={`${ConsoleStore.store.prefix}/fibers/${log.fiberId.replace("#", "")}`}
|
|
35
|
-
style="color:#6b7280;white-space:nowrap;margin-left:8px;text-decoration:none"
|
|
36
|
-
>
|
|
37
|
-
{log.fiberId}
|
|
38
|
-
</a>
|
|
39
|
-
{log.cause && (
|
|
40
|
-
<div style="color:#ef4444;font-size:11px;padding:2px 0 0 0;white-space:pre-wrap;width:100%">
|
|
41
|
-
{log.cause}
|
|
42
|
-
</div>
|
|
43
|
-
)}
|
|
44
|
-
</div>
|
|
45
|
-
)
|
|
46
|
-
}
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
import type * as ConsoleStore from "../ConsoleStore.ts"
|
|
2
|
-
|
|
3
|
-
function MetricValue({ metric }: { metric: ConsoleStore.ConsoleMetricSnapshot }) {
|
|
4
|
-
if (metric.type === "counter" || metric.type === "gauge") {
|
|
5
|
-
return (
|
|
6
|
-
<div style="font-size:32px;font-weight:700;color:#e5e7eb;font-family:monospace;line-height:1.1">
|
|
7
|
-
{String(metric.value)}
|
|
8
|
-
</div>
|
|
9
|
-
)
|
|
10
|
-
}
|
|
11
|
-
if (metric.type === "histogram") {
|
|
12
|
-
const h = metric.value as { count: number; sum: number; min: number; max: number }
|
|
13
|
-
return (
|
|
14
|
-
<div style="display:grid;grid-template-columns:auto auto;gap:2px 12px;font-size:12px;font-family:monospace">
|
|
15
|
-
<span style="color:#6b7280">count</span>
|
|
16
|
-
<span style="color:#e5e7eb">{h.count}</span>
|
|
17
|
-
<span style="color:#6b7280">sum</span>
|
|
18
|
-
<span style="color:#e5e7eb">{h.sum.toFixed(2)}</span>
|
|
19
|
-
<span style="color:#6b7280">min</span>
|
|
20
|
-
<span style="color:#e5e7eb">{h.min.toFixed(2)}</span>
|
|
21
|
-
<span style="color:#6b7280">max</span>
|
|
22
|
-
<span style="color:#e5e7eb">{h.max.toFixed(2)}</span>
|
|
23
|
-
</div>
|
|
24
|
-
)
|
|
25
|
-
}
|
|
26
|
-
if (metric.type === "frequency") {
|
|
27
|
-
const occ = metric.value as Record<string, number>
|
|
28
|
-
return (
|
|
29
|
-
<div style="display:grid;grid-template-columns:auto auto;gap:2px 12px;font-size:12px;font-family:monospace">
|
|
30
|
-
{Object.entries(occ)
|
|
31
|
-
.slice(0, 10)
|
|
32
|
-
.map(([k, v]) => (
|
|
33
|
-
<>
|
|
34
|
-
<span style="color:#6b7280">{k}</span>
|
|
35
|
-
<span style="color:#e5e7eb">{v}</span>
|
|
36
|
-
</>
|
|
37
|
-
))}
|
|
38
|
-
</div>
|
|
39
|
-
)
|
|
40
|
-
}
|
|
41
|
-
return (
|
|
42
|
-
<pre style="font-size:11px;color:#9ca3af;margin:0;white-space:pre-wrap">
|
|
43
|
-
{JSON.stringify(metric.value, null, 2)}
|
|
44
|
-
</pre>
|
|
45
|
-
)
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function MetricCard({ metric }: { metric: ConsoleStore.ConsoleMetricSnapshot }) {
|
|
49
|
-
return (
|
|
50
|
-
<div style="background:#111827;border:1px solid #374151;border-radius:6px;padding:12px;min-width:200px">
|
|
51
|
-
<div style="display:flex;align-items:center;gap:8px;margin-bottom:8px">
|
|
52
|
-
<span style="color:#d1d5db;font-size:13px;font-weight:600">{metric.name}</span>
|
|
53
|
-
<span style="font-size:10px;padding:1px 6px;border-radius:4px;background:#1e3a5f;color:#60a5fa">
|
|
54
|
-
{metric.type}
|
|
55
|
-
</span>
|
|
56
|
-
</div>
|
|
57
|
-
<MetricValue metric={metric} />
|
|
58
|
-
{metric.tags.length > 0 && (
|
|
59
|
-
<div style="font-size:10px;color:#6b7280;margin-top:4px">
|
|
60
|
-
{metric.tags.map((t) => `${t.key}=${t.value}`).join(" ")}
|
|
61
|
-
</div>
|
|
62
|
-
)}
|
|
63
|
-
</div>
|
|
64
|
-
)
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
export function MetricsGrid({ metrics }: { metrics: Array<ConsoleStore.ConsoleMetricSnapshot> }) {
|
|
68
|
-
if (metrics.length === 0) {
|
|
69
|
-
return <div class="empty">Waiting for metrics...</div>
|
|
70
|
-
}
|
|
71
|
-
return (
|
|
72
|
-
<>
|
|
73
|
-
{metrics.map((m) => (
|
|
74
|
-
<MetricCard metric={m} />
|
|
75
|
-
))}
|
|
76
|
-
</>
|
|
77
|
-
)
|
|
78
|
-
}
|
|
@@ -1,125 +0,0 @@
|
|
|
1
|
-
export interface RouteInfo {
|
|
2
|
-
readonly method: string
|
|
3
|
-
readonly path: string
|
|
4
|
-
readonly format: string | undefined
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
function groupByPath(routes: Array<RouteInfo>): Array<{ path: string; routes: Array<RouteInfo> }> {
|
|
8
|
-
const byPath = new Map<string, Array<RouteInfo>>()
|
|
9
|
-
for (const r of routes) {
|
|
10
|
-
let group = byPath.get(r.path)
|
|
11
|
-
if (!group) {
|
|
12
|
-
group = []
|
|
13
|
-
byPath.set(r.path, group)
|
|
14
|
-
}
|
|
15
|
-
group.push(r)
|
|
16
|
-
}
|
|
17
|
-
return Array.from(byPath, ([path, routes]) => ({
|
|
18
|
-
path,
|
|
19
|
-
routes: routes.sort((a, b) => methodOrder.indexOf(a.method) - methodOrder.indexOf(b.method)),
|
|
20
|
-
})).sort((a, b) => a.path.localeCompare(b.path))
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
function methodColor(method: string): string {
|
|
24
|
-
if (method === "GET") return "#22c55e"
|
|
25
|
-
if (method === "POST") return "#3b82f6"
|
|
26
|
-
if (method === "PUT") return "#f59e0b"
|
|
27
|
-
if (method === "DELETE") return "#ef4444"
|
|
28
|
-
if (method === "PATCH") return "#a855f7"
|
|
29
|
-
if (method === "*") return "#6b7280"
|
|
30
|
-
return "#94a3b8"
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function methodBg(method: string): string {
|
|
34
|
-
if (method === "GET") return "#052e16"
|
|
35
|
-
if (method === "POST") return "#172554"
|
|
36
|
-
if (method === "PUT") return "#422006"
|
|
37
|
-
if (method === "DELETE") return "#450a0a"
|
|
38
|
-
if (method === "PATCH") return "#3b0764"
|
|
39
|
-
return "#1e293b"
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
const methodOrder = ["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS", "*"]
|
|
43
|
-
|
|
44
|
-
function MethodBadge({ method }: { method: string }) {
|
|
45
|
-
return (
|
|
46
|
-
<span
|
|
47
|
-
style={`font-size:10px;font-weight:700;font-family:monospace;padding:2px 6px;border-radius:3px;background:${methodBg(method)};color:${methodColor(method)};min-width:48px;text-align:center;display:inline-block`}
|
|
48
|
-
>
|
|
49
|
-
{method}
|
|
50
|
-
</span>
|
|
51
|
-
)
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
function FormatBadge({ format }: { format: string }) {
|
|
55
|
-
return (
|
|
56
|
-
<span style="font-size:10px;padding:1px 6px;border-radius:3px;background:#1e3a5f;color:#60a5fa">
|
|
57
|
-
{format}
|
|
58
|
-
</span>
|
|
59
|
-
)
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
function ColoredPath({ path }: { path: string }) {
|
|
63
|
-
const segments = path.split("/").filter(Boolean)
|
|
64
|
-
return (
|
|
65
|
-
<span style="font-family:monospace;font-size:13px">
|
|
66
|
-
{segments.length === 0 ? (
|
|
67
|
-
<span style="color:#e2e8f0">/</span>
|
|
68
|
-
) : (
|
|
69
|
-
segments.map((seg) => {
|
|
70
|
-
const isParam = seg.startsWith(":")
|
|
71
|
-
return (
|
|
72
|
-
<>
|
|
73
|
-
<span style="color:#475569">/</span>
|
|
74
|
-
<span style={isParam ? "color:#c084fc" : "color:#e2e8f0"}>{seg}</span>
|
|
75
|
-
</>
|
|
76
|
-
)
|
|
77
|
-
})
|
|
78
|
-
)}
|
|
79
|
-
</span>
|
|
80
|
-
)
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
function PathGroup({ path, routes }: { path: string; routes: Array<RouteInfo> }) {
|
|
84
|
-
return (
|
|
85
|
-
<div style="padding:8px 12px;border-bottom:1px solid #1e293b">
|
|
86
|
-
<div style="margin-bottom:4px">
|
|
87
|
-
<ColoredPath path={path} />
|
|
88
|
-
</div>
|
|
89
|
-
<div style="display:flex;flex-wrap:wrap;gap:4px">
|
|
90
|
-
{routes.map((r) => (
|
|
91
|
-
<div style="display:flex;align-items:center;gap:6px">
|
|
92
|
-
<MethodBadge method={r.method} />
|
|
93
|
-
{r.format && <FormatBadge format={r.format} />}
|
|
94
|
-
</div>
|
|
95
|
-
))}
|
|
96
|
-
</div>
|
|
97
|
-
</div>
|
|
98
|
-
)
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
export function RouteList({ routes }: { routes: Array<RouteInfo> }) {
|
|
102
|
-
if (routes.length === 0) {
|
|
103
|
-
return <div class="empty">No routes registered</div>
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
const groups = groupByPath(routes)
|
|
107
|
-
const routeCount = routes.length
|
|
108
|
-
const pathCount = groups.length
|
|
109
|
-
|
|
110
|
-
return (
|
|
111
|
-
<>
|
|
112
|
-
<div style="padding:8px 12px;border-bottom:1px solid #1e293b;display:flex;gap:16px;font-size:12px;color:#64748b">
|
|
113
|
-
<span>
|
|
114
|
-
{pathCount} path{pathCount !== 1 ? "s" : ""}
|
|
115
|
-
</span>
|
|
116
|
-
<span>
|
|
117
|
-
{routeCount} route{routeCount !== 1 ? "s" : ""}
|
|
118
|
-
</span>
|
|
119
|
-
</div>
|
|
120
|
-
{groups.map((g) => (
|
|
121
|
-
<PathGroup path={g.path} routes={g.routes} />
|
|
122
|
-
))}
|
|
123
|
-
</>
|
|
124
|
-
)
|
|
125
|
-
}
|