traceTree
One trace (an id, or the start of one), as a tree: its spans in the order they started, each with its depth (a span whose parent isn't kept here, a caller's, is a root of what this server did) and what it's of; the build that answered; and the jobs it enqueued, each a trace of its own. "none" or "many" if the id names no trace or more than one.
import type { Codebase } from "#codebase/tcmts/cli/codebase"; import { spanOf } from "#codebase/tcmts/cli/traced"; import type { Of } from "#codebase/tcmts/cli/traced"; import { shortHash } from "#codebase/tcmts/core/hash"; import type { DatabaseSync } from "node:sqlite"; export function traceTree( db: DatabaseSync, cb: Codebase, id: string, ): | { trace: string; start: number; build?: string; spans: (Of & { id: string; kind: string; name: string; start: number; dur: number; error: string | null; attrs: Record<string, unknown>; link: string | null; depth: number; from?: Of; })[]; caused: (Of & { trace: string })[]; } | "none" | "many" { const ids = db .prepare("SELECT DISTINCT trace FROM spans WHERE trace LIKE ? LIMIT 2") .all(`${id.toLowerCase()}%`) as { trace: string }[]; if (ids.length !== 1) return ids.length ? "many" : "none"; const trace = ids[0]!.trace; const spans = db .prepare("SELECT * FROM spans WHERE trace = ? ORDER BY start") .all(trace) as unknown as { id: string; parent: string | null; kind: string; name: string; hash: string | null; build: string | null; start: number; dur: number; error: string | null; attrs: string; link: string | null; }[]; const here = new Set(spans.map((s) => s.id)); const children = new Map<string | null, typeof spans>(); for (const s of spans) { const up = s.parent && here.has(s.parent) ? s.parent : null; children.set(up, [...(children.get(up) ?? []), s]); } const ordered: ReturnType<typeof shown>[] = []; const shown = (s: (typeof spans)[number], depth: number) => { const attrs = JSON.parse(s.attrs) as Record<string, unknown>; const from = attrs["tcm.from"]; return { ...spanOf(cb, s), id: s.id, kind: s.kind, name: s.name, start: s.start, dur: s.dur, error: s.error, attrs, link: s.link, depth, ...(typeof from === "string" ? { from: spanOf(cb, { hash: from, name: shortHash(from) }) } : {}), }; }; const walk = (s: (typeof spans)[number], depth: number) => { ordered.push(shown(s, depth)); for (const c of children.get(s.id) ?? []) walk(c, depth + 1); }; for (const s of children.get(null) ?? []) walk(s, 0); const build = spans.find((s) => s.build)?.build; const caused = db .prepare("SELECT DISTINCT trace, name, hash FROM spans WHERE link LIKE ?") .all(`00-${trace}-%`) as { trace: string; name: string; hash: string | null }[]; return { trace, start: spans[0]!.start, ...(build ? { build } : {}), spans: ordered, caused: caused.map((c) => ({ ...spanOf(cb, c), trace: c.trace })), }; }