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
|
@@ -1,426 +0,0 @@
|
|
|
1
|
-
import * as ConsoleStore from "../ConsoleStore.ts"
|
|
2
|
-
|
|
3
|
-
function formatDuration(ms: number | undefined): string {
|
|
4
|
-
if (ms == null) return "..."
|
|
5
|
-
if (ms < 1) return `${(ms * 1000).toFixed(0)}µs`
|
|
6
|
-
if (ms < 1000) return `${ms.toFixed(1)}ms`
|
|
7
|
-
return `${(ms / 1000).toFixed(2)}s`
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
function statusColor(status: string): string {
|
|
11
|
-
if (status === "ok") return "#22c55e"
|
|
12
|
-
if (status === "error") return "#ef4444"
|
|
13
|
-
return "#eab308"
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
function KeyValue({ label, value }: { label: string; value: string | undefined | null }) {
|
|
17
|
-
if (value == null) return null
|
|
18
|
-
return (
|
|
19
|
-
<div style="display:flex;gap:8px;padding:4px 0;border-bottom:1px solid #1e293b;font-size:12px">
|
|
20
|
-
<span style="color:#64748b;min-width:120px">{label}</span>
|
|
21
|
-
<span style="color:#e2e8f0;font-family:monospace;word-break:break-all">{value}</span>
|
|
22
|
-
</div>
|
|
23
|
-
)
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
function StatusBadge({ status }: { status: string }) {
|
|
27
|
-
const bg = status === "ok" ? "#166534" : status === "error" ? "#7f1d1d" : "#713f12"
|
|
28
|
-
const fg = status === "ok" ? "#4ade80" : status === "error" ? "#fca5a5" : "#fde047"
|
|
29
|
-
return (
|
|
30
|
-
<span style={`font-size:11px;padding:2px 8px;border-radius:4px;background:${bg};color:${fg}`}>
|
|
31
|
-
{status}
|
|
32
|
-
</span>
|
|
33
|
-
)
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// --- Tree building ---
|
|
37
|
-
|
|
38
|
-
interface TreeSpan {
|
|
39
|
-
span: ConsoleStore.ConsoleSpan
|
|
40
|
-
depth: number
|
|
41
|
-
childCount: number
|
|
42
|
-
isLastChild: boolean
|
|
43
|
-
ancestorHasNextSibling: Array<boolean>
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function buildSpanTree(spans: Array<ConsoleStore.ConsoleSpan>): Array<TreeSpan> {
|
|
47
|
-
const byId = new Map<string, ConsoleStore.ConsoleSpan>()
|
|
48
|
-
const childrenOf = new Map<string, Array<ConsoleStore.ConsoleSpan>>()
|
|
49
|
-
|
|
50
|
-
for (const s of spans) {
|
|
51
|
-
byId.set(s.spanId, s)
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
const roots: Array<ConsoleStore.ConsoleSpan> = []
|
|
55
|
-
for (const s of spans) {
|
|
56
|
-
if (s.parentSpanId && byId.has(s.parentSpanId)) {
|
|
57
|
-
let children = childrenOf.get(s.parentSpanId)
|
|
58
|
-
if (!children) {
|
|
59
|
-
children = []
|
|
60
|
-
childrenOf.set(s.parentSpanId, children)
|
|
61
|
-
}
|
|
62
|
-
children.push(s)
|
|
63
|
-
} else {
|
|
64
|
-
roots.push(s)
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
const sortByStart = (a: ConsoleStore.ConsoleSpan, b: ConsoleStore.ConsoleSpan) =>
|
|
69
|
-
Number(a.startTime - b.startTime)
|
|
70
|
-
|
|
71
|
-
roots.sort(sortByStart)
|
|
72
|
-
for (const children of childrenOf.values()) {
|
|
73
|
-
children.sort(sortByStart)
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
const result: Array<TreeSpan> = []
|
|
77
|
-
|
|
78
|
-
function walk(
|
|
79
|
-
span: ConsoleStore.ConsoleSpan,
|
|
80
|
-
depth: number,
|
|
81
|
-
isLast: boolean,
|
|
82
|
-
ancestors: Array<boolean>,
|
|
83
|
-
) {
|
|
84
|
-
const children = childrenOf.get(span.spanId) ?? []
|
|
85
|
-
result.push({
|
|
86
|
-
span,
|
|
87
|
-
depth,
|
|
88
|
-
childCount: children.length,
|
|
89
|
-
isLastChild: isLast,
|
|
90
|
-
ancestorHasNextSibling: [...ancestors],
|
|
91
|
-
})
|
|
92
|
-
for (let i = 0; i < children.length; i++) {
|
|
93
|
-
walk(children[i], depth + 1, i === children.length - 1, [...ancestors, !isLast])
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
for (let i = 0; i < roots.length; i++) {
|
|
98
|
-
walk(roots[i], 0, i === roots.length - 1, [])
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
return result
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// --- Components ---
|
|
105
|
-
|
|
106
|
-
function TreeConnectors({ tree }: { tree: TreeSpan }) {
|
|
107
|
-
if (tree.depth === 0) return null
|
|
108
|
-
|
|
109
|
-
const indent = tree.depth * 20
|
|
110
|
-
const elements: Array<any> = []
|
|
111
|
-
|
|
112
|
-
for (let i = 0; i < tree.ancestorHasNextSibling.length; i++) {
|
|
113
|
-
if (tree.ancestorHasNextSibling[i]) {
|
|
114
|
-
elements.push(<div class="wf-vline" style={`left:${i * 20 + 6}px`} />)
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
if (tree.isLastChild) {
|
|
119
|
-
elements.push(<div class="wf-elbow" style={`left:${(tree.depth - 1) * 20 + 6}px`} />)
|
|
120
|
-
} else {
|
|
121
|
-
elements.push(<div class="wf-vline" style={`left:${(tree.depth - 1) * 20 + 6}px`} />)
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
elements.push(<div class="wf-hline" style={`left:${(tree.depth - 1) * 20 + 6}px;top:50%`} />)
|
|
125
|
-
|
|
126
|
-
return (
|
|
127
|
-
<div class="wf-tree" style={`width:${indent}px;position:relative`}>
|
|
128
|
-
{elements}
|
|
129
|
-
</div>
|
|
130
|
-
)
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
function TimeAxis({ totalMs }: { totalMs: number }) {
|
|
134
|
-
const ticks = 5
|
|
135
|
-
const labels: Array<string> = []
|
|
136
|
-
for (let i = 0; i <= ticks; i++) {
|
|
137
|
-
labels.push(formatDuration((totalMs / ticks) * i))
|
|
138
|
-
}
|
|
139
|
-
return (
|
|
140
|
-
<div class="wf-axis">
|
|
141
|
-
<div style="padding:4px 8px;color:#64748b;font-size:11px">Span</div>
|
|
142
|
-
<div class="wf-axis-ticks">
|
|
143
|
-
{labels.map((l) => (
|
|
144
|
-
<span>{l}</span>
|
|
145
|
-
))}
|
|
146
|
-
</div>
|
|
147
|
-
</div>
|
|
148
|
-
)
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
function WaterfallRow({
|
|
152
|
-
tree,
|
|
153
|
-
totalMs,
|
|
154
|
-
rootStart,
|
|
155
|
-
}: {
|
|
156
|
-
tree: TreeSpan
|
|
157
|
-
totalMs: number
|
|
158
|
-
rootStart: bigint
|
|
159
|
-
}) {
|
|
160
|
-
const s = tree.span
|
|
161
|
-
const offsetMs = Number(s.startTime - rootStart) / 1_000_000
|
|
162
|
-
const durMs = s.durationMs ?? 0
|
|
163
|
-
const leftPct = totalMs > 0 ? Math.min(100, (offsetMs / totalMs) * 100) : 0
|
|
164
|
-
const widthPct =
|
|
165
|
-
totalMs > 0 ? Math.max(0.5, Math.min(100 - leftPct, (durMs / totalMs) * 100)) : 100
|
|
166
|
-
const color = statusColor(s.status)
|
|
167
|
-
|
|
168
|
-
const durLabelLeft = leftPct + widthPct + 0.5
|
|
169
|
-
|
|
170
|
-
return (
|
|
171
|
-
<div class="wf-row">
|
|
172
|
-
<div class="wf-name">
|
|
173
|
-
<TreeConnectors tree={tree} />
|
|
174
|
-
<span style="overflow:hidden;text-overflow:ellipsis">{s.name}</span>
|
|
175
|
-
{tree.childCount > 0 && <span class="wf-badge">{tree.childCount}</span>}
|
|
176
|
-
</div>
|
|
177
|
-
<div class="wf-bar-cell">
|
|
178
|
-
<div class="wf-bar" style={`left:${leftPct}%;width:${widthPct}%;background:${color}`} />
|
|
179
|
-
<div class="wf-dur" style={`left:${durLabelLeft}%`}>
|
|
180
|
-
{formatDuration(s.durationMs)}
|
|
181
|
-
</div>
|
|
182
|
-
</div>
|
|
183
|
-
</div>
|
|
184
|
-
)
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
function MiniWaterfall({
|
|
188
|
-
spans,
|
|
189
|
-
totalMs,
|
|
190
|
-
rootStart,
|
|
191
|
-
}: {
|
|
192
|
-
spans: Array<ConsoleStore.ConsoleSpan>
|
|
193
|
-
totalMs: number
|
|
194
|
-
rootStart: bigint
|
|
195
|
-
}) {
|
|
196
|
-
if (totalMs <= 0) return <div class="mini-wf" />
|
|
197
|
-
return (
|
|
198
|
-
<div class="mini-wf">
|
|
199
|
-
{spans.map((s) => {
|
|
200
|
-
const offsetMs = Number(s.startTime - rootStart) / 1_000_000
|
|
201
|
-
const durMs = s.durationMs ?? 0
|
|
202
|
-
const leftPct = Math.min(100, (offsetMs / totalMs) * 100)
|
|
203
|
-
const widthPct = Math.max(0.3, Math.min(100 - leftPct, (durMs / totalMs) * 100))
|
|
204
|
-
return (
|
|
205
|
-
<div
|
|
206
|
-
class="mini-wf-bar"
|
|
207
|
-
style={`left:${leftPct}%;width:${widthPct}%;background:${statusColor(s.status)}`}
|
|
208
|
-
/>
|
|
209
|
-
)
|
|
210
|
-
})}
|
|
211
|
-
</div>
|
|
212
|
-
)
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
// --- Exports ---
|
|
216
|
-
|
|
217
|
-
export function groupByTraceId(
|
|
218
|
-
spans: Array<ConsoleStore.ConsoleSpan>,
|
|
219
|
-
): Map<string, Array<ConsoleStore.ConsoleSpan>> {
|
|
220
|
-
const groups = new Map<string, Array<ConsoleStore.ConsoleSpan>>()
|
|
221
|
-
for (const span of spans) {
|
|
222
|
-
let group = groups.get(span.traceId)
|
|
223
|
-
if (!group) {
|
|
224
|
-
group = []
|
|
225
|
-
groups.set(span.traceId, group)
|
|
226
|
-
}
|
|
227
|
-
group.push(span)
|
|
228
|
-
}
|
|
229
|
-
return groups
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
export function TraceGroup({ spans }: { spans: Array<ConsoleStore.ConsoleSpan> }) {
|
|
233
|
-
if (spans.length === 0) return null
|
|
234
|
-
const root = spans.find((s) => !s.parentSpanId) ?? spans[0]
|
|
235
|
-
const traceId = root.traceId
|
|
236
|
-
const totalMs = root.durationMs ?? 0
|
|
237
|
-
const rootStart = root.startTime
|
|
238
|
-
const hasError = spans.some((s) => s.status === "error")
|
|
239
|
-
const status = hasError ? "error" : root.status
|
|
240
|
-
|
|
241
|
-
return (
|
|
242
|
-
<details class="tl-row">
|
|
243
|
-
<summary class="tl-summary tl-cols">
|
|
244
|
-
<span class="tl-cell tl-cell-status">
|
|
245
|
-
<span
|
|
246
|
-
style={`width:8px;height:8px;border-radius:50%;background:${statusColor(status)};display:block`}
|
|
247
|
-
/>
|
|
248
|
-
</span>
|
|
249
|
-
<span class="tl-cell tl-cell-name">{root.name}</span>
|
|
250
|
-
<span class="tl-cell tl-cell-spans">{spans.length}</span>
|
|
251
|
-
<span class="tl-cell tl-cell-dur">{formatDuration(totalMs)}</span>
|
|
252
|
-
<span class="tl-cell tl-cell-id">{traceId.slice(0, 12)}</span>
|
|
253
|
-
</summary>
|
|
254
|
-
<div class="tl-body">
|
|
255
|
-
<div style="display:flex;gap:12px;align-items:center;margin-bottom:8px">
|
|
256
|
-
<a
|
|
257
|
-
href={`${ConsoleStore.store.prefix}/traces/${traceId}`}
|
|
258
|
-
style="color:#38bdf8;font-size:12px;text-decoration:none"
|
|
259
|
-
>
|
|
260
|
-
Full trace view
|
|
261
|
-
</a>
|
|
262
|
-
<StatusBadge status={status} />
|
|
263
|
-
<span style="color:#64748b;font-size:11px">
|
|
264
|
-
{spans.length} span{spans.length !== 1 ? "s" : ""}
|
|
265
|
-
</span>
|
|
266
|
-
<span style="color:#64748b;font-size:11px;font-family:monospace">
|
|
267
|
-
{formatDuration(totalMs)}
|
|
268
|
-
</span>
|
|
269
|
-
<span style="color:#475569;font-size:10px;font-family:monospace">{traceId}</span>
|
|
270
|
-
</div>
|
|
271
|
-
{spans.map((s) => {
|
|
272
|
-
const offsetMs = Number(s.startTime - rootStart) / 1_000_000
|
|
273
|
-
const leftPct = totalMs > 0 ? Math.min(100, (offsetMs / totalMs) * 100) : 0
|
|
274
|
-
const widthPct =
|
|
275
|
-
totalMs > 0
|
|
276
|
-
? Math.max(0.5, Math.min(100 - leftPct, ((s.durationMs ?? 0) / totalMs) * 100))
|
|
277
|
-
: 100
|
|
278
|
-
return (
|
|
279
|
-
<div style="display:flex;align-items:center;gap:8px;padding:2px 0;font-size:11px;font-family:monospace">
|
|
280
|
-
<span style={`color:${statusColor(s.status)};min-width:10px`}>
|
|
281
|
-
{s.parentSpanId ? " " : ""}
|
|
282
|
-
</span>
|
|
283
|
-
<span style="color:#d1d5db;min-width:160px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">
|
|
284
|
-
{s.name}
|
|
285
|
-
</span>
|
|
286
|
-
<div style="flex:1;height:10px;background:#1f2937;border-radius:2px;position:relative;overflow:hidden">
|
|
287
|
-
<div
|
|
288
|
-
style={`position:absolute;height:100%;border-radius:2px;background:${statusColor(s.status)};left:${leftPct}%;width:${widthPct}%`}
|
|
289
|
-
/>
|
|
290
|
-
</div>
|
|
291
|
-
<span style="color:#9ca3af;min-width:60px;text-align:right">
|
|
292
|
-
{formatDuration(s.durationMs)}
|
|
293
|
-
</span>
|
|
294
|
-
</div>
|
|
295
|
-
)
|
|
296
|
-
})}
|
|
297
|
-
</div>
|
|
298
|
-
</details>
|
|
299
|
-
)
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
export function TraceGroups({ spans }: { spans: Array<ConsoleStore.ConsoleSpan> }) {
|
|
303
|
-
const groups = groupByTraceId(spans)
|
|
304
|
-
const sorted = Array.from(groups.values())
|
|
305
|
-
.sort((a, b) => Number(b[0].startTime) - Number(a[0].startTime))
|
|
306
|
-
.slice(0, 50)
|
|
307
|
-
|
|
308
|
-
if (sorted.length === 0) {
|
|
309
|
-
return <div class="empty">Waiting for traces...</div>
|
|
310
|
-
}
|
|
311
|
-
return (
|
|
312
|
-
<div class="tl-grid">
|
|
313
|
-
<div class="tl-header tl-cols">
|
|
314
|
-
<span class="tl-cell tl-cell-status" />
|
|
315
|
-
<span class="tl-cell tl-cell-name">Name</span>
|
|
316
|
-
<span class="tl-cell tl-cell-spans">Spans</span>
|
|
317
|
-
<span class="tl-cell tl-cell-dur">Duration</span>
|
|
318
|
-
<span class="tl-cell tl-cell-id">Trace</span>
|
|
319
|
-
</div>
|
|
320
|
-
{sorted.map((group) => (
|
|
321
|
-
<TraceGroup spans={group} />
|
|
322
|
-
))}
|
|
323
|
-
</div>
|
|
324
|
-
)
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
export function TraceDetail({
|
|
328
|
-
prefix,
|
|
329
|
-
spans,
|
|
330
|
-
}: {
|
|
331
|
-
prefix: string
|
|
332
|
-
spans: Array<ConsoleStore.ConsoleSpan>
|
|
333
|
-
}) {
|
|
334
|
-
if (spans.length === 0) {
|
|
335
|
-
return <div class="empty">Trace not found</div>
|
|
336
|
-
}
|
|
337
|
-
const root = spans.find((s) => !s.parentSpanId) ?? spans[0]
|
|
338
|
-
const traceId = root.traceId
|
|
339
|
-
const totalMs = root.durationMs ?? 0
|
|
340
|
-
const rootStart = root.startTime
|
|
341
|
-
const startDate = new Date(Number(rootStart) / 1_000_000)
|
|
342
|
-
const tree = buildSpanTree(spans)
|
|
343
|
-
|
|
344
|
-
return (
|
|
345
|
-
<>
|
|
346
|
-
<div style="padding:12px 16px;border-bottom:1px solid #1e293b">
|
|
347
|
-
<div style="display:flex;align-items:center;gap:8px;margin-bottom:8px">
|
|
348
|
-
<a href={`${prefix}/traces`} style="color:#64748b;text-decoration:none;font-size:12px">
|
|
349
|
-
Traces
|
|
350
|
-
</a>
|
|
351
|
-
<span style="color:#475569">/</span>
|
|
352
|
-
<span style="color:#e2e8f0;font-size:13px;font-family:monospace">{root.name}</span>
|
|
353
|
-
</div>
|
|
354
|
-
<div style="display:flex;gap:16px;font-size:12px;color:#94a3b8;align-items:center">
|
|
355
|
-
<StatusBadge status={root.status} />
|
|
356
|
-
<span>
|
|
357
|
-
{spans.length} span{spans.length !== 1 ? "s" : ""}
|
|
358
|
-
</span>
|
|
359
|
-
<span>{formatDuration(totalMs)}</span>
|
|
360
|
-
<span>{startDate.toLocaleTimeString("en", { hour12: false })}</span>
|
|
361
|
-
<span style="color:#475569;font-family:monospace;font-size:10px">{traceId}</span>
|
|
362
|
-
</div>
|
|
363
|
-
</div>
|
|
364
|
-
|
|
365
|
-
<div style="padding:8px 16px">
|
|
366
|
-
<MiniWaterfall spans={spans} totalMs={totalMs} rootStart={rootStart} />
|
|
367
|
-
</div>
|
|
368
|
-
|
|
369
|
-
<div style="padding:0 8px">
|
|
370
|
-
<TimeAxis totalMs={totalMs} />
|
|
371
|
-
<div class="wf-grid">
|
|
372
|
-
{tree.map((t) => (
|
|
373
|
-
<WaterfallRow tree={t} totalMs={totalMs} rootStart={rootStart} />
|
|
374
|
-
))}
|
|
375
|
-
</div>
|
|
376
|
-
</div>
|
|
377
|
-
|
|
378
|
-
<div style="padding:8px">
|
|
379
|
-
{tree.map((t) => {
|
|
380
|
-
const s = t.span
|
|
381
|
-
const stacktrace = s.attributes["code.stacktrace"] as string | undefined
|
|
382
|
-
const customAttrs = Object.entries(s.attributes).filter(([k]) => k !== "code.stacktrace")
|
|
383
|
-
|
|
384
|
-
return (
|
|
385
|
-
<details class="span-panel" style="margin-bottom:4px">
|
|
386
|
-
<summary class="span-panel-header">
|
|
387
|
-
<span
|
|
388
|
-
style={`width:8px;height:8px;border-radius:50%;background:${statusColor(s.status)};flex-shrink:0`}
|
|
389
|
-
/>
|
|
390
|
-
<span style="color:#e2e8f0;font-family:monospace;font-size:12px;font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1">
|
|
391
|
-
{s.name}
|
|
392
|
-
</span>
|
|
393
|
-
<StatusBadge status={s.status} />
|
|
394
|
-
<span style="color:#64748b;font-size:11px;font-family:monospace;margin-left:auto">
|
|
395
|
-
{formatDuration(s.durationMs)}
|
|
396
|
-
</span>
|
|
397
|
-
</summary>
|
|
398
|
-
<div class="span-panel-body">
|
|
399
|
-
<KeyValue label="Span ID" value={s.spanId} />
|
|
400
|
-
<KeyValue label="Kind" value={s.kind} />
|
|
401
|
-
{s.parentSpanId && <KeyValue label="Parent" value={s.parentSpanId} />}
|
|
402
|
-
{stacktrace && <KeyValue label="Source" value={stacktrace} />}
|
|
403
|
-
{customAttrs.map(([k, v]) => (
|
|
404
|
-
<KeyValue label={k} value={String(v)} />
|
|
405
|
-
))}
|
|
406
|
-
{s.events.length > 0 && (
|
|
407
|
-
<div style="margin-top:4px">
|
|
408
|
-
<span style="color:#64748b;font-size:11px">Events:</span>
|
|
409
|
-
{s.events.map((ev) => (
|
|
410
|
-
<div style="padding:2px 0;font-size:11px;color:#94a3b8;font-family:monospace">
|
|
411
|
-
{ev.name}
|
|
412
|
-
{ev.attributes && (
|
|
413
|
-
<span style="color:#64748b"> {JSON.stringify(ev.attributes)}</span>
|
|
414
|
-
)}
|
|
415
|
-
</div>
|
|
416
|
-
))}
|
|
417
|
-
</div>
|
|
418
|
-
)}
|
|
419
|
-
</div>
|
|
420
|
-
</details>
|
|
421
|
-
)
|
|
422
|
-
})}
|
|
423
|
-
</div>
|
|
424
|
-
</>
|
|
425
|
-
)
|
|
426
|
-
}
|
package/src/sql/Sql.ts
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import * as Context from "effect/Context"
|
|
2
|
-
import * as Data from "effect/Data"
|
|
3
|
-
import type * as Effect from "effect/Effect"
|
|
4
|
-
import type * as Scope from "effect/Scope"
|
|
5
|
-
|
|
6
|
-
export class SqlError extends Data.TaggedError("SqlError")<{
|
|
7
|
-
readonly code: string
|
|
8
|
-
readonly message: string
|
|
9
|
-
readonly cause?: unknown
|
|
10
|
-
}> {}
|
|
11
|
-
|
|
12
|
-
export interface SqlQuery {
|
|
13
|
-
<T = any>(
|
|
14
|
-
strings: TemplateStringsArray,
|
|
15
|
-
...values: Array<unknown>
|
|
16
|
-
): Effect.Effect<ReadonlyArray<T>, SqlError>
|
|
17
|
-
|
|
18
|
-
readonly unsafe: <T = any>(
|
|
19
|
-
query: string,
|
|
20
|
-
values?: Array<unknown>,
|
|
21
|
-
) => Effect.Effect<ReadonlyArray<T>, SqlError>
|
|
22
|
-
|
|
23
|
-
readonly values: {
|
|
24
|
-
<T extends Record<string, unknown>>(obj: T | ReadonlyArray<T>): SqlHelper<T>
|
|
25
|
-
<T extends Record<string, unknown>, K extends keyof T>(
|
|
26
|
-
obj: T | ReadonlyArray<T>,
|
|
27
|
-
...columns: [K, ...Array<K>]
|
|
28
|
-
): SqlHelper<Pick<T, K>>
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export interface SqlClient extends SqlQuery {
|
|
33
|
-
readonly withTransaction: <A, E, R>(
|
|
34
|
-
self: Effect.Effect<A, E, R>,
|
|
35
|
-
) => Effect.Effect<A, SqlError | E, R>
|
|
36
|
-
|
|
37
|
-
readonly reserve: Effect.Effect<SqlQuery, SqlError, Scope.Scope>
|
|
38
|
-
|
|
39
|
-
readonly close: (options?: { readonly timeout?: number }) => Effect.Effect<void, SqlError>
|
|
40
|
-
|
|
41
|
-
readonly use: <T>(fn: (driver: any) => Promise<T> | T) => Effect.Effect<T, SqlError>
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export interface SqlHelper<T> {
|
|
45
|
-
readonly value: ReadonlyArray<T>
|
|
46
|
-
readonly columns: ReadonlyArray<keyof T>
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export const SqlClient: Context.Tag<SqlClient, SqlClient> = Context.GenericTag<SqlClient>(
|
|
50
|
-
"effect-start/Sql/SqlClient",
|
|
51
|
-
)
|