| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589 |
- /**
- * The wire shapes of the graph API — types only, no runtime.
- *
- * These mirror the server's payloads (`src/ui-server/api/`, CG-42) rather than
- * re-deriving them: the API is versioned with the binary that serves it, so a
- * field the server stopped sending should break the type-check here, not
- * surface as `undefined` in a rail three screens later.
- *
- * They are also the vocabulary of {@link GraphAdapter} (`adapter.ts`): a host
- * embedding these components answers in exactly these shapes, whether it is
- * reading them over HTTP from `codegraph ui` or building them in-process from
- * its own engine. Keeping them in a file with no imports and no side effects is
- * what lets a host depend on the vocabulary without pulling in the transport.
- */
- import type { WireHighlight } from './highlight';
- /* ---------------------------------------------------------------- shapes -- */
- export type NodeKind = string;
- export type EdgeKind = string;
- export interface WireNodeRef {
- id: string;
- kind: NodeKind;
- name: string;
- qualifiedName: string;
- /** Project-relative, forward slashes on every platform. */
- file: string;
- line: number;
- endLine: number;
- language: string;
- signature?: string;
- exported?: boolean;
- /** Lives in a file that looks like test or fixture code. */
- test: boolean;
- }
- export interface WireNodeDetail extends WireNodeRef {
- startColumn: number;
- endColumn: number;
- docstring?: string;
- visibility?: string;
- async?: boolean;
- static?: boolean;
- abstract?: boolean;
- decorators?: string[];
- typeParameters?: string[];
- returnType?: string;
- lines: number;
- }
- export interface WireMember extends WireNodeRef {
- parentId: string;
- /** 1 = a direct member; 2 = a member of a member (a method inside a file's class). */
- depth: number;
- fanIn: number;
- fanOut: number;
- }
- export interface WireEdge {
- kind: EdgeKind;
- line?: number;
- col?: number;
- confidence?: number;
- resolvedBy?: string;
- provenance?: string;
- synthesizedBy?: string;
- via?: string;
- registeredAt?: string;
- valueRef?: boolean;
- }
- /** Every edge between the focal symbol and ONE other symbol, as a single row. */
- export interface WireRelation {
- node: WireNodeRef;
- edgeKinds: EdgeKind[];
- edges: WireEdge[];
- edgeCount: number;
- /** Distinct call-site lines, ascending — what the gutter ports anchor to. */
- lines: number[];
- confidence: number | null;
- uncertain: boolean;
- synthesized: boolean;
- fanIn?: number;
- hub?: boolean;
- }
- export interface WireList<T> {
- total: number;
- shown: number;
- truncated: boolean;
- items: T[];
- }
- export interface WireTestSummary {
- reached: boolean;
- hops: number | null;
- fileCount: number;
- files: string[];
- /** False weakens the claim to "no test calls this directly" — see the server. */
- exhaustive: boolean;
- hopsSearched: number;
- }
- export interface WireOutsideIndex {
- total: number;
- byKind: Record<string, number>;
- samples: Array<{ name: string; kind: string; line?: number; col?: number }>;
- }
- export interface WireBlastSummary {
- direct: number;
- withinHops: number;
- hops: number;
- files: number;
- testFiles: number;
- routes: number;
- topFiles: Array<{ file: string; symbols: number; test: boolean }>;
- }
- export interface WireSymbolPayload {
- node: WireNodeDetail;
- /** Outermost first: file, then module/class, then the symbol's own parent. */
- ancestors: WireNodeRef[];
- members: WireList<WireMember>;
- incoming: WireList<WireRelation>;
- outgoing: WireList<WireRelation>;
- typesUsed: WireRelation[];
- counts: {
- callers: number;
- callees: number;
- typesUsed: number;
- fanIn: number;
- fanOut: number;
- members: number;
- hub: boolean;
- };
- tests: WireTestSummary;
- outsideIndex: WireOutsideIndex;
- blast: WireBlastSummary | null;
- /** The file changed on disk since the index — line ranges may be shifted. */
- drift: boolean;
- }
- export interface WireSource {
- file: string;
- language: string;
- drift: boolean;
- /**
- * Which numbering `lines` belong to. `'indexed'` — the file matches the
- * index. `'current'` — it drifted and we asked for the bytes anyway
- * (`ondrift: 'current'`), so nothing the graph holds about this file lines up
- * with them. `'none'` — it drifted and no slice came back.
- */
- showing: 'indexed' | 'current' | 'none';
- contentHash: string;
- indexedAt: number;
- generated: boolean;
- totalLines: number | null;
- from?: number;
- to?: number;
- /** Absent when the file drifted and `ondrift` was left at its default. */
- lines?: string[];
- truncated?: boolean;
- reason?: string;
- /**
- * The same lines, classified by the server's tree-sitter parse — one entry
- * per line, each a list of `[classId, text]` pairs indexed into `classes`.
- * Absent whenever `lines` is, and `engine: 'plain'` whenever no grammar
- * covers the file. See `lib/highlight.ts`.
- */
- highlight?: WireHighlight;
- }
- /* ------------------------------------------------------------- file view -- */
- /** A row in the file outline — a symbol, its nesting and its edge counts. */
- export interface WireOutlineEntry extends WireNodeRef {
- /** Containing symbol within this file, or null for a top-level one. */
- parentId: string | null;
- /** Nesting depth from the top level of the file, starting at 0. */
- depth: number;
- fanIn: number;
- fanOut: number;
- }
- /** One file at the far end of an import rail, with the symbols the edges name. */
- export interface WireImportRow {
- file: string;
- test: boolean;
- symbols: Array<{ id: string; name: string; kind: string; line: number }>;
- symbolCount: number;
- }
- export interface WireFilePayload {
- file: {
- path: string;
- language: string;
- size: number;
- modifiedAt: number;
- indexedAt: number;
- contentHash: string;
- nodeCount: number;
- generated: boolean;
- test: boolean;
- errors: string[];
- /** The file node's own id, so the viewer can open the file AS a symbol. */
- id: string | null;
- };
- /** Calls made outside every definition — module-level code. */
- topLevel: { calls: number };
- /** The file changed on disk since it was indexed; the outline's lines shifted. */
- drift: boolean;
- outline: WireList<WireOutlineEntry>;
- /** `imports` edges only — a subset of `dependencies`, with symbol names. */
- imports: WireList<WireImportRow>;
- importedBy: WireList<WireImportRow>;
- /** Import statements that resolved to nothing indexed: packages, builtins. */
- unresolvedImports: Array<{ name: string; line: number }>;
- /** Every file this one reaches by any cross-file edge — `getFileDependencies`. */
- dependencies: string[];
- /** Every file that reaches into this one — `getFileDependents`. */
- dependents: string[];
- }
- /* ------------------------------------------------ whole-file source view -- */
- /** A reference the resolver never landed: a gutter port with no destination. */
- export interface WireFileOutsideRef {
- line: number;
- col: number;
- name: string;
- kind: string;
- }
- /** Every edge from ONE symbol in a file to ONE symbol anywhere. */
- export interface WireFileCall {
- /** The symbol making the calls — the file node itself for top-level code. */
- ownerId: string;
- ownerLine: number;
- relation: WireRelation;
- }
- export interface WireFileCodePayload {
- file: {
- path: string;
- language: string;
- size: number;
- indexedAt: number;
- contentHash: string;
- generated: boolean;
- test: boolean;
- errors: string[];
- id: string | null;
- /** Lines on disk now — the height of the scrolling document. */
- totalLines: number | null;
- };
- drift: boolean;
- reason?: string;
- outline: WireList<WireOutlineEntry>;
- calls: WireList<WireFileCall>;
- outside: WireList<WireFileOutsideRef>;
- /** Calls landing on a definition in this same file — the arc diagram's total. */
- intraFileCalls: number;
- timing: { elapsedMs: number };
- }
- export interface WireBlastScale {
- maxDirect: number;
- maxWithinHops: number;
- hops: number;
- sampled: number;
- estimated: boolean;
- }
- /* ------------------------------------------------------- search palette -- */
- /** How a result's text matched the query — the server's primary sort key. */
- export type MatchKind = 'exact' | 'prefix' | 'substring' | 'qualified' | 'file' | 'related';
- export interface WireSearchResult extends WireNodeRef {
- matchKind: MatchKind;
- }
- export interface WireSearchGroup {
- kind: NodeKind;
- count: number;
- items: WireSearchResult[];
- }
- export interface WireSearch {
- query: string;
- /** The free-text part, with any `kind:` / `lang:` / `path:` filters removed. */
- text: string;
- filters: { kinds: string[]; languages: string[]; paths: string[]; names: string[] };
- results: WireList<WireSearchResult>;
- /** Kind buckets in ranked order — flattening them reproduces the ranking. */
- groups: WireSearchGroup[];
- }
- export interface WireNodeRefs {
- items: WireNodeRef[];
- /** Ids that name nothing in this index — a stale link, not an error. */
- missing: string[];
- }
- /* --------------------------------------------------------------- routes -- */
- /** One row of the URL -> handler map (`/api/routes`). */
- export interface WireRoute {
- /** The route node's name, verbatim: "POST /v1/users/{id}". */
- url: string;
- /** The verb, when the name leads with one. Null for a file-routed page. */
- method: string | null;
- /** The URL without the verb — the same string as `url` when there is none. */
- path: string;
- handler: string;
- handlerKind: string;
- /** Where the request is SERVED. */
- file: string;
- line: number;
- handlerId: string | null;
- /** Where the URL is REGISTERED — the router file, which is how routes group. */
- routeFile: string;
- routeLine: number;
- routeId: string;
- }
- export interface WireRoutes {
- routed: boolean;
- /** Every URL the index holds, whether or not its handler resolved. */
- routeCount: number;
- /** Rows in `entries` — the ones whose handler the manifest could name. */
- shown: number;
- truncated: boolean;
- topHandlerFile: string | null;
- topHandlerFileCount: number;
- entries: WireRoute[];
- }
- /* ---------------------------------------------------------- entry points -- */
- export interface WireEntryRoute {
- /** The route node's name, verbatim: "POST /v1/users/{id}". */
- url: string;
- /** The verb, when the name leads with one. Null for a file-routed page. */
- method: string | null;
- /** The URL without the verb — the same string as `url` when there is none. */
- path: string;
- handler: string;
- handlerKind: string;
- /** Where the request is SERVED. */
- file: string;
- line: number;
- handlerId: string | null;
- /** Where the URL is REGISTERED — the router file, which is how routes group. */
- routeFile: string;
- routeLine: number;
- routeId: string;
- }
- export interface WireEntryFile extends WireNodeRef {
- /** Calls and instantiations made at the top level of the file. */
- calls: number;
- /** Distinct other files this one's symbols reach. */
- reaches: number;
- /** Other files reaching into this one. Zero means nothing imports it. */
- dependents: number;
- }
- export interface WireEntryHub extends WireNodeRef {
- dependents: number;
- }
- export interface WireEntryTest extends WireNodeRef {
- /** Distinct other files this test reaches — what it exercises. */
- reaches: number;
- /** References behind that reach. */
- refs: number;
- }
- export interface WireEntryPoints {
- /** Frameworks the resolver detected — named in the Routes header. */
- frameworks: string[];
- routes: {
- routed: boolean;
- /** Every `route` node in the graph, resolved handler or not. */
- routeCount: number;
- items: WireList<WireEntryRoute>;
- };
- /** `total` is a floor on `files` and `hubs`; on `tests` it is exact. */
- files: WireList<WireEntryFile>;
- tests: WireList<WireEntryTest>;
- hubs: WireList<WireEntryHub>;
- index: { lastIndexedAt: number | null; files: number };
- timing: { elapsedMs: number; cached: boolean };
- }
- export interface WireStats {
- project: { root: string; name: string };
- index: {
- state: string | null;
- lastIndexedAt: number | null;
- stale: boolean;
- version: string | null;
- extractionVersion: number | null;
- backend: string;
- journalMode: string;
- pendingReferences: number;
- generatedFiles: number;
- watching: boolean;
- watcherDegraded: boolean;
- };
- graph: {
- nodes: number;
- edges: number;
- files: number;
- nodesByKind: Record<string, number>;
- edgesByKind: Record<string, number>;
- filesByLanguage: Record<string, number>;
- dbSizeBytes: number;
- walSizeBytes: number;
- };
- frameworks: string[];
- thresholds: { hub: number; uncertainBelow: number };
- blastScale: WireBlastScale;
- }
- /* ------------------------------------------------------------- flow strip -- */
- export interface WireFlowEdge extends WireEdge {
- /** The link's label: "calls", "via callback · registered at file:line". */
- label: string;
- /** This hop reads callee → caller — the reader stepped UP into it. */
- upward: boolean;
- /** Confidence below 0.6: the link is dashed `2 3`. */
- uncertain: boolean;
- /** A synthesized dynamic-dispatch bridge: dashed `5 3`. */
- synthesized: boolean;
- }
- export interface WireFlowSource {
- file: string;
- language: string;
- from: number;
- to: number;
- /** Absent when `drift` — a mis-sliced window is worse than an empty card. */
- lines?: string[];
- highlight?: WireHighlight;
- drift: boolean;
- reason?: string;
- }
- /** The call site a card is opened at — the identifier drawn as an accent link. */
- export interface WireFlowCallRef {
- line: number;
- col: number | null;
- name: string;
- targetId: string;
- /** The link points back at the previous card, not on to the next one. */
- backwards: boolean;
- }
- export interface WireFlowHop {
- node: WireNodeRef;
- /** The edge from the PREVIOUS hop into this one; null on the first. */
- edge: WireFlowEdge | null;
- callRef: WireFlowCallRef | null;
- source: WireFlowSource | null;
- }
- /** One plausible runtime target of a keyed dispatch — a clickable cap row. */
- export interface WireBoundaryCandidate {
- node: WireNodeRef;
- display: string;
- named: boolean;
- }
- /** A dynamic-dispatch site: the form, the key when it is visible, the targets. */
- export interface WireBoundarySite {
- form: string;
- label: string;
- snippet: string;
- line: number;
- key: string | null;
- keyIsType: boolean;
- moreSites: number;
- candidates: WireBoundaryCandidate[];
- candidateNote: string | null;
- }
- export interface WireFlowContinuation {
- node: WireNodeRef;
- line: number | null;
- confidence: number | null;
- }
- /** Where the graph stops — the strip's end cap (design spec §3.5). */
- export interface WireFlowBoundary {
- node: WireNodeRef;
- sites: WireBoundarySite[];
- uncertain: WireList<WireFlowContinuation>;
- further: WireList<WireFlowContinuation>;
- missed: WireNodeRef[];
- }
- export interface WireFlow {
- id: string;
- /** "execute → rowToFileRecord", for the header's flow picker. */
- label: string;
- hops: WireFlowHop[];
- /** Null on a flow that reaches everything it was asked about. */
- boundary: WireFlowBoundary | null;
- /** One card at the dispatch site, not a path: the answer ran out here. */
- partial: boolean;
- }
- export interface WireFlowAmbiguity {
- token: string;
- chosen: WireNodeRef | null;
- others: WireNodeRef[];
- }
- export interface WireFlowPayload {
- query: {
- kind: 'directed' | 'symbols' | 'trail';
- from: string | null;
- to: string | null;
- symbols: string[];
- };
- flows: WireFlow[];
- ambiguous: WireFlowAmbiguity[];
- /** Tokens that named nothing in this index. */
- unresolved: string[];
- /** Why there is no flow, when there is none. */
- reason: string | null;
- index: { lastIndexedAt: number | null; edges: number; files: number };
- timing: { elapsedMs: number };
- }
- /* -------------------------------------------------------------- the map -- */
- export interface WireMapModule {
- /** Directory path, the `(root files)` bucket, or a façade file's own path. */
- id: string;
- label: string;
- files: number;
- symbols: number;
- languages: Array<{ language: string; files: number }>;
- /** More than half its files are tests. */
- test: boolean;
- /** A single file kept out of the root bucket because it is the façade. */
- facade: boolean;
- /** Its files, capped — the side panel's list when the module is selected. */
- fileList: { total: number; shown: number; truncated: boolean; items: string[] };
- }
- export interface WireMapLink {
- source: string;
- target: string;
- /** Every confident cross-module edge behind this link. */
- count: number;
- /**
- * The subset resolved through an import, a qualified name, an inheritance
- * clause or a typed receiver — what the layering trusts.
- */
- declared: number;
- byKind: Array<{ kind: EdgeKind; count: number }>;
- topPairs: Array<{ from: string; to: string; count: number; declared: number }>;
- }
- export interface WireMapCycle {
- size: number;
- files: string[];
- modules: string[];
- }
- export interface WireMapPayload {
- root: string;
- depth: number;
- roots: Array<{ root: string; label: string; files: number }>;
- modules: WireMapModule[];
- links: WireMapLink[];
- cycles: { total: number; shown: number; truncated: boolean; items: WireMapCycle[] };
- excluded: { uncertainEdges: number; confidenceBelow: number };
- index: { lastIndexedAt: number | null; edges: number; files: number };
- timing: { elapsedMs: number; cached: boolean };
- }
|