effect-start 0.31.0 → 0.33.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/dist/Entity.d.ts +3 -0
- package/dist/Entity.d.ts.map +1 -1
- package/dist/Entity.js +20 -5
- package/dist/Entity.js.map +1 -1
- package/dist/Fetch.d.ts +1 -1
- package/dist/Fetch.d.ts.map +1 -1
- package/dist/Fetch.js +1 -1
- package/dist/Fetch.js.map +1 -1
- package/dist/Html.d.ts +5 -11
- package/dist/Html.d.ts.map +1 -1
- package/dist/Html.js +21 -1
- package/dist/Html.js.map +1 -1
- package/dist/KeyValueStore.d.ts +37 -0
- package/dist/KeyValueStore.d.ts.map +1 -0
- package/dist/KeyValueStore.js +99 -0
- package/dist/KeyValueStore.js.map +1 -0
- package/dist/Password.d.ts +26 -0
- package/dist/Password.d.ts.map +1 -0
- package/dist/Password.js +83 -0
- package/dist/Password.js.map +1 -0
- package/dist/Route.d.ts +2 -2
- package/dist/Route.d.ts.map +1 -1
- package/dist/Route.js +1 -1
- package/dist/Route.js.map +1 -1
- package/dist/RouteBody.d.ts +2 -2
- package/dist/RouteBody.d.ts.map +1 -1
- package/dist/RouteMount.d.ts +0 -20
- package/dist/RouteMount.d.ts.map +1 -1
- package/dist/RouteMount.js +0 -15
- package/dist/RouteMount.js.map +1 -1
- package/dist/RouteSse.d.ts +1 -2
- package/dist/RouteSse.d.ts.map +1 -1
- package/dist/RouteSse.js +2 -2
- package/dist/RouteSse.js.map +1 -1
- package/dist/Start.d.ts.map +1 -1
- package/dist/Start.js +4 -0
- package/dist/Start.js.map +1 -1
- package/dist/_StreamExtra.d.ts.map +1 -1
- package/dist/_StreamExtra.js +0 -1
- package/dist/_StreamExtra.js.map +1 -1
- package/dist/bun/BunRoute.d.ts.map +1 -1
- package/dist/bun/BunRoute.js +90 -78
- package/dist/bun/BunRoute.js.map +1 -1
- package/dist/bun/BunServer.d.ts +1 -1
- package/dist/bun/BunServer.d.ts.map +1 -1
- package/dist/bun/BunServer.js +8 -1
- package/dist/bun/BunServer.js.map +1 -1
- package/dist/datastar/attributes/computed.js +3 -3
- package/dist/datastar/attributes/computed.js.map +1 -1
- package/dist/datastar/attributes/on.js +11 -36
- package/dist/datastar/attributes/on.js.map +1 -1
- package/dist/datastar/engine.d.ts +9 -7
- package/dist/datastar/engine.d.ts.map +1 -1
- package/dist/datastar/engine.js +45 -29
- package/dist/datastar/engine.js.map +1 -1
- package/dist/datastar/jsx.d.ts +70 -0
- package/dist/datastar/jsx.d.ts.map +1 -0
- package/dist/datastar/jsx.js +2 -0
- package/dist/datastar/jsx.js.map +1 -0
- package/dist/datastar/window.d.ts +8 -0
- package/dist/datastar/window.d.ts.map +1 -0
- package/dist/datastar/window.js +4 -0
- package/dist/datastar/window.js.map +1 -0
- package/dist/experimental/KeyValueStore.d.ts +37 -0
- package/dist/experimental/KeyValueStore.d.ts.map +1 -0
- package/dist/experimental/KeyValueStore.js +99 -0
- package/dist/experimental/KeyValueStore.js.map +1 -0
- package/dist/experimental/SqlCache.d.ts +19 -0
- package/dist/experimental/SqlCache.d.ts.map +1 -0
- package/dist/experimental/SqlCache.js +35 -0
- package/dist/experimental/SqlCache.js.map +1 -0
- package/dist/experimental/SqlIntrospect.d.ts +92 -0
- package/dist/experimental/SqlIntrospect.d.ts.map +1 -0
- package/dist/experimental/SqlIntrospect.js +478 -0
- package/dist/experimental/SqlIntrospect.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/jsx-runtime.d.ts +2 -2
- package/dist/jsx-runtime.d.ts.map +1 -1
- package/dist/jsx.d.ts +3216 -0
- package/dist/jsx.d.ts.map +1 -0
- package/dist/jsx.js +6 -0
- package/dist/jsx.js.map +1 -0
- package/dist/lint/plugin.d.ts +4 -3
- package/dist/lint/plugin.js +56 -17
- package/dist/lint/plugin.js.map +1 -1
- package/dist/sql/index.d.ts +0 -2
- package/dist/sql/index.d.ts.map +1 -1
- package/dist/sql/index.js +0 -2
- package/dist/sql/index.js.map +1 -1
- package/dist/studio/Studio.d.ts +1 -1
- package/dist/studio/Studio.d.ts.map +1 -1
- package/dist/studio/Studio.js +5 -4
- package/dist/studio/Studio.js.map +1 -1
- package/dist/studio/StudioErrors.d.ts.map +1 -1
- package/dist/studio/StudioErrors.js +2 -2
- package/dist/studio/StudioErrors.js.map +1 -1
- package/dist/studio/StudioLogger.js +3 -4
- package/dist/studio/StudioLogger.js.map +1 -1
- package/dist/studio/StudioStore.d.ts +26 -17
- package/dist/studio/StudioStore.d.ts.map +1 -1
- package/dist/studio/StudioStore.js +61 -44
- package/dist/studio/StudioStore.js.map +1 -1
- package/dist/studio/StudioTracer.d.ts.map +1 -1
- package/dist/studio/StudioTracer.js +10 -4
- package/dist/studio/StudioTracer.js.map +1 -1
- package/dist/studio/_Pretty.d.ts +4 -0
- package/dist/studio/_Pretty.d.ts.map +1 -0
- package/dist/studio/_Pretty.js +56 -0
- package/dist/studio/_Pretty.js.map +1 -0
- package/dist/studio/routes/errors/route.d.ts +1 -1
- package/dist/studio/routes/errors/route.d.ts.map +1 -1
- package/dist/studio/routes/errors/route.js +2 -2
- package/dist/studio/routes/errors/route.js.map +1 -1
- package/dist/studio/routes/fiberDetail.d.ts +1 -1
- package/dist/studio/routes/fiberDetail.js +4 -4
- package/dist/studio/routes/fiberDetail.js.map +1 -1
- package/dist/studio/routes/fibers/route.d.ts +2 -2
- package/dist/studio/routes/fibers/route.js +4 -4
- package/dist/studio/routes/fibers/route.js.map +1 -1
- package/dist/studio/routes/layout.d.ts +2 -0
- package/dist/studio/routes/layout.d.ts.map +1 -1
- package/dist/studio/routes/layout.html +3 -12
- package/dist/studio/routes/layout.js +6 -1
- package/dist/studio/routes/layout.js.map +1 -1
- package/dist/studio/routes/logs/route.d.ts +1 -1
- package/dist/studio/routes/logs/route.d.ts.map +1 -1
- package/dist/studio/routes/logs/route.js +2 -2
- package/dist/studio/routes/logs/route.js.map +1 -1
- package/dist/studio/routes/traceDetail.d.ts +1 -1
- package/dist/studio/routes/traceDetail.js +1 -1
- package/dist/studio/routes/traceDetail.js.map +1 -1
- package/dist/studio/routes/traces/route.d.ts +2 -2
- package/dist/studio/routes/traces/route.d.ts.map +1 -1
- package/dist/studio/routes/traces/route.js +9 -6
- package/dist/studio/routes/traces/route.js.map +1 -1
- package/dist/studio/routes/tree.d.ts +30 -8
- package/dist/studio/routes/tree.d.ts.map +1 -1
- package/dist/studio/ui/Errors.d.ts +1 -1
- package/dist/studio/ui/Errors.js +1 -1
- package/dist/studio/ui/Errors.js.map +1 -1
- package/dist/studio/ui/Fibers.d.ts +2 -2
- package/dist/studio/ui/Fibers.d.ts.map +1 -1
- package/dist/studio/ui/Fibers.js +4 -3
- package/dist/studio/ui/Fibers.js.map +1 -1
- package/dist/studio/ui/Logs.d.ts +1 -1
- package/dist/studio/ui/Logs.d.ts.map +1 -1
- package/dist/studio/ui/Logs.js +2 -1
- package/dist/studio/ui/Logs.js.map +1 -1
- package/dist/studio/ui/Metrics.d.ts +1 -1
- package/dist/studio/ui/Routes.d.ts +1 -1
- package/dist/studio/ui/Routes.d.ts.map +1 -1
- package/dist/studio/ui/Services.d.ts +1 -1
- package/dist/studio/ui/Services.d.ts.map +1 -1
- package/dist/studio/ui/Shell.d.ts +2 -2
- package/dist/studio/ui/Shell.d.ts.map +1 -1
- package/dist/studio/ui/System.d.ts +1 -1
- package/dist/studio/ui/Traces.d.ts +4 -3
- package/dist/studio/ui/Traces.d.ts.map +1 -1
- package/dist/studio/ui/Traces.js +36 -21
- package/dist/studio/ui/Traces.js.map +1 -1
- package/dist/studio/ui/_PrettyValue.d.ts +10 -0
- package/dist/studio/ui/_PrettyValue.d.ts.map +1 -0
- package/dist/studio/ui/_PrettyValue.js +27 -0
- package/dist/studio/ui/_PrettyValue.js.map +1 -0
- package/dist/tailwind/TailwindPlugin.d.ts.map +1 -1
- package/dist/tailwind/TailwindPlugin.js +89 -62
- package/dist/tailwind/TailwindPlugin.js.map +1 -1
- package/dist/ts/import-plugin.cjs +388 -0
- package/dist/ts/import-plugin.cjs.map +1 -0
- package/dist/ts/import-plugin.d.cts +87 -0
- package/dist/ts/import-plugin.d.cts.map +1 -0
- package/dist/ts/import-plugin.d.ts +87 -0
- package/dist/ts/import-plugin.d.ts.map +1 -0
- package/dist/ts/import-plugin.js +390 -0
- package/dist/ts/import-plugin.js.map +1 -0
- package/package.json +109 -8
- package/src/Entity.ts +32 -7
- package/src/Fetch.ts +2 -2
- package/src/Html.ts +28 -21
- package/src/Password.ts +130 -0
- package/src/Route.ts +2 -2
- package/src/RouteBody.ts +2 -2
- package/src/RouteMount.ts +0 -54
- package/src/RouteSse.ts +4 -4
- package/src/Start.ts +4 -0
- package/src/_StreamExtra.ts +0 -1
- package/src/bun/BunRoute.ts +117 -95
- package/src/bun/BunServer.ts +9 -2
- package/src/datastar/README.md +24 -8
- package/src/datastar/attributes/computed.ts +3 -3
- package/src/datastar/attributes/on.ts +11 -37
- package/src/datastar/engine.ts +61 -37
- package/src/datastar/jsx.d.ts +12 -26
- package/src/datastar/types.d.ts +8 -0
- package/src/experimental/KeyValueStore.ts +161 -0
- package/src/{sql → experimental}/SqlCache.ts +1 -1
- package/src/{sql → experimental}/SqlIntrospect.ts +1 -1
- package/src/index.ts +1 -0
- package/src/jsx-runtime.ts +1 -1
- package/src/jsx.d.ts +17 -2
- package/src/lint/plugin.js +54 -19
- package/src/sql/index.ts +0 -2
- package/src/studio/Studio.ts +6 -5
- package/src/studio/StudioErrors.ts +2 -3
- package/src/studio/StudioLogger.ts +4 -4
- package/src/studio/StudioStore.ts +177 -115
- package/src/studio/StudioTracer.ts +11 -5
- package/src/studio/_Pretty.ts +59 -0
- package/src/studio/routes/errors/route.tsx +3 -1
- package/src/studio/routes/fiberDetail.tsx +4 -4
- package/src/studio/routes/fibers/route.tsx +4 -4
- package/src/studio/routes/layout.html +3 -12
- package/src/studio/routes/layout.tsx +9 -1
- package/src/studio/routes/logs/route.tsx +3 -1
- package/src/studio/routes/traceDetail.tsx +1 -1
- package/src/studio/routes/traces/route.tsx +13 -6
- package/src/studio/ui/Errors.tsx +1 -1
- package/src/studio/ui/Fibers.tsx +14 -10
- package/src/studio/ui/Logs.tsx +15 -10
- package/src/studio/ui/Traces.tsx +80 -80
- package/src/studio/ui/_PrettyValue.tsx +34 -0
- package/src/tailwind/TailwindPlugin.ts +102 -75
- package/src/RouteTrie.ts +0 -205
- package/src/experimental/index.ts +0 -1
|
@@ -12,7 +12,7 @@ export default Route.get(
|
|
|
12
12
|
const url = new URL(ctx.request.url)
|
|
13
13
|
const search = url.searchParams.get("errorSearch") || ""
|
|
14
14
|
const tag = url.searchParams.get("errorTag") || ""
|
|
15
|
-
const allErrors = yield* StudioStore.allErrors(
|
|
15
|
+
const allErrors = yield* StudioStore.allErrors()
|
|
16
16
|
const tagSet = new Set<string>()
|
|
17
17
|
for (const error of allErrors) {
|
|
18
18
|
for (const d of error.details) {
|
|
@@ -67,6 +67,8 @@ export default Route.get(
|
|
|
67
67
|
<Errors.ErrorLine error={e} />
|
|
68
68
|
))}
|
|
69
69
|
</div>
|
|
70
|
+
|
|
71
|
+
<div data-init={`@get('${prefix}/errors')`} />
|
|
70
72
|
</form>
|
|
71
73
|
</Shell.Shell>
|
|
72
74
|
)
|
|
@@ -12,8 +12,8 @@ export default Route.get(
|
|
|
12
12
|
const fiberId = ctx.pathParams.id
|
|
13
13
|
const fiberName = fiberId.startsWith("#") ? fiberId : `#${fiberId}`
|
|
14
14
|
|
|
15
|
-
const fiberLogs = yield* StudioStore.logsByFiberId(
|
|
16
|
-
const fiberSpans = yield* StudioStore.spansByFiberId(
|
|
15
|
+
const fiberLogs = yield* StudioStore.logsByFiberId(fiberName)
|
|
16
|
+
const fiberSpans = yield* StudioStore.spansByFiberId(fiberName)
|
|
17
17
|
|
|
18
18
|
const fiberNum = parseInt(fiberName.slice(1), 10)
|
|
19
19
|
const counter = StudioStore.fiberIdCounter()
|
|
@@ -30,8 +30,8 @@ export default Route.get(
|
|
|
30
30
|
else if (fiberNum < counter) alive = "dead"
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
const parents = yield* StudioStore.getParentChain(
|
|
34
|
-
const fiberContext = yield* StudioStore.getFiberContext(
|
|
33
|
+
const parents = yield* StudioStore.getParentChain(fiberName)
|
|
34
|
+
const fiberContext = yield* StudioStore.getFiberContext(fiberName)
|
|
35
35
|
|
|
36
36
|
return (
|
|
37
37
|
<Shell.Shell prefix={StudioStore.store.prefix} active="fibers">
|
|
@@ -8,8 +8,8 @@ import * as Shell from "../../ui/Shell.tsx"
|
|
|
8
8
|
|
|
9
9
|
export default Route.get(
|
|
10
10
|
Route.html(function* () {
|
|
11
|
-
const logs = yield* StudioStore.allLogs(
|
|
12
|
-
const spans = yield* StudioStore.allSpans(
|
|
11
|
+
const logs = yield* StudioStore.allLogs()
|
|
12
|
+
const spans = yield* StudioStore.allSpans()
|
|
13
13
|
const fibers = Fibers.collectFibers(logs, spans)
|
|
14
14
|
return (
|
|
15
15
|
<Shell.Shell prefix={StudioStore.store.prefix} active="fibers">
|
|
@@ -28,8 +28,8 @@ export default Route.get(
|
|
|
28
28
|
Stream.filter((e) => e._tag === "SpanStart" || e._tag === "SpanEnd" || e._tag === "Log"),
|
|
29
29
|
Stream.mapEffect(() =>
|
|
30
30
|
Effect.gen(function* () {
|
|
31
|
-
const logs = yield* StudioStore.allLogs(
|
|
32
|
-
const spans = yield* StudioStore.allSpans(
|
|
31
|
+
const logs = yield* StudioStore.allLogs()
|
|
32
|
+
const spans = yield* StudioStore.allSpans()
|
|
33
33
|
const fibers = Fibers.collectFibers(logs, spans)
|
|
34
34
|
const html = Html.renderToString(
|
|
35
35
|
<Fibers.FiberList fibers={fibers} prefix={StudioStore.store.prefix} />,
|
|
@@ -261,21 +261,12 @@
|
|
|
261
261
|
}
|
|
262
262
|
.tl-row {
|
|
263
263
|
border-bottom: 1px solid #1e293b;
|
|
264
|
+
color: inherit;
|
|
265
|
+
text-decoration: none;
|
|
264
266
|
}
|
|
265
|
-
.tl-row
|
|
267
|
+
.tl-row:hover {
|
|
266
268
|
background: #111827;
|
|
267
269
|
}
|
|
268
|
-
.tl-summary {
|
|
269
|
-
cursor: pointer;
|
|
270
|
-
user-select: none;
|
|
271
|
-
list-style: none;
|
|
272
|
-
}
|
|
273
|
-
.tl-summary::-webkit-details-marker {
|
|
274
|
-
display: none;
|
|
275
|
-
}
|
|
276
|
-
.tl-summary:hover {
|
|
277
|
-
background: #1e293b;
|
|
278
|
-
}
|
|
279
270
|
.tl-cell {
|
|
280
271
|
padding: 6px 8px;
|
|
281
272
|
display: flex;
|
|
@@ -1,4 +1,12 @@
|
|
|
1
|
+
import * as Effect from "effect/Effect"
|
|
1
2
|
import * as BunRoute from "../../bun/BunRoute.ts"
|
|
2
3
|
import * as Route from "../../Route.ts"
|
|
4
|
+
import * as StudioStore from "../StudioStore.ts"
|
|
3
5
|
|
|
4
|
-
export default Route.use(
|
|
6
|
+
export default Route.use(
|
|
7
|
+
Route.filter(function* () {
|
|
8
|
+
yield* Effect.annotateCurrentSpan(StudioStore.studioTraceAttribute, true)
|
|
9
|
+
return { context: {} }
|
|
10
|
+
}),
|
|
11
|
+
BunRoute.htmlBundle(() => import("./layout.html")),
|
|
12
|
+
)
|
|
@@ -12,7 +12,7 @@ export default Route.get(
|
|
|
12
12
|
const url = new URL(ctx.request.url)
|
|
13
13
|
const level = url.searchParams.get("logLevel") || ""
|
|
14
14
|
const search = url.searchParams.get("logSearch") || ""
|
|
15
|
-
let logs = yield* StudioStore.allLogs(
|
|
15
|
+
let logs = yield* StudioStore.allLogs()
|
|
16
16
|
if (level) logs = logs.filter((l) => l.level === level)
|
|
17
17
|
if (search) {
|
|
18
18
|
const lower = search.toLowerCase()
|
|
@@ -53,6 +53,8 @@ export default Route.get(
|
|
|
53
53
|
<Logs.LogLine log={l} />
|
|
54
54
|
))}
|
|
55
55
|
</div>
|
|
56
|
+
|
|
57
|
+
<div data-init={`@get('${prefix}/logs')`} />
|
|
56
58
|
</form>
|
|
57
59
|
</Shell.Shell>
|
|
58
60
|
)
|
|
@@ -18,7 +18,7 @@ export default Route.get(
|
|
|
18
18
|
</Shell.Shell>
|
|
19
19
|
)
|
|
20
20
|
}
|
|
21
|
-
const spans = yield* StudioStore.spansByTraceId(
|
|
21
|
+
const spans = yield* StudioStore.spansByTraceId(traceId)
|
|
22
22
|
|
|
23
23
|
return (
|
|
24
24
|
<Shell.Shell prefix={StudioStore.store.prefix} active="traces">
|
|
@@ -12,7 +12,7 @@ export default Route.get(
|
|
|
12
12
|
Route.html(function* (ctx) {
|
|
13
13
|
const url = new URL(ctx.request.url)
|
|
14
14
|
const search = url.searchParams.get("traceSearch") || ""
|
|
15
|
-
const allSpans = yield* StudioStore.allSpans(
|
|
15
|
+
const allSpans = StudioStore.filterOutStudioSpans(yield* StudioStore.allSpans())
|
|
16
16
|
const names = Array.from(new Set(allSpans.map((s) => s.name))).sort()
|
|
17
17
|
let spans = allSpans
|
|
18
18
|
if (search) {
|
|
@@ -52,17 +52,24 @@ export default Route.get(
|
|
|
52
52
|
}),
|
|
53
53
|
Route.sse(
|
|
54
54
|
Stream.fromPubSub(StudioStore.store.events).pipe(
|
|
55
|
-
Stream.filter((e) => e._tag === "
|
|
56
|
-
Stream.mapEffect(() =>
|
|
55
|
+
Stream.filter((e) => e._tag === "TraceEnd"),
|
|
56
|
+
Stream.mapEffect((e) =>
|
|
57
57
|
Effect.gen(function* () {
|
|
58
|
-
const
|
|
59
|
-
|
|
58
|
+
const traceSpans = yield* StudioStore.spansByTraceId(e.traceId)
|
|
59
|
+
if (StudioStore.isStudioTrace(traceSpans)) {
|
|
60
|
+
return undefined
|
|
61
|
+
}
|
|
62
|
+
const traceHtml = Html.renderToString(
|
|
63
|
+
<Traces.TraceGroup id={e.traceId} spans={traceSpans} />,
|
|
64
|
+
)
|
|
65
|
+
|
|
60
66
|
return {
|
|
61
67
|
event: "datastar-patch-elements",
|
|
62
|
-
data: `selector
|
|
68
|
+
data: `selector .tl-header\nmode after\nelements ${traceHtml}`,
|
|
63
69
|
}
|
|
64
70
|
}),
|
|
65
71
|
),
|
|
72
|
+
Stream.filter((event): event is { event: string; data: string } => event !== undefined),
|
|
66
73
|
),
|
|
67
74
|
),
|
|
68
75
|
)
|
package/src/studio/ui/Errors.tsx
CHANGED
|
@@ -31,7 +31,7 @@ export function ErrorLine(options: { error: StudioStore.StudioError }) {
|
|
|
31
31
|
<span style="color:#64748b">tag </span>
|
|
32
32
|
<span
|
|
33
33
|
style="color:#fca5a5;text-decoration:underline;cursor:copy"
|
|
34
|
-
data-on:click={`(e) => { e.signals.errorTag
|
|
34
|
+
data-on:click={`(e) => { e.signals.errorTag = '${t}'; e.actions.get(location.href, { contentType: 'form' }) }`}
|
|
35
35
|
>
|
|
36
36
|
{t}
|
|
37
37
|
</span>
|
package/src/studio/ui/Fibers.tsx
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as Unique from "../../Unique.ts"
|
|
2
2
|
import * as StudioStore from "../StudioStore.ts"
|
|
3
3
|
import * as Logs from "./Logs.tsx"
|
|
4
|
+
import * as PrettyValue from "./_PrettyValue.tsx"
|
|
4
5
|
|
|
5
6
|
function formatDuration(ms: number | undefined): string {
|
|
6
7
|
if (ms == null) return "..."
|
|
@@ -9,14 +10,20 @@ function formatDuration(ms: number | undefined): string {
|
|
|
9
10
|
return `${(ms / 1000).toFixed(2)}s`
|
|
10
11
|
}
|
|
11
12
|
|
|
12
|
-
function KeyValue(options: { label: string; value:
|
|
13
|
+
function KeyValue(options: { label: string; value: unknown }) {
|
|
13
14
|
if (options.value == null) return null
|
|
14
15
|
return (
|
|
15
|
-
<div
|
|
16
|
+
<div
|
|
17
|
+
style="display:flex;align-items:flex-start;gap:8px;padding:4px 0;border-bottom:1px solid #1e293b;font-size:12px"
|
|
18
|
+
>
|
|
16
19
|
<span style="color:#64748b;min-width:120px">{options.label}</span>
|
|
17
|
-
<
|
|
18
|
-
|
|
19
|
-
|
|
20
|
+
<div style="flex:1;min-width:0">
|
|
21
|
+
<PrettyValue.PrettyValue
|
|
22
|
+
value={options.value}
|
|
23
|
+
style="color:#e2e8f0;font-family:monospace;word-break:break-all"
|
|
24
|
+
preStyle="color:#e2e8f0;font-family:monospace;word-break:break-all;white-space:pre-wrap;margin:0"
|
|
25
|
+
/>
|
|
26
|
+
</div>
|
|
20
27
|
</div>
|
|
21
28
|
)
|
|
22
29
|
}
|
|
@@ -276,10 +283,7 @@ export function FiberDetail(options: {
|
|
|
276
283
|
</div>
|
|
277
284
|
)}
|
|
278
285
|
{Object.entries(options.context.annotations).map(([k, v]) => (
|
|
279
|
-
<KeyValue
|
|
280
|
-
label={k}
|
|
281
|
-
value={typeof v === "object" ? JSON.stringify(v) : String(v)}
|
|
282
|
-
/>
|
|
286
|
+
<KeyValue label={k} value={v} />
|
|
283
287
|
))}
|
|
284
288
|
</div>
|
|
285
289
|
</div>
|
|
@@ -310,7 +314,7 @@ export function FiberDetail(options: {
|
|
|
310
314
|
{Object.entries(s.attributes)
|
|
311
315
|
.filter(([k]) => k !== "code.stacktrace")
|
|
312
316
|
.map(([k, v]) => (
|
|
313
|
-
<KeyValue label={k} value={
|
|
317
|
+
<KeyValue label={k} value={v} />
|
|
314
318
|
))}
|
|
315
319
|
</div>
|
|
316
320
|
)
|
package/src/studio/ui/Logs.tsx
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as Unique from "../../Unique.ts"
|
|
2
2
|
import * as StudioStore from "../StudioStore.ts"
|
|
3
|
+
import * as PrettyValue from "./_PrettyValue.tsx"
|
|
3
4
|
|
|
4
5
|
function levelColor(level: string): string {
|
|
5
6
|
if (level === "DEBUG") return "#94a3b8"
|
|
@@ -25,26 +26,30 @@ export function LogLine(options: { log: StudioStore.StudioLog }) {
|
|
|
25
26
|
return (
|
|
26
27
|
<div
|
|
27
28
|
id={`log-${options.log.id}`}
|
|
28
|
-
style="padding:
|
|
29
|
+
style="padding:6px 8px;border-bottom:1px solid #1f2937;font-family:monospace;font-size:12px;display:flex;align-items:flex-start;gap:8px"
|
|
29
30
|
>
|
|
30
31
|
<span style="color:#6b7280;white-space:nowrap">{time}</span>
|
|
31
32
|
<span style={`color:${color};font-weight:600;width:56px;text-align:center;flex-shrink:0`}>
|
|
32
33
|
{options.log.level}
|
|
33
34
|
</span>
|
|
34
|
-
<
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
<div style="flex:1;min-width:0;display:flex;flex-direction:column;gap:4px">
|
|
36
|
+
<PrettyValue.PreformattedText
|
|
37
|
+
text={options.log.message}
|
|
38
|
+
style="color:#e5e7eb;margin:0;white-space:pre-wrap;word-break:break-word;font:inherit"
|
|
39
|
+
/>
|
|
40
|
+
{options.log.cause && (
|
|
41
|
+
<PrettyValue.PreformattedText
|
|
42
|
+
text={options.log.cause}
|
|
43
|
+
style="color:#ef4444;font-size:11px;margin:0;white-space:pre-wrap;word-break:break-word;font:inherit"
|
|
44
|
+
/>
|
|
45
|
+
)}
|
|
46
|
+
</div>
|
|
37
47
|
<a
|
|
38
48
|
href={`${StudioStore.store.prefix}/fibers/${options.log.fiberId.replace("#", "")}`}
|
|
39
|
-
style="color:#6b7280;white-space:nowrap;
|
|
49
|
+
style="color:#6b7280;white-space:nowrap;text-decoration:none;flex-shrink:0"
|
|
40
50
|
>
|
|
41
51
|
{options.log.fiberId}
|
|
42
52
|
</a>
|
|
43
|
-
{options.log.cause && (
|
|
44
|
-
<div style="color:#ef4444;font-size:11px;padding:2px 0 0 0;white-space:pre-wrap;width:100%">
|
|
45
|
-
{options.log.cause}
|
|
46
|
-
</div>
|
|
47
|
-
)}
|
|
48
53
|
</div>
|
|
49
54
|
)
|
|
50
55
|
}
|
package/src/studio/ui/Traces.tsx
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import * as Pretty from "../_Pretty.ts"
|
|
1
2
|
import * as StudioStore from "../StudioStore.ts"
|
|
3
|
+
import * as PrettyValue from "./_PrettyValue.tsx"
|
|
2
4
|
|
|
3
5
|
function formatDuration(ms: number | undefined): string {
|
|
4
6
|
if (ms == null) return "..."
|
|
@@ -13,14 +15,20 @@ function statusColor(status: string): string {
|
|
|
13
15
|
return "#eab308"
|
|
14
16
|
}
|
|
15
17
|
|
|
16
|
-
function KeyValue(options: { label: string; value:
|
|
18
|
+
function KeyValue(options: { label: string; value: unknown }) {
|
|
17
19
|
if (options.value == null) return null
|
|
18
20
|
return (
|
|
19
|
-
<div
|
|
21
|
+
<div
|
|
22
|
+
style="display:flex;align-items:flex-start;gap:8px;padding:4px 0;border-bottom:1px solid #1e293b;font-size:12px"
|
|
23
|
+
>
|
|
20
24
|
<span style="color:#64748b;min-width:120px">{options.label}</span>
|
|
21
|
-
<
|
|
22
|
-
|
|
23
|
-
|
|
25
|
+
<div style="flex:1;min-width:0">
|
|
26
|
+
<PrettyValue.PrettyValue
|
|
27
|
+
value={options.value}
|
|
28
|
+
style="color:#e2e8f0;font-family:monospace;word-break:break-all"
|
|
29
|
+
preStyle="color:#e2e8f0;font-family:monospace;word-break:break-all;white-space:pre-wrap;margin:0"
|
|
30
|
+
/>
|
|
31
|
+
</div>
|
|
24
32
|
</div>
|
|
25
33
|
)
|
|
26
34
|
}
|
|
@@ -47,6 +55,20 @@ interface TreeSpan {
|
|
|
47
55
|
ancestorHasNextSibling: Array<boolean>
|
|
48
56
|
}
|
|
49
57
|
|
|
58
|
+
function sortByStartTime(a: StudioStore.StudioSpan, b: StudioStore.StudioSpan): number {
|
|
59
|
+
if (a.startTime < b.startTime) return -1
|
|
60
|
+
if (a.startTime > b.startTime) return 1
|
|
61
|
+
return 0
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function pickRootSpan(spans: Array<StudioStore.StudioSpan>): StudioStore.StudioSpan {
|
|
65
|
+
const spanIds = new Set(spans.map((span) => span.spanId))
|
|
66
|
+
return (
|
|
67
|
+
spans.find((span) => !span.parentSpanId || !spanIds.has(span.parentSpanId)) ??
|
|
68
|
+
spans.slice().sort(sortByStartTime)[0]
|
|
69
|
+
)
|
|
70
|
+
}
|
|
71
|
+
|
|
50
72
|
function buildSpanTree(spans: Array<StudioStore.StudioSpan>): Array<TreeSpan> {
|
|
51
73
|
const byId = new Map<bigint, StudioStore.StudioSpan>()
|
|
52
74
|
const childrenOf = new Map<bigint, Array<StudioStore.StudioSpan>>()
|
|
@@ -69,23 +91,31 @@ function buildSpanTree(spans: Array<StudioStore.StudioSpan>): Array<TreeSpan> {
|
|
|
69
91
|
}
|
|
70
92
|
}
|
|
71
93
|
|
|
72
|
-
|
|
73
|
-
Number(a.startTime - b.startTime)
|
|
74
|
-
|
|
75
|
-
roots.sort(sortByStart)
|
|
94
|
+
roots.sort(sortByStartTime)
|
|
76
95
|
for (const children of childrenOf.values()) {
|
|
77
|
-
children.sort(
|
|
96
|
+
children.sort(sortByStartTime)
|
|
78
97
|
}
|
|
79
98
|
|
|
80
99
|
const result: Array<TreeSpan> = []
|
|
100
|
+
const visited = new Set<bigint>()
|
|
81
101
|
|
|
82
102
|
function walk(
|
|
83
103
|
span: StudioStore.StudioSpan,
|
|
84
104
|
depth: number,
|
|
85
105
|
isLast: boolean,
|
|
86
106
|
ancestors: Array<boolean>,
|
|
107
|
+
lineage: Set<bigint>,
|
|
87
108
|
) {
|
|
88
|
-
|
|
109
|
+
if (lineage.has(span.spanId) || visited.has(span.spanId)) return
|
|
110
|
+
|
|
111
|
+
const nextLineage = new Set(lineage)
|
|
112
|
+
nextLineage.add(span.spanId)
|
|
113
|
+
|
|
114
|
+
const children = (childrenOf.get(span.spanId) ?? []).filter(
|
|
115
|
+
(child) => !nextLineage.has(child.spanId) && !visited.has(child.spanId),
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
visited.add(span.spanId)
|
|
89
119
|
result.push({
|
|
90
120
|
span,
|
|
91
121
|
depth,
|
|
@@ -94,12 +124,18 @@ function buildSpanTree(spans: Array<StudioStore.StudioSpan>): Array<TreeSpan> {
|
|
|
94
124
|
ancestorHasNextSibling: [...ancestors],
|
|
95
125
|
})
|
|
96
126
|
for (let i = 0; i < children.length; i++) {
|
|
97
|
-
walk(children[i], depth + 1, i === children.length - 1, [...ancestors, !isLast])
|
|
127
|
+
walk(children[i], depth + 1, i === children.length - 1, [...ancestors, !isLast], nextLineage)
|
|
98
128
|
}
|
|
99
129
|
}
|
|
100
130
|
|
|
101
131
|
for (let i = 0; i < roots.length; i++) {
|
|
102
|
-
walk(roots[i], 0, i === roots.length - 1, [])
|
|
132
|
+
walk(roots[i], 0, i === roots.length - 1, [], new Set())
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const remaining = spans.filter((span) => !visited.has(span.spanId)).sort(sortByStartTime)
|
|
136
|
+
|
|
137
|
+
for (let i = 0; i < remaining.length; i++) {
|
|
138
|
+
walk(remaining[i], 0, i === remaining.length - 1, [], new Set())
|
|
103
139
|
}
|
|
104
140
|
|
|
105
141
|
return result
|
|
@@ -225,73 +261,30 @@ export function groupByTraceId(
|
|
|
225
261
|
return groups
|
|
226
262
|
}
|
|
227
263
|
|
|
228
|
-
export function TraceGroup(options: { spans: Array<StudioStore.StudioSpan> }) {
|
|
264
|
+
export function TraceGroup(options: { id?: bigint; spans: Array<StudioStore.StudioSpan> }) {
|
|
229
265
|
if (options.spans.length === 0) return null
|
|
230
|
-
const root = options.spans
|
|
231
|
-
const traceId = root.traceId
|
|
266
|
+
const root = pickRootSpan(options.spans)
|
|
267
|
+
const traceId = options.id ?? root.traceId
|
|
232
268
|
const totalMs = root.durationMs ?? 0
|
|
233
|
-
const rootStart = root.startTime
|
|
234
269
|
const hasError = options.spans.some((s) => s.status === "error")
|
|
235
270
|
const status = hasError ? "error" : root.status
|
|
236
271
|
|
|
237
272
|
return (
|
|
238
|
-
<
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
</
|
|
250
|
-
<
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
href={`${StudioStore.store.prefix}/traces/${traceId}`}
|
|
254
|
-
style="color:#38bdf8;font-size:12px;text-decoration:none"
|
|
255
|
-
>
|
|
256
|
-
Full trace view
|
|
257
|
-
</a>
|
|
258
|
-
<StatusBadge status={status} />
|
|
259
|
-
<span style="color:#64748b;font-size:11px">
|
|
260
|
-
{options.spans.length} span{options.spans.length !== 1 ? "s" : ""}
|
|
261
|
-
</span>
|
|
262
|
-
<span style="color:#64748b;font-size:11px;font-family:monospace">
|
|
263
|
-
{formatDuration(totalMs)}
|
|
264
|
-
</span>
|
|
265
|
-
<span style="color:#475569;font-size:10px;font-family:monospace">{traceId}</span>
|
|
266
|
-
</div>
|
|
267
|
-
{options.spans.map((s) => {
|
|
268
|
-
const offsetMs = Number(s.startTime - rootStart) / 1_000_000
|
|
269
|
-
const leftPct = totalMs > 0 ? Math.min(100, (offsetMs / totalMs) * 100) : 0
|
|
270
|
-
const widthPct =
|
|
271
|
-
totalMs > 0
|
|
272
|
-
? Math.max(0.5, Math.min(100 - leftPct, ((s.durationMs ?? 0) / totalMs) * 100))
|
|
273
|
-
: 100
|
|
274
|
-
return (
|
|
275
|
-
<div style="display:flex;align-items:center;gap:8px;padding:2px 0;font-size:11px;font-family:monospace">
|
|
276
|
-
<span style={`color:${statusColor(s.status)};min-width:10px`}>
|
|
277
|
-
{s.parentSpanId ? " " : ""}
|
|
278
|
-
</span>
|
|
279
|
-
<span style="color:#d1d5db;min-width:160px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">
|
|
280
|
-
{s.name}
|
|
281
|
-
</span>
|
|
282
|
-
<div style="flex:1;height:10px;background:#1f2937;border-radius:2px;position:relative;overflow:hidden">
|
|
283
|
-
<div
|
|
284
|
-
style={`position:absolute;height:100%;border-radius:2px;background:${statusColor(s.status)};left:${leftPct}%;width:${widthPct}%`}
|
|
285
|
-
/>
|
|
286
|
-
</div>
|
|
287
|
-
<span style="color:#9ca3af;min-width:60px;text-align:right">
|
|
288
|
-
{formatDuration(s.durationMs)}
|
|
289
|
-
</span>
|
|
290
|
-
</div>
|
|
291
|
-
)
|
|
292
|
-
})}
|
|
293
|
-
</div>
|
|
294
|
-
</details>
|
|
273
|
+
<a
|
|
274
|
+
id={`trace-${traceId}`}
|
|
275
|
+
class="tl-row tl-cols"
|
|
276
|
+
href={`${StudioStore.store.prefix}/traces/${traceId}`}
|
|
277
|
+
>
|
|
278
|
+
<span class="tl-cell tl-cell-status">
|
|
279
|
+
<span
|
|
280
|
+
style={`width:8px;height:8px;border-radius:50%;background:${statusColor(status)};display:block`}
|
|
281
|
+
/>
|
|
282
|
+
</span>
|
|
283
|
+
<span class="tl-cell tl-cell-name">{root.name}</span>
|
|
284
|
+
<span class="tl-cell tl-cell-spans">{options.spans.length}</span>
|
|
285
|
+
<span class="tl-cell tl-cell-dur">{formatDuration(totalMs)}</span>
|
|
286
|
+
<span class="tl-cell tl-cell-id">{String(traceId).slice(0, 12)}</span>
|
|
287
|
+
</a>
|
|
295
288
|
)
|
|
296
289
|
}
|
|
297
290
|
|
|
@@ -324,7 +317,7 @@ export function TraceDetail(options: { prefix: string; spans: Array<StudioStore.
|
|
|
324
317
|
if (options.spans.length === 0) {
|
|
325
318
|
return <div class="empty">Trace not found</div>
|
|
326
319
|
}
|
|
327
|
-
const root = options.spans
|
|
320
|
+
const root = pickRootSpan(options.spans)
|
|
328
321
|
const traceId = root.traceId
|
|
329
322
|
const totalMs = root.durationMs ?? 0
|
|
330
323
|
const rootStart = root.startTime
|
|
@@ -375,7 +368,7 @@ export function TraceDetail(options: { prefix: string; spans: Array<StudioStore.
|
|
|
375
368
|
const customAttrs = Object.entries(s.attributes).filter(([k]) => k !== "code.stacktrace")
|
|
376
369
|
|
|
377
370
|
return (
|
|
378
|
-
<details class="span-panel" style="margin-bottom:4px">
|
|
371
|
+
<details class="span-panel" style="margin-bottom:4px" open>
|
|
379
372
|
<summary class="span-panel-header">
|
|
380
373
|
<span
|
|
381
374
|
style={`width:8px;height:8px;border-radius:50%;background:${statusColor(s.status)};flex-shrink:0`}
|
|
@@ -394,16 +387,23 @@ export function TraceDetail(options: { prefix: string; spans: Array<StudioStore.
|
|
|
394
387
|
{s.parentSpanId && <KeyValue label="Parent" value={s.parentSpanId} />}
|
|
395
388
|
{stacktrace && <KeyValue label="Source" value={stacktrace} />}
|
|
396
389
|
{customAttrs.map(([k, v]) => (
|
|
397
|
-
<KeyValue label={k} value={
|
|
390
|
+
<KeyValue label={k} value={v} />
|
|
398
391
|
))}
|
|
399
392
|
{s.events.length > 0 && (
|
|
400
393
|
<div style="margin-top:4px">
|
|
401
394
|
<span style="color:#64748b;font-size:11px">Events:</span>
|
|
402
395
|
{s.events.map((ev) => (
|
|
403
|
-
<div
|
|
404
|
-
|
|
396
|
+
<div
|
|
397
|
+
style="display:flex;align-items:flex-start;gap:8px;padding:4px 0;font-size:11px;color:#94a3b8;font-family:monospace"
|
|
398
|
+
>
|
|
399
|
+
<span>{ev.name}</span>
|
|
405
400
|
{ev.attributes && (
|
|
406
|
-
<
|
|
401
|
+
<div style="flex:1;min-width:0">
|
|
402
|
+
<PrettyValue.PreformattedText
|
|
403
|
+
text={Pretty.prettyPrintJson(ev.attributes)}
|
|
404
|
+
style="color:#64748b;white-space:pre-wrap;word-break:break-all;margin:0;font:inherit"
|
|
405
|
+
/>
|
|
406
|
+
</div>
|
|
407
407
|
)}
|
|
408
408
|
</div>
|
|
409
409
|
))}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import * as Pretty from "../_Pretty.ts"
|
|
2
|
+
|
|
3
|
+
const htmlEscapeMap: Record<string, string> = {
|
|
4
|
+
"&": "&",
|
|
5
|
+
"<": "<",
|
|
6
|
+
">": ">",
|
|
7
|
+
'"': """,
|
|
8
|
+
"'": "'",
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function escapeHtml(text: string): string {
|
|
12
|
+
return text.replace(/[&<>"']/g, (char) => htmlEscapeMap[char]!)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function toPreHtml(text: string): string {
|
|
16
|
+
return escapeHtml(text).replaceAll("\n", " ")
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function PreformattedText(options: { text: string; style?: string }) {
|
|
20
|
+
return <pre style={options.style} dangerouslySetInnerHTML={{ __html: toPreHtml(options.text) }} />
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function PrettyValue(options: { value: unknown; style?: string; preStyle?: string }) {
|
|
24
|
+
if (options.value == null) return null
|
|
25
|
+
if (Pretty.isStructuredValue(options.value)) {
|
|
26
|
+
return (
|
|
27
|
+
<PreformattedText
|
|
28
|
+
text={Pretty.prettyPrintJson(options.value)}
|
|
29
|
+
style={options.preStyle}
|
|
30
|
+
/>
|
|
31
|
+
)
|
|
32
|
+
}
|
|
33
|
+
return <span style={options.style}>{String(options.value)}</span>
|
|
34
|
+
}
|