lopata 0.6.0 → 0.8.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.
@@ -5746,14 +5746,11 @@ function ErrorList() {
5746
5746
  /* @__PURE__ */ u3("td", {
5747
5747
  class: "px-4 py-2.5 text-right",
5748
5748
  children: err.traceId ? /* @__PURE__ */ u3("a", {
5749
- href: `#/traces?trace=${err.traceId}`,
5749
+ href: `#/traces/${err.traceId}`,
5750
5750
  onClick: (e3) => e3.stopPropagation(),
5751
5751
  class: "text-link hover:text-accent-lime text-xs font-mono",
5752
- children: [
5753
- err.traceId.slice(0, 8),
5754
- "..."
5755
- ]
5756
- }, undefined, true, undefined, this) : /* @__PURE__ */ u3("span", {
5752
+ children: err.traceId
5753
+ }, undefined, false, undefined, this) : /* @__PURE__ */ u3("span", {
5757
5754
  class: "text-text-dim text-xs",
5758
5755
  children: "-"
5759
5756
  }, undefined, false, undefined, this)
@@ -5911,14 +5908,30 @@ function ErrorDetailPage({ errorId }) {
5911
5908
  }, undefined, false, undefined, this)
5912
5909
  }, undefined, false, undefined, this)
5913
5910
  }, undefined, false, undefined, this),
5914
- traceSpans && traceSpans.length > 0 && /* @__PURE__ */ u3(Section, {
5911
+ detail.traceId && /* @__PURE__ */ u3(Section, {
5915
5912
  title: "Trace",
5916
5913
  open: true,
5917
- children: /* @__PURE__ */ u3(SimpleTraceWaterfall, {
5918
- spans: traceSpans,
5919
- highlightSpanId: detail.spanId
5920
- }, undefined, false, undefined, this)
5921
- }, undefined, false, undefined, this),
5914
+ children: [
5915
+ /* @__PURE__ */ u3("div", {
5916
+ class: "px-4 py-2.5 border-b border-border-subtle flex items-center gap-2",
5917
+ children: [
5918
+ /* @__PURE__ */ u3("span", {
5919
+ class: "text-xs text-text-muted",
5920
+ children: "Trace ID:"
5921
+ }, undefined, false, undefined, this),
5922
+ /* @__PURE__ */ u3("a", {
5923
+ href: `#/traces/${detail.traceId}`,
5924
+ class: "text-link hover:text-accent-lime text-xs font-mono",
5925
+ children: detail.traceId
5926
+ }, undefined, false, undefined, this)
5927
+ ]
5928
+ }, undefined, true, undefined, this),
5929
+ traceSpans && traceSpans.length > 0 && /* @__PURE__ */ u3(SimpleTraceWaterfall, {
5930
+ spans: traceSpans,
5931
+ highlightSpanId: detail.spanId
5932
+ }, undefined, false, undefined, this)
5933
+ ]
5934
+ }, undefined, true, undefined, this),
5922
5935
  data.request.method && data.request.url && /* @__PURE__ */ u3(Section, {
5923
5936
  title: "Request",
5924
5937
  open: true,
@@ -6251,6 +6264,325 @@ function truncateUrl(url) {
6251
6264
  }
6252
6265
  }
6253
6266
 
6267
+ // src/dashboard/views/generations.tsx
6268
+ var STATE_COLORS = {
6269
+ active: "bg-emerald-500/15 text-emerald-500",
6270
+ draining: "bg-yellow-500/15 text-yellow-500",
6271
+ stopped: "bg-gray-500/15 text-gray-400"
6272
+ };
6273
+ var STATE_DESCRIPTIONS = {
6274
+ active: "Receiving new requests",
6275
+ draining: "Finishing in-flight requests, no new requests accepted",
6276
+ stopped: "All work finished, will be removed shortly"
6277
+ };
6278
+ function formatRelativeTime(timestamp) {
6279
+ const diff = Date.now() - timestamp;
6280
+ if (diff < 1000)
6281
+ return "just now";
6282
+ if (diff < 60000)
6283
+ return `${Math.floor(diff / 1000)}s ago`;
6284
+ if (diff < 3600000)
6285
+ return `${Math.floor(diff / 60000)}m ago`;
6286
+ return `${Math.floor(diff / 3600000)}h ago`;
6287
+ }
6288
+ function GenerationCard({ gen, onReload, onStop }) {
6289
+ const [detail, setDetail] = d2(null);
6290
+ const [expanded, setExpanded] = d2(false);
6291
+ y2(() => {
6292
+ if (!expanded)
6293
+ return;
6294
+ rpc("generations.detail", { id: gen.id, workerName: gen.workerName }).then(setDetail).catch(() => {});
6295
+ }, [expanded, gen.id]);
6296
+ return /* @__PURE__ */ u3("div", {
6297
+ class: "bg-panel rounded-lg border border-border p-4",
6298
+ children: [
6299
+ /* @__PURE__ */ u3("div", {
6300
+ class: "flex items-center justify-between",
6301
+ children: [
6302
+ /* @__PURE__ */ u3("div", {
6303
+ class: "flex items-center gap-3",
6304
+ children: [
6305
+ /* @__PURE__ */ u3("span", {
6306
+ class: "text-lg font-bold font-mono text-ink",
6307
+ children: [
6308
+ "#",
6309
+ gen.id
6310
+ ]
6311
+ }, undefined, true, undefined, this),
6312
+ /* @__PURE__ */ u3("span", {
6313
+ title: STATE_DESCRIPTIONS[gen.state],
6314
+ children: /* @__PURE__ */ u3(StatusBadge, {
6315
+ status: gen.state,
6316
+ colorMap: STATE_COLORS
6317
+ }, undefined, false, undefined, this)
6318
+ }, undefined, false, undefined, this),
6319
+ gen.workerName && /* @__PURE__ */ u3("span", {
6320
+ class: "inline-flex px-2 py-0.5 rounded-md text-xs font-medium bg-panel-hover text-text-secondary",
6321
+ children: gen.workerName
6322
+ }, undefined, false, undefined, this)
6323
+ ]
6324
+ }, undefined, true, undefined, this),
6325
+ /* @__PURE__ */ u3("div", {
6326
+ class: "flex items-center gap-2",
6327
+ children: [
6328
+ gen.state === "active" && /* @__PURE__ */ u3("button", {
6329
+ onClick: onReload,
6330
+ class: "rounded-md px-3 py-1.5 text-xs font-medium bg-panel border border-border text-text-secondary hover:bg-panel-hover transition-all",
6331
+ children: "Reload"
6332
+ }, undefined, false, undefined, this),
6333
+ gen.state === "draining" && /* @__PURE__ */ u3("button", {
6334
+ onClick: () => onStop(gen.id),
6335
+ class: "rounded-md px-3 py-1.5 text-xs font-medium bg-panel border border-border text-text-secondary btn-danger transition-all",
6336
+ children: "Force Stop"
6337
+ }, undefined, false, undefined, this)
6338
+ ]
6339
+ }, undefined, true, undefined, this)
6340
+ ]
6341
+ }, undefined, true, undefined, this),
6342
+ /* @__PURE__ */ u3("div", {
6343
+ class: "mt-3 flex items-center gap-4 text-xs text-text-muted",
6344
+ children: [
6345
+ /* @__PURE__ */ u3("span", {
6346
+ title: new Date(gen.createdAt).toLocaleString(),
6347
+ children: [
6348
+ "Loaded ",
6349
+ formatRelativeTime(gen.createdAt)
6350
+ ]
6351
+ }, undefined, true, undefined, this),
6352
+ /* @__PURE__ */ u3("span", {
6353
+ title: "Number of HTTP requests currently being processed by this generation",
6354
+ children: [
6355
+ gen.activeRequests,
6356
+ " in-flight request",
6357
+ gen.activeRequests !== 1 ? "s" : ""
6358
+ ]
6359
+ }, undefined, true, undefined, this)
6360
+ ]
6361
+ }, undefined, true, undefined, this),
6362
+ gen.durableObjects && gen.durableObjects.length > 0 && /* @__PURE__ */ u3("div", {
6363
+ class: "mt-3 flex flex-wrap gap-2",
6364
+ children: gen.durableObjects.map((d3) => /* @__PURE__ */ u3("span", {
6365
+ class: "inline-flex items-center gap-1 px-2 py-0.5 rounded-md text-xs bg-panel-hover text-text-secondary",
6366
+ title: `Durable Object class "${d3.namespace}": ${d3.activeInstances} active instance(s), ${d3.totalWebSockets} WebSocket connection(s)`,
6367
+ children: [
6368
+ d3.namespace,
6369
+ ":",
6370
+ /* @__PURE__ */ u3("span", {
6371
+ class: "font-medium text-ink",
6372
+ children: d3.activeInstances
6373
+ }, undefined, false, undefined, this),
6374
+ " instance",
6375
+ d3.activeInstances !== 1 ? "s" : "",
6376
+ d3.totalWebSockets > 0 && /* @__PURE__ */ u3("span", {
6377
+ class: "text-text-muted",
6378
+ children: [
6379
+ "(",
6380
+ d3.totalWebSockets,
6381
+ " ws)"
6382
+ ]
6383
+ }, undefined, true, undefined, this)
6384
+ ]
6385
+ }, d3.namespace, true, undefined, this))
6386
+ }, undefined, false, undefined, this),
6387
+ /* @__PURE__ */ u3("button", {
6388
+ onClick: () => setExpanded(!expanded),
6389
+ class: "mt-2 text-xs text-text-muted hover:text-ink transition-colors",
6390
+ children: expanded ? "Hide DO instances" : "Show DO instances"
6391
+ }, undefined, false, undefined, this),
6392
+ expanded && detail && /* @__PURE__ */ u3("div", {
6393
+ class: "mt-3 border-t border-border pt-3",
6394
+ children: detail.doNamespaces.length === 0 ? /* @__PURE__ */ u3("div", {
6395
+ class: "text-xs text-text-muted",
6396
+ children: "No active Durable Object instances in this generation"
6397
+ }, undefined, false, undefined, this) : detail.doNamespaces.map((ns) => /* @__PURE__ */ u3("div", {
6398
+ class: "mb-3",
6399
+ children: [
6400
+ /* @__PURE__ */ u3("div", {
6401
+ class: "text-xs font-medium text-text-secondary mb-1",
6402
+ children: ns.namespace
6403
+ }, undefined, false, undefined, this),
6404
+ ns.instances.length === 0 ? /* @__PURE__ */ u3("div", {
6405
+ class: "text-xs text-text-muted ml-2",
6406
+ children: "No active instances"
6407
+ }, undefined, false, undefined, this) : /* @__PURE__ */ u3("div", {
6408
+ class: "ml-2 space-y-0.5",
6409
+ children: ns.instances.map((inst) => /* @__PURE__ */ u3("div", {
6410
+ class: "text-xs font-mono text-text-data flex items-center gap-2",
6411
+ children: [
6412
+ /* @__PURE__ */ u3("span", {
6413
+ class: "truncate max-w-[200px]",
6414
+ title: inst.id,
6415
+ children: inst.id
6416
+ }, undefined, false, undefined, this),
6417
+ inst.wsCount > 0 && /* @__PURE__ */ u3("span", {
6418
+ class: "text-text-muted",
6419
+ title: "Active WebSocket connections",
6420
+ children: [
6421
+ inst.wsCount,
6422
+ " ws"
6423
+ ]
6424
+ }, undefined, true, undefined, this)
6425
+ ]
6426
+ }, inst.id, true, undefined, this))
6427
+ }, undefined, false, undefined, this)
6428
+ ]
6429
+ }, ns.namespace, true, undefined, this))
6430
+ }, undefined, false, undefined, this)
6431
+ ]
6432
+ }, undefined, true, undefined, this);
6433
+ }
6434
+ function GenerationsView() {
6435
+ const { data, refetch } = useQuery("generations.list");
6436
+ const reload = useMutation("generations.reload");
6437
+ const drain = useMutation("generations.drain");
6438
+ const configMutation = useMutation("generations.config");
6439
+ const [gracePeriod, setGracePeriod] = d2("");
6440
+ y2(() => {
6441
+ if (data?.gracePeriodMs != null && gracePeriod === "") {
6442
+ setGracePeriod(String(data.gracePeriodMs));
6443
+ }
6444
+ }, [data?.gracePeriodMs]);
6445
+ y2(() => {
6446
+ const timer = setInterval(refetch, 2000);
6447
+ return () => clearInterval(timer);
6448
+ }, [refetch]);
6449
+ const handleReload = q2(async (workerName) => {
6450
+ await reload.mutate(workerName ? { workerName } : undefined);
6451
+ refetch();
6452
+ }, [reload, refetch]);
6453
+ const handleStop = q2(async (workerName) => {
6454
+ await drain.mutate(workerName ? { workerName } : undefined);
6455
+ refetch();
6456
+ }, [drain, refetch]);
6457
+ const handleGracePeriodSave = q2(() => {
6458
+ const ms = parseInt(gracePeriod, 10);
6459
+ if (Number.isNaN(ms) || ms < 0)
6460
+ return;
6461
+ configMutation.mutate({ gracePeriodMs: ms });
6462
+ }, [gracePeriod, configMutation]);
6463
+ if (!data) {
6464
+ return /* @__PURE__ */ u3("div", {
6465
+ class: "p-4 sm:p-8",
6466
+ children: [
6467
+ /* @__PURE__ */ u3(PageHeader, {
6468
+ title: "Generations"
6469
+ }, undefined, false, undefined, this),
6470
+ /* @__PURE__ */ u3("div", {
6471
+ class: "text-text-muted text-sm text-center py-12",
6472
+ children: "Loading..."
6473
+ }, undefined, false, undefined, this)
6474
+ ]
6475
+ }, undefined, true, undefined, this);
6476
+ }
6477
+ const generations = data.generations;
6478
+ return /* @__PURE__ */ u3("div", {
6479
+ class: "p-4 sm:p-8",
6480
+ children: [
6481
+ /* @__PURE__ */ u3(PageHeader, {
6482
+ title: "Generations",
6483
+ subtitle: `${generations.length} generation(s)`,
6484
+ actions: /* @__PURE__ */ u3("div", {
6485
+ class: "flex items-center gap-2",
6486
+ children: [
6487
+ /* @__PURE__ */ u3("div", {
6488
+ class: "flex items-center gap-1",
6489
+ title: "How long to wait for in-flight requests to finish before force-stopping an old generation",
6490
+ children: [
6491
+ /* @__PURE__ */ u3("label", {
6492
+ class: "text-xs text-text-muted",
6493
+ children: "Grace period (ms):"
6494
+ }, undefined, false, undefined, this),
6495
+ /* @__PURE__ */ u3("input", {
6496
+ type: "number",
6497
+ value: gracePeriod,
6498
+ onInput: (e3) => setGracePeriod(e3.target.value),
6499
+ onBlur: handleGracePeriodSave,
6500
+ class: "bg-panel border border-border rounded-md px-2 py-1 text-xs w-24 outline-none focus:border-border focus:ring-1 focus:ring-border"
6501
+ }, undefined, false, undefined, this)
6502
+ ]
6503
+ }, undefined, true, undefined, this),
6504
+ /* @__PURE__ */ u3("button", {
6505
+ onClick: () => handleReload(),
6506
+ disabled: reload.isLoading,
6507
+ class: "rounded-md px-3 py-1.5 text-sm font-medium bg-accent-lime text-surface hover:bg-accent-lime/90 disabled:opacity-50 transition-all",
6508
+ children: reload.isLoading ? "Reloading..." : "Reload"
6509
+ }, undefined, false, undefined, this)
6510
+ ]
6511
+ }, undefined, true, undefined, this)
6512
+ }, undefined, false, undefined, this),
6513
+ /* @__PURE__ */ u3("p", {
6514
+ class: "text-xs text-text-muted mb-6 max-w-xl leading-relaxed",
6515
+ children: [
6516
+ "A generation is a snapshot of your worker code at a point in time. When code changes, a new generation is created and the old one is drained (finishes in-flight work, then stops). Each request and Durable Object is tied to the generation that was active when it started. The",
6517
+ " ",
6518
+ /* @__PURE__ */ u3("span", {
6519
+ class: "font-mono",
6520
+ children: "Gen"
6521
+ }, undefined, false, undefined, this),
6522
+ " column in Traces shows which generation handled each request."
6523
+ ]
6524
+ }, undefined, true, undefined, this),
6525
+ generations.length === 0 ? /* @__PURE__ */ u3(EmptyState, {
6526
+ message: "No generations yet. Start your dev server to see generations here."
6527
+ }, undefined, false, undefined, this) : /* @__PURE__ */ u3("div", {
6528
+ class: "space-y-4",
6529
+ children: generations.map((gen) => /* @__PURE__ */ u3(GenerationCard, {
6530
+ gen,
6531
+ onReload: () => handleReload(),
6532
+ onStop: (id) => handleStop()
6533
+ }, gen.id, false, undefined, this))
6534
+ }, undefined, false, undefined, this),
6535
+ data.workers && data.workers.length > 0 && /* @__PURE__ */ u3("div", {
6536
+ class: "mt-8",
6537
+ children: [
6538
+ /* @__PURE__ */ u3("h2", {
6539
+ class: "text-lg font-bold text-ink mb-1",
6540
+ children: "Per-Worker Generations"
6541
+ }, undefined, false, undefined, this),
6542
+ /* @__PURE__ */ u3("p", {
6543
+ class: "text-xs text-text-muted mb-4",
6544
+ children: "In multi-worker mode, each worker has its own independent generation lifecycle."
6545
+ }, undefined, false, undefined, this),
6546
+ data.workers.map((w3) => /* @__PURE__ */ u3("div", {
6547
+ class: "mb-6",
6548
+ children: [
6549
+ /* @__PURE__ */ u3("div", {
6550
+ class: "flex items-center gap-3 mb-3",
6551
+ children: [
6552
+ /* @__PURE__ */ u3("h3", {
6553
+ class: "text-sm font-bold text-ink",
6554
+ children: w3.workerName
6555
+ }, undefined, false, undefined, this),
6556
+ /* @__PURE__ */ u3("span", {
6557
+ class: "text-xs text-text-muted",
6558
+ children: [
6559
+ w3.generations.length,
6560
+ " generation(s)"
6561
+ ]
6562
+ }, undefined, true, undefined, this),
6563
+ /* @__PURE__ */ u3("button", {
6564
+ onClick: () => handleReload(w3.workerName),
6565
+ class: "rounded-md px-2 py-1 text-xs font-medium bg-panel border border-border text-text-secondary hover:bg-panel-hover transition-all",
6566
+ children: "Reload"
6567
+ }, undefined, false, undefined, this)
6568
+ ]
6569
+ }, undefined, true, undefined, this),
6570
+ /* @__PURE__ */ u3("div", {
6571
+ class: "space-y-3",
6572
+ children: w3.generations.map((gen) => /* @__PURE__ */ u3(GenerationCard, {
6573
+ gen,
6574
+ onReload: () => handleReload(w3.workerName),
6575
+ onStop: () => handleStop(w3.workerName)
6576
+ }, gen.id, false, undefined, this))
6577
+ }, undefined, false, undefined, this)
6578
+ ]
6579
+ }, w3.workerName, true, undefined, this))
6580
+ ]
6581
+ }, undefined, true, undefined, this)
6582
+ ]
6583
+ }, undefined, true, undefined, this);
6584
+ }
6585
+
6254
6586
  // src/dashboard/views/home.tsx
6255
6587
  var INVENTORY = [
6256
6588
  { key: "kv", label: "KV", path: "/kv", icon: "kv" },
@@ -7582,6 +7914,14 @@ function ScheduledList() {
7582
7914
  }
7583
7915
 
7584
7916
  // src/dashboard/views/traces.tsx
7917
+ var SOURCE_BADGE_STYLES = {
7918
+ fetch: { bg: "var(--color-badge-blue-bg)", color: "var(--color-badge-blue-text)" },
7919
+ scheduled: { bg: "var(--color-badge-purple-bg)", color: "var(--color-badge-purple-text)" },
7920
+ queue: { bg: "var(--color-badge-orange-bg)", color: "var(--color-badge-orange-text)" },
7921
+ alarm: { bg: "var(--color-badge-yellow-bg)", color: "var(--color-badge-yellow-text)" },
7922
+ workflow: { bg: "var(--color-badge-emerald-bg)", color: "var(--color-badge-emerald-text)" }
7923
+ };
7924
+ var DEFAULT_BADGE_STYLE = { bg: "var(--color-badge-red-bg)", color: "var(--color-badge-red-text)" };
7585
7925
  var eventListeners = new Set;
7586
7926
  function onTraceEvents(fn) {
7587
7927
  eventListeners.add(fn);
@@ -7596,6 +7936,39 @@ function emitTraceEvents(events) {
7596
7936
  } catch {}
7597
7937
  }
7598
7938
  }
7939
+ function useTraceData(traceId) {
7940
+ const [spans, setSpans] = d2([]);
7941
+ const [events, setEvents] = d2([]);
7942
+ const [traceErrors, setTraceErrors] = d2([]);
7943
+ const [isLoading, setIsLoading] = d2(true);
7944
+ y2(() => {
7945
+ setIsLoading(true);
7946
+ rpc("traces.getTrace", { traceId }).then((data) => {
7947
+ setSpans(data.spans);
7948
+ setEvents(data.events);
7949
+ setIsLoading(false);
7950
+ });
7951
+ rpc("traces.errors", { traceId }).then(setTraceErrors).catch(() => {});
7952
+ }, [traceId]);
7953
+ y2(() => {
7954
+ return onTraceEvents((traceEvents) => {
7955
+ for (const ev of traceEvents) {
7956
+ if (ev.type === "span.start" && ev.span.traceId === traceId) {
7957
+ setSpans((prev) => {
7958
+ if (prev.some((s3) => s3.spanId === ev.span.spanId))
7959
+ return prev;
7960
+ return [...prev, ev.span];
7961
+ });
7962
+ } else if (ev.type === "span.end" && ev.span.traceId === traceId) {
7963
+ setSpans((prev) => prev.map((s3) => s3.spanId === ev.span.spanId ? ev.span : s3));
7964
+ } else if (ev.type === "span.event" && ev.event.traceId === traceId) {
7965
+ setEvents((prev) => [...prev, ev.event]);
7966
+ }
7967
+ }
7968
+ });
7969
+ }, [traceId]);
7970
+ return { spans, events, traceErrors, isLoading };
7971
+ }
7599
7972
  function useTraceStream() {
7600
7973
  const [traces, setTraces] = d2(new Map);
7601
7974
  const [wsStatus, setWsStatus] = d2("connecting");
@@ -7634,7 +8007,8 @@ function useTraceStream() {
7634
8007
  startTime: s3.startTime,
7635
8008
  durationMs: s3.durationMs,
7636
8009
  spanCount: 1,
7637
- errorCount: 0
8010
+ errorCount: 0,
8011
+ generationId: typeof s3.attributes["lopata.generation_id"] === "number" ? s3.attributes["lopata.generation_id"] : null
7638
8012
  });
7639
8013
  } else if (event.type === "span.end" && event.span.parentSpanId === null) {
7640
8014
  const s3 = event.span;
@@ -7707,7 +8081,16 @@ var TIME_RANGE_OPTIONS = [
7707
8081
  { label: "24h", ms: 24 * 60 * 60 * 1000 },
7708
8082
  { label: "All", ms: 0 }
7709
8083
  ];
7710
- function TracesView() {
8084
+ function TracesView({ route }) {
8085
+ const traceIdFromRoute = route.startsWith("/traces/") ? route.slice("/traces/".length) : null;
8086
+ if (traceIdFromRoute) {
8087
+ return /* @__PURE__ */ u3(TraceDetailPage, {
8088
+ traceId: traceIdFromRoute
8089
+ }, undefined, false, undefined, this);
8090
+ }
8091
+ return /* @__PURE__ */ u3(TracesListView, {}, undefined, false, undefined, this);
8092
+ }
8093
+ function TracesListView() {
7711
8094
  const { traces, setFilter, wsStatus } = useTraceStream();
7712
8095
  const [selectedTraceId, setSelectedTraceId] = d2(null);
7713
8096
  const [pathFilter, setPathFilter] = d2("");
@@ -7794,10 +8177,7 @@ function TracesView() {
7794
8177
  ]
7795
8178
  }, undefined, true, undefined, this),
7796
8179
  /* @__PURE__ */ u3("button", {
7797
- onClick: () => {
7798
- clearTraces.mutate();
7799
- setSelectedTraceId(null);
7800
- },
8180
+ onClick: () => clearTraces.mutate(),
7801
8181
  class: "rounded-md px-3 py-1.5 text-sm font-medium bg-panel border border-border text-text-secondary btn-danger transition-all",
7802
8182
  children: "Clear all"
7803
8183
  }, undefined, false, undefined, this)
@@ -7910,6 +8290,10 @@ function TracesView() {
7910
8290
  class: "text-left px-4 py-2.5 text-xs text-text-muted font-medium",
7911
8291
  children: "Worker"
7912
8292
  }, undefined, false, undefined, this),
8293
+ /* @__PURE__ */ u3("th", {
8294
+ class: "text-left px-4 py-2.5 text-xs text-text-muted font-medium",
8295
+ children: "Gen"
8296
+ }, undefined, false, undefined, this),
7913
8297
  /* @__PURE__ */ u3("th", {
7914
8298
  class: "text-left px-4 py-2.5 text-xs text-text-muted font-medium",
7915
8299
  style: { minWidth: "140px" },
@@ -7957,6 +8341,16 @@ function TracesView() {
7957
8341
  children: trace.workerName
7958
8342
  }, undefined, false, undefined, this)
7959
8343
  }, undefined, false, undefined, this),
8344
+ /* @__PURE__ */ u3("td", {
8345
+ class: "px-4 py-2.5",
8346
+ children: trace.generationId != null && /* @__PURE__ */ u3("span", {
8347
+ class: "inline-flex px-1.5 py-0.5 rounded text-[10px] font-mono font-medium bg-panel-hover text-text-secondary",
8348
+ children: [
8349
+ "#",
8350
+ trace.generationId
8351
+ ]
8352
+ }, undefined, true, undefined, this)
8353
+ }, undefined, false, undefined, this),
7960
8354
  /* @__PURE__ */ u3("td", {
7961
8355
  class: "px-4 py-2.5",
7962
8356
  children: /* @__PURE__ */ u3(DurationBar, {
@@ -8001,6 +8395,139 @@ function TracesView() {
8001
8395
  ]
8002
8396
  }, undefined, true, undefined, this);
8003
8397
  }
8398
+ function TraceDetailPage({ traceId }) {
8399
+ const { spans, events, traceErrors, isLoading } = useTraceData(traceId);
8400
+ const rootSpan = spans.find((s3) => !s3.parentSpanId);
8401
+ return /* @__PURE__ */ u3("div", {
8402
+ class: "p-4 sm:p-8 h-full flex flex-col",
8403
+ children: [
8404
+ /* @__PURE__ */ u3("div", {
8405
+ class: "flex items-center gap-3 mb-6",
8406
+ children: [
8407
+ /* @__PURE__ */ u3("button", {
8408
+ onClick: () => navigate("/traces"),
8409
+ class: "flex items-center gap-1.5 text-sm text-text-secondary hover:text-ink transition-colors",
8410
+ children: [
8411
+ /* @__PURE__ */ u3("svg", {
8412
+ class: "w-4 h-4",
8413
+ viewBox: "0 0 20 20",
8414
+ fill: "currentColor",
8415
+ children: /* @__PURE__ */ u3("path", {
8416
+ "fill-rule": "evenodd",
8417
+ d: "M17 10a.75.75 0 01-.75.75H5.612l4.158 3.96a.75.75 0 11-1.04 1.08l-5.5-5.25a.75.75 0 010-1.08l5.5-5.25a.75.75 0 111.04 1.08L5.612 9.25H16.25A.75.75 0 0117 10z",
8418
+ "clip-rule": "evenodd"
8419
+ }, undefined, false, undefined, this)
8420
+ }, undefined, false, undefined, this),
8421
+ "Back to traces"
8422
+ ]
8423
+ }, undefined, true, undefined, this),
8424
+ /* @__PURE__ */ u3("div", {
8425
+ class: "h-5 w-px bg-border"
8426
+ }, undefined, false, undefined, this),
8427
+ /* @__PURE__ */ u3("div", {
8428
+ children: [
8429
+ /* @__PURE__ */ u3("div", {
8430
+ class: "flex items-center gap-2",
8431
+ children: [
8432
+ rootSpan && /* @__PURE__ */ u3(TraceStatusBadge, {
8433
+ status: rootSpan.status
8434
+ }, undefined, false, undefined, this),
8435
+ /* @__PURE__ */ u3("h1", {
8436
+ class: "text-lg font-bold text-ink",
8437
+ children: rootSpan?.name ?? "Loading..."
8438
+ }, undefined, false, undefined, this)
8439
+ ]
8440
+ }, undefined, true, undefined, this),
8441
+ /* @__PURE__ */ u3("div", {
8442
+ class: "text-xs text-text-muted font-mono mt-0.5",
8443
+ children: [
8444
+ "Trace ",
8445
+ traceId,
8446
+ rootSpan?.workerName && /* @__PURE__ */ u3("span", {
8447
+ class: "ml-2 inline-flex px-2 py-0.5 rounded-md text-xs font-medium bg-panel-hover text-text-secondary",
8448
+ children: rootSpan.workerName
8449
+ }, undefined, false, undefined, this),
8450
+ typeof rootSpan?.attributes["lopata.generation_id"] === "number" && /* @__PURE__ */ u3("span", {
8451
+ class: "ml-2 inline-flex px-1.5 py-0.5 rounded text-[10px] font-mono font-medium bg-panel-hover text-text-secondary",
8452
+ children: [
8453
+ "Gen #",
8454
+ rootSpan.attributes["lopata.generation_id"]
8455
+ ]
8456
+ }, undefined, true, undefined, this),
8457
+ rootSpan?.durationMs != null && /* @__PURE__ */ u3("span", {
8458
+ class: "ml-2 text-text-secondary",
8459
+ children: formatDuration(rootSpan.durationMs)
8460
+ }, undefined, false, undefined, this)
8461
+ ]
8462
+ }, undefined, true, undefined, this)
8463
+ ]
8464
+ }, undefined, true, undefined, this)
8465
+ ]
8466
+ }, undefined, true, undefined, this),
8467
+ /* @__PURE__ */ u3("div", {
8468
+ class: "flex-1 overflow-y-auto scrollbar-thin min-h-0",
8469
+ children: isLoading ? /* @__PURE__ */ u3("div", {
8470
+ class: "text-text-muted text-sm py-12 text-center",
8471
+ children: "Loading trace..."
8472
+ }, undefined, false, undefined, this) : /* @__PURE__ */ u3("div", {
8473
+ children: [
8474
+ traceErrors.length > 0 && /* @__PURE__ */ u3("div", {
8475
+ class: "mb-4",
8476
+ children: [
8477
+ /* @__PURE__ */ u3("div", {
8478
+ class: "text-xs font-medium text-text-muted uppercase tracking-wider mb-2",
8479
+ children: [
8480
+ "Errors (",
8481
+ traceErrors.length,
8482
+ ")"
8483
+ ]
8484
+ }, undefined, true, undefined, this),
8485
+ /* @__PURE__ */ u3("div", {
8486
+ class: "space-y-1",
8487
+ children: traceErrors.map((err) => /* @__PURE__ */ u3("a", {
8488
+ href: `#/errors/${err.id}`,
8489
+ class: "flex items-center gap-2 px-3 py-2 rounded-md text-xs no-underline transition-colors",
8490
+ style: { background: "var(--color-error-highlight)", borderColor: "var(--color-error-ring)" },
8491
+ children: [
8492
+ err.source && (() => {
8493
+ const s3 = SOURCE_BADGE_STYLES[err.source] ?? DEFAULT_BADGE_STYLE;
8494
+ return /* @__PURE__ */ u3("span", {
8495
+ class: "inline-flex px-1.5 py-0.5 rounded text-[10px] font-medium",
8496
+ style: { background: s3.bg, color: s3.color },
8497
+ children: err.source
8498
+ }, undefined, false, undefined, this);
8499
+ })(),
8500
+ /* @__PURE__ */ u3("span", {
8501
+ class: "font-medium",
8502
+ style: { color: "var(--color-badge-red-text)" },
8503
+ children: err.errorName
8504
+ }, undefined, false, undefined, this),
8505
+ /* @__PURE__ */ u3("span", {
8506
+ style: { color: "var(--color-badge-red-text)" },
8507
+ class: "truncate",
8508
+ children: err.errorMessage
8509
+ }, undefined, false, undefined, this),
8510
+ /* @__PURE__ */ u3("span", {
8511
+ style: { color: "var(--color-badge-red-text)", opacity: 0.7 },
8512
+ class: "font-mono ml-auto flex-shrink-0",
8513
+ children: formatTimestamp(err.timestamp)
8514
+ }, undefined, false, undefined, this)
8515
+ ]
8516
+ }, err.id, true, undefined, this))
8517
+ }, undefined, false, undefined, this)
8518
+ ]
8519
+ }, undefined, true, undefined, this),
8520
+ /* @__PURE__ */ u3(TraceWaterfall, {
8521
+ spans,
8522
+ events,
8523
+ onAddAttributeFilter: () => {}
8524
+ }, undefined, false, undefined, this)
8525
+ ]
8526
+ }, undefined, true, undefined, this)
8527
+ }, undefined, false, undefined, this)
8528
+ ]
8529
+ }, undefined, true, undefined, this);
8530
+ }
8004
8531
  function SpansListTab() {
8005
8532
  const [spans, setSpans] = d2([]);
8006
8533
  const [cursor, setCursor] = d2(null);
@@ -8098,11 +8625,8 @@ function SpansListTab() {
8098
8625
  children: /* @__PURE__ */ u3("button", {
8099
8626
  onClick: () => setSelectedTraceId(span.traceId),
8100
8627
  class: "text-link hover:text-accent-lime text-xs font-mono",
8101
- children: [
8102
- span.traceId.slice(0, 8),
8103
- "..."
8104
- ]
8105
- }, undefined, true, undefined, this)
8628
+ children: span.traceId
8629
+ }, undefined, false, undefined, this)
8106
8630
  }, undefined, false, undefined, this)
8107
8631
  ]
8108
8632
  }, span.spanId, true, undefined, this))
@@ -8126,8 +8650,7 @@ function SpansListTab() {
8126
8650
  }, undefined, false, undefined, this),
8127
8651
  selectedTraceId && /* @__PURE__ */ u3(TraceDrawer, {
8128
8652
  traceId: selectedTraceId,
8129
- onClose: () => setSelectedTraceId(null),
8130
- onAddAttributeFilter: () => {}
8653
+ onClose: () => setSelectedTraceId(null)
8131
8654
  }, undefined, false, undefined, this)
8132
8655
  ]
8133
8656
  }, undefined, true, undefined, this);
@@ -8136,6 +8659,7 @@ function LogsListTab() {
8136
8659
  const [logs, setLogs] = d2([]);
8137
8660
  const [cursor, setCursor] = d2(null);
8138
8661
  const [isLoading, setIsLoading] = d2(true);
8662
+ const [selectedTraceId, setSelectedTraceId] = d2(null);
8139
8663
  const loadLogs = q2((cur) => {
8140
8664
  setIsLoading(true);
8141
8665
  rpc("traces.listLogs", { limit: 50, cursor: cur }).then((data) => {
@@ -8216,12 +8740,13 @@ function LogsListTab() {
8216
8740
  children: formatTimestamp(log.timestamp)
8217
8741
  }, undefined, false, undefined, this),
8218
8742
  /* @__PURE__ */ u3("td", {
8219
- class: "px-4 py-2.5 text-right font-mono text-xs text-text-muted",
8220
- children: [
8221
- log.traceId.slice(0, 8),
8222
- "..."
8223
- ]
8224
- }, undefined, true, undefined, this)
8743
+ class: "px-4 py-2.5 text-right",
8744
+ children: /* @__PURE__ */ u3("button", {
8745
+ onClick: () => setSelectedTraceId(log.traceId),
8746
+ class: "text-link hover:text-accent-lime text-xs font-mono",
8747
+ children: log.traceId
8748
+ }, undefined, false, undefined, this)
8749
+ }, undefined, false, undefined, this)
8225
8750
  ]
8226
8751
  }, log.id, true, undefined, this))
8227
8752
  }, undefined, false, undefined, this)
@@ -8241,49 +8766,16 @@ function LogsListTab() {
8241
8766
  isLoading && logs.length === 0 && /* @__PURE__ */ u3("div", {
8242
8767
  class: "text-text-muted text-sm text-center py-12",
8243
8768
  children: "Loading logs..."
8769
+ }, undefined, false, undefined, this),
8770
+ selectedTraceId && /* @__PURE__ */ u3(TraceDrawer, {
8771
+ traceId: selectedTraceId,
8772
+ onClose: () => setSelectedTraceId(null)
8244
8773
  }, undefined, false, undefined, this)
8245
8774
  ]
8246
8775
  }, undefined, true, undefined, this);
8247
8776
  }
8248
- var SOURCE_BADGE_STYLES = {
8249
- fetch: { bg: "var(--color-badge-blue-bg)", color: "var(--color-badge-blue-text)" },
8250
- scheduled: { bg: "var(--color-badge-purple-bg)", color: "var(--color-badge-purple-text)" },
8251
- queue: { bg: "var(--color-badge-orange-bg)", color: "var(--color-badge-orange-text)" },
8252
- alarm: { bg: "var(--color-badge-yellow-bg)", color: "var(--color-badge-yellow-text)" },
8253
- workflow: { bg: "var(--color-badge-emerald-bg)", color: "var(--color-badge-emerald-text)" }
8254
- };
8255
- var DEFAULT_BADGE_STYLE = { bg: "var(--color-badge-red-bg)", color: "var(--color-badge-red-text)" };
8256
8777
  function TraceDrawer({ traceId, onClose, onAddAttributeFilter }) {
8257
- const [spans, setSpans] = d2([]);
8258
- const [events, setEvents] = d2([]);
8259
- const [traceErrors, setTraceErrors] = d2([]);
8260
- const [isLoading, setIsLoading] = d2(true);
8261
- y2(() => {
8262
- setIsLoading(true);
8263
- rpc("traces.getTrace", { traceId }).then((data) => {
8264
- setSpans(data.spans);
8265
- setEvents(data.events);
8266
- setIsLoading(false);
8267
- });
8268
- rpc("traces.errors", { traceId }).then(setTraceErrors).catch(() => {});
8269
- }, [traceId]);
8270
- y2(() => {
8271
- return onTraceEvents((traceEvents) => {
8272
- for (const ev of traceEvents) {
8273
- if (ev.type === "span.start" && ev.span.traceId === traceId) {
8274
- setSpans((prev) => {
8275
- if (prev.some((s3) => s3.spanId === ev.span.spanId))
8276
- return prev;
8277
- return [...prev, ev.span];
8278
- });
8279
- } else if (ev.type === "span.end" && ev.span.traceId === traceId) {
8280
- setSpans((prev) => prev.map((s3) => s3.spanId === ev.span.spanId ? ev.span : s3));
8281
- } else if (ev.type === "span.event" && ev.event.traceId === traceId) {
8282
- setEvents((prev) => [...prev, ev.event]);
8283
- }
8284
- }
8285
- });
8286
- }, [traceId]);
8778
+ const { spans, events, traceErrors, isLoading } = useTraceData(traceId);
8287
8779
  return /* @__PURE__ */ u3(k, {
8288
8780
  children: [
8289
8781
  /* @__PURE__ */ u3("div", {
@@ -8302,8 +8794,7 @@ function TraceDrawer({ traceId, onClose, onAddAttributeFilter }) {
8302
8794
  class: "text-xs text-text-muted font-mono",
8303
8795
  children: [
8304
8796
  "Trace ",
8305
- traceId.slice(0, 12),
8306
- "..."
8797
+ traceId
8307
8798
  ]
8308
8799
  }, undefined, true, undefined, this),
8309
8800
  /* @__PURE__ */ u3("div", {
@@ -8312,11 +8803,34 @@ function TraceDrawer({ traceId, onClose, onAddAttributeFilter }) {
8312
8803
  }, undefined, false, undefined, this)
8313
8804
  ]
8314
8805
  }, undefined, true, undefined, this),
8315
- /* @__PURE__ */ u3("button", {
8316
- onClick: onClose,
8317
- class: "w-7 h-7 flex items-center justify-center rounded-md hover:bg-panel-hover transition-colors text-text-muted hover:text-ink",
8318
- children: "×"
8319
- }, undefined, false, undefined, this)
8806
+ /* @__PURE__ */ u3("div", {
8807
+ class: "flex items-center gap-1",
8808
+ children: [
8809
+ /* @__PURE__ */ u3("a", {
8810
+ href: `#/traces/${traceId}`,
8811
+ class: "w-7 h-7 flex items-center justify-center rounded-md hover:bg-panel-hover transition-colors text-text-muted hover:text-ink",
8812
+ title: "Open full page",
8813
+ children: /* @__PURE__ */ u3("svg", {
8814
+ class: "w-4 h-4",
8815
+ viewBox: "0 0 20 20",
8816
+ fill: "currentColor",
8817
+ children: [
8818
+ /* @__PURE__ */ u3("path", {
8819
+ d: "M4.75 5.75a.75.75 0 00.75.75h4.69l-4.22 4.22a.75.75 0 001.06 1.06L11.25 7.56v4.69a.75.75 0 001.5 0v-6.5a.75.75 0 00-.75-.75h-6.5a.75.75 0 00-.75.75z"
8820
+ }, undefined, false, undefined, this),
8821
+ /* @__PURE__ */ u3("path", {
8822
+ d: "M3 13.5a1.5 1.5 0 011.5-1.5h1.25a.75.75 0 010 1.5H4.5v4h4v-1.25a.75.75 0 011.5 0v1.25a1.5 1.5 0 01-1.5 1.5h-4A1.5 1.5 0 013 17.5v-4z"
8823
+ }, undefined, false, undefined, this)
8824
+ ]
8825
+ }, undefined, true, undefined, this)
8826
+ }, undefined, false, undefined, this),
8827
+ /* @__PURE__ */ u3("button", {
8828
+ onClick: onClose,
8829
+ class: "w-7 h-7 flex items-center justify-center rounded-md hover:bg-panel-hover transition-colors text-text-muted hover:text-ink",
8830
+ children: "×"
8831
+ }, undefined, false, undefined, this)
8832
+ ]
8833
+ }, undefined, true, undefined, this)
8320
8834
  ]
8321
8835
  }, undefined, true, undefined, this),
8322
8836
  /* @__PURE__ */ u3("div", {
@@ -8375,7 +8889,7 @@ function TraceDrawer({ traceId, onClose, onAddAttributeFilter }) {
8375
8889
  /* @__PURE__ */ u3(TraceWaterfall, {
8376
8890
  spans,
8377
8891
  events,
8378
- onAddAttributeFilter
8892
+ onAddAttributeFilter: onAddAttributeFilter ?? (() => {})
8379
8893
  }, undefined, false, undefined, this)
8380
8894
  ]
8381
8895
  }, undefined, true, undefined, this)
@@ -9361,9 +9875,13 @@ function App() {
9361
9875
  route
9362
9876
  }, undefined, false, undefined, this);
9363
9877
  if (route.startsWith("/traces"))
9364
- return /* @__PURE__ */ u3(TracesView, {}, undefined, false, undefined, this);
9878
+ return /* @__PURE__ */ u3(TracesView, {
9879
+ route
9880
+ }, undefined, false, undefined, this);
9365
9881
  if (route.startsWith("/workers"))
9366
9882
  return /* @__PURE__ */ u3(WorkersView, {}, undefined, false, undefined, this);
9883
+ if (route.startsWith("/generations"))
9884
+ return /* @__PURE__ */ u3(GenerationsView, {}, undefined, false, undefined, this);
9367
9885
  if (route.startsWith("/kv"))
9368
9886
  return /* @__PURE__ */ u3(KvView, {
9369
9887
  route
@@ -9480,8 +9998,26 @@ function App() {
9480
9998
  }, undefined, true, undefined, this),
9481
9999
  /* @__PURE__ */ u3("div", {
9482
10000
  class: "border-t border-border px-2 py-2",
9483
- children: /* @__PURE__ */ u3(ThemeSwitcher, {}, undefined, false, undefined, this)
9484
- }, undefined, false, undefined, this)
10001
+ children: [
10002
+ /* @__PURE__ */ u3("a", {
10003
+ href: "#/generations",
10004
+ class: `flex items-center gap-2 w-full px-3 py-1.5 text-xs font-mono rounded-md transition-colors ${activeSection === "/generations" ? "text-ink bg-panel-hover" : "text-text-muted hover:text-ink hover:bg-panel-hover"}`,
10005
+ children: [
10006
+ /* @__PURE__ */ u3("span", {
10007
+ class: "w-4 text-center opacity-60",
10008
+ children: /* @__PURE__ */ u3(BindingIcon, {
10009
+ type: "workers",
10010
+ class: "w-4 text-center opacity-60 flex items-center justify-center"
10011
+ }, undefined, false, undefined, this)
10012
+ }, undefined, false, undefined, this),
10013
+ /* @__PURE__ */ u3("span", {
10014
+ children: "Generations"
10015
+ }, undefined, false, undefined, this)
10016
+ ]
10017
+ }, undefined, true, undefined, this),
10018
+ /* @__PURE__ */ u3(ThemeSwitcher, {}, undefined, false, undefined, this)
10019
+ ]
10020
+ }, undefined, true, undefined, this)
9485
10021
  ]
9486
10022
  }, undefined, true, undefined, this);
9487
10023
  return /* @__PURE__ */ u3("div", {