@descryy/viz 0.1.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/README.md +40 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/reliability.d.ts +10 -0
- package/dist/reliability.d.ts.map +1 -0
- package/dist/reliability.js +15 -0
- package/dist/reliability.js.map +1 -0
- package/dist/search.d.ts +17 -0
- package/dist/search.d.ts.map +1 -0
- package/dist/search.js +61 -0
- package/dist/search.js.map +1 -0
- package/dist/server.d.ts +23 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +153 -0
- package/dist/server.js.map +1 -0
- package/dist/subgraph.d.ts +40 -0
- package/dist/subgraph.d.ts.map +1 -0
- package/dist/subgraph.js +0 -0
- package/dist/subgraph.js.map +1 -0
- package/package.json +31 -0
- package/public/app.js +686 -0
- package/public/index.html +86 -0
- package/public/style.css +266 -0
package/README.md
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# `@descry/viz`
|
|
2
|
+
|
|
3
|
+
A local, query-driven graph visualizer over an already-built graph. Search a
|
|
4
|
+
symbol, see its bounded neighbourhood — never the whole graph at once.
|
|
5
|
+
|
|
6
|
+
```
|
|
7
|
+
descry viz [--port <number>] [repository-path]
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
Run `descry analyze` first — this reads an existing graph, it does not build one.
|
|
11
|
+
|
|
12
|
+
## What it does
|
|
13
|
+
|
|
14
|
+

|
|
15
|
+
*Search returns a bounded neighbourhood, fitted to the canvas — this is the landing state, one hop, legible at a glance.*
|
|
16
|
+
|
|
17
|
+
- Search by node / file / function; click a result to load its neighbourhood.
|
|
18
|
+
- Depth (1–4 hops), direction (upstream / downstream / both), and node-type / edge-type filter chips.
|
|
19
|
+
- Click a node for its file, line range, language, resolution, reliability class, `producedBy`, and `attrs`.
|
|
20
|
+
- Click an edge for its resolution, confidence, `producedBy`, and a **"why is this connected?"** panel over the edge's own `attrs` — the real evidence, or an honest statement that this edge carries none beyond its core provenance.
|
|
21
|
+
- Upstream / downstream highlighting from any selected node, computed over the loaded view.
|
|
22
|
+
- `API_ENDPOINT` nodes get a visual ring — the cross-language `USES_API` → `API_ENDPOINT` ← `SERVES_API` join is the hardest shape to verify by reading code, and the one most worth seeing.
|
|
23
|
+
- Export the current scoped view as JSON.
|
|
24
|
+
|
|
25
|
+

|
|
26
|
+
*The depth control doing real work — a wider neighbourhood scales to fit rather than running off the canvas.*
|
|
27
|
+
|
|
28
|
+

|
|
29
|
+
*"Why is this connected?" — no explicit reason field on this edge, so it says that, and shows the full attrs instead. This is the disclosure guarantee, made visible.*
|
|
30
|
+
|
|
31
|
+
## What it does not do
|
|
32
|
+
|
|
33
|
+
- **No repository filter.** No per-node `repository` field exists in the graph schema today — a node's id hashes the repository but never hands it back. The filter is visibly present and disabled, with that reason stated, rather than omitted or faked.
|
|
34
|
+
- **No force-directed layout, by choice.** Nodes lay out in deterministic depth-ordered columns from the search seed. "A legible path, not a cloud" was the actual design constraint; a physics simulation fights it at any real fan-out.
|
|
35
|
+
- **No saved or bookmarked views** beyond the one-shot JSON export.
|
|
36
|
+
- **Every edge renders dashed.** Resolution 4 (`observed`) is reserved for the runtime layer, which does not exist yet — nothing in a static-analysis graph can be `resolution === 4`, so the solid "observed" edge style is real but currently unreachable. It stays unreachable, honestly, until `descry-runtime` exists to produce it.
|
|
37
|
+
|
|
38
|
+
## Reading the graph
|
|
39
|
+
|
|
40
|
+
Everything here goes through the existing query layer (`openStore`, `createStoreProvider`, `findNodes`, `bfs`) — no new SQL, no new store access path, no new IR vocabulary.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare const VIZ_PACKAGE = "@descryy/viz";
|
|
2
|
+
export { startVizServer } from "./server.ts";
|
|
3
|
+
export type { VizServer, VizServerOptions } from "./server.ts";
|
|
4
|
+
export { buildSubgraph } from "./subgraph.ts";
|
|
5
|
+
export type { Subgraph, SubgraphOptions } from "./subgraph.ts";
|
|
6
|
+
export { searchNodes } from "./search.ts";
|
|
7
|
+
export type { SearchResult } from "./search.ts";
|
|
8
|
+
export { reliabilityClassFor } from "./reliability.ts";
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,WAAW,iBAAiB,CAAC;AAE1C,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,YAAY,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAE/D,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,YAAY,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAE/D,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export const VIZ_PACKAGE = "@descryy/viz";
|
|
2
|
+
export { startVizServer } from "./server.js";
|
|
3
|
+
export { buildSubgraph } from "./subgraph.js";
|
|
4
|
+
export { searchNodes } from "./search.js";
|
|
5
|
+
export { reliabilityClassFor } from "./reliability.js";
|
|
6
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,WAAW,GAAG,cAAc,CAAC;AAE1C,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAG7C,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAG9C,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAG1C,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Display-only label for the resolution cap the architecture already
|
|
3
|
+
* publishes (README/CLAUDE.md: R0->C, R1->B, R2->B (A for name-level facts
|
|
4
|
+
* only), R3->A, R4->A unconditionally). Restates a decided policy for the
|
|
5
|
+
* inspector panel; computes nothing the engine does not already guarantee,
|
|
6
|
+
* and is never fed back into a query or a certificate.
|
|
7
|
+
*/
|
|
8
|
+
import type { ResolutionLevel } from "@descryy/ir";
|
|
9
|
+
export declare function reliabilityClassFor(resolution: ResolutionLevel): "A" | "B" | "C";
|
|
10
|
+
//# sourceMappingURL=reliability.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reliability.d.ts","sourceRoot":"","sources":["../src/reliability.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEnD,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,eAAe,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAIhF"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Display-only label for the resolution cap the architecture already
|
|
3
|
+
* publishes (README/CLAUDE.md: R0->C, R1->B, R2->B (A for name-level facts
|
|
4
|
+
* only), R3->A, R4->A unconditionally). Restates a decided policy for the
|
|
5
|
+
* inspector panel; computes nothing the engine does not already guarantee,
|
|
6
|
+
* and is never fed back into a query or a certificate.
|
|
7
|
+
*/
|
|
8
|
+
export function reliabilityClassFor(resolution) {
|
|
9
|
+
if (resolution >= 3)
|
|
10
|
+
return "A";
|
|
11
|
+
if (resolution >= 1)
|
|
12
|
+
return "B";
|
|
13
|
+
return "C";
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=reliability.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reliability.js","sourceRoot":"","sources":["../src/reliability.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,MAAM,UAAU,mBAAmB,CAAC,UAA2B;IAC7D,IAAI,UAAU,IAAI,CAAC;QAAE,OAAO,GAAG,CAAC;IAChC,IAAI,UAAU,IAAI,CAAC;QAAE,OAAO,GAAG,CAAC;IAChC,OAAO,GAAG,CAAC;AACb,CAAC"}
|
package/dist/search.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* "Search by node / file / function" — composed entirely from what the query
|
|
3
|
+
* layer already exposes. `findNodes` (store/reader.ts) is an exact match on
|
|
4
|
+
* `name`/`file`, the same primitive `descry-mcp`'s `lookup.ts` uses; a
|
|
5
|
+
* developer typing a partial name needs more than that, so this adds a
|
|
6
|
+
* bounded substring scan over `AdjacencyProvider.nodesOfType` — an existing
|
|
7
|
+
* query, not a new one. No SQL beyond what `findNodes`/`nodesOfType` already
|
|
8
|
+
* run.
|
|
9
|
+
*/
|
|
10
|
+
import type { IRNode } from "@descryy/ir";
|
|
11
|
+
import { type AdjacencyProvider, type SqlDriver } from "@descryy/core";
|
|
12
|
+
export interface SearchResult {
|
|
13
|
+
readonly nodes: readonly IRNode[];
|
|
14
|
+
readonly truncated: boolean;
|
|
15
|
+
}
|
|
16
|
+
export declare function searchNodes(driver: SqlDriver, provider: AdjacencyProvider, query: string): SearchResult;
|
|
17
|
+
//# sourceMappingURL=search.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../src/search.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAY,MAAM,aAAa,CAAC;AACpD,OAAO,EAAa,KAAK,iBAAiB,EAAE,KAAK,SAAS,EAAE,MAAM,eAAe,CAAC;AAsBlF,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;CAC7B;AAED,wBAAgB,WAAW,CAAC,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,iBAAiB,EAAE,KAAK,EAAE,MAAM,GAAG,YAAY,CA6BvG"}
|
package/dist/search.js
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* "Search by node / file / function" — composed entirely from what the query
|
|
3
|
+
* layer already exposes. `findNodes` (store/reader.ts) is an exact match on
|
|
4
|
+
* `name`/`file`, the same primitive `descry-mcp`'s `lookup.ts` uses; a
|
|
5
|
+
* developer typing a partial name needs more than that, so this adds a
|
|
6
|
+
* bounded substring scan over `AdjacencyProvider.nodesOfType` — an existing
|
|
7
|
+
* query, not a new one. No SQL beyond what `findNodes`/`nodesOfType` already
|
|
8
|
+
* run.
|
|
9
|
+
*/
|
|
10
|
+
import { findNodes } from "@descryy/core";
|
|
11
|
+
/** Node types worth scanning by substring. Fileless types (API_ENDPOINT) still carry a name. */
|
|
12
|
+
const SEARCHABLE_TYPES = [
|
|
13
|
+
"FUNCTION",
|
|
14
|
+
"CLASS",
|
|
15
|
+
"MODULE",
|
|
16
|
+
"FILE",
|
|
17
|
+
"API_ROUTE",
|
|
18
|
+
"API_ENDPOINT",
|
|
19
|
+
"COMPONENT",
|
|
20
|
+
"PAGE",
|
|
21
|
+
"MODEL",
|
|
22
|
+
"DTO",
|
|
23
|
+
"DATABASE_TABLE",
|
|
24
|
+
"TEST_CASE",
|
|
25
|
+
];
|
|
26
|
+
/** Per-type cap on the substring scan — a search box, not a set-property query. */
|
|
27
|
+
const SCAN_LIMIT_PER_TYPE = 3_000;
|
|
28
|
+
const MAX_RESULTS = 40;
|
|
29
|
+
export function searchNodes(driver, provider, query) {
|
|
30
|
+
const q = query.trim();
|
|
31
|
+
if (q === "")
|
|
32
|
+
return { nodes: [], truncated: false };
|
|
33
|
+
// Exact match first — cheap, indexed, and correct when the developer typed
|
|
34
|
+
// the whole name.
|
|
35
|
+
const exact = findNodes(driver, { name: q, limit: MAX_RESULTS });
|
|
36
|
+
const seen = new Set(exact.map((n) => n.id));
|
|
37
|
+
const results = [...exact];
|
|
38
|
+
const needle = q.toLowerCase();
|
|
39
|
+
let scannedTruncated = false;
|
|
40
|
+
for (const type of SEARCHABLE_TYPES) {
|
|
41
|
+
if (results.length >= MAX_RESULTS)
|
|
42
|
+
break;
|
|
43
|
+
const set = provider.nodesOfType(type, SCAN_LIMIT_PER_TYPE);
|
|
44
|
+
if (set.truncated)
|
|
45
|
+
scannedTruncated = true;
|
|
46
|
+
for (const node of set.nodes) {
|
|
47
|
+
if (results.length >= MAX_RESULTS)
|
|
48
|
+
break;
|
|
49
|
+
if (seen.has(node.id))
|
|
50
|
+
continue;
|
|
51
|
+
const inName = node.name.toLowerCase().includes(needle);
|
|
52
|
+
const inFile = node.file !== null && node.file.toLowerCase().includes(needle);
|
|
53
|
+
if (!inName && !inFile)
|
|
54
|
+
continue;
|
|
55
|
+
seen.add(node.id);
|
|
56
|
+
results.push(node);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return { nodes: results, truncated: scannedTruncated || results.length >= MAX_RESULTS };
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=search.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search.js","sourceRoot":"","sources":["../src/search.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,EAAE,SAAS,EAA0C,MAAM,eAAe,CAAC;AAElF,gGAAgG;AAChG,MAAM,gBAAgB,GAAwB;IAC5C,UAAU;IACV,OAAO;IACP,QAAQ;IACR,MAAM;IACN,WAAW;IACX,cAAc;IACd,WAAW;IACX,MAAM;IACN,OAAO;IACP,KAAK;IACL,gBAAgB;IAChB,WAAW;CACZ,CAAC;AAEF,mFAAmF;AACnF,MAAM,mBAAmB,GAAG,KAAK,CAAC;AAClC,MAAM,WAAW,GAAG,EAAE,CAAC;AAOvB,MAAM,UAAU,WAAW,CAAC,MAAiB,EAAE,QAA2B,EAAE,KAAa;IACvF,MAAM,CAAC,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IACvB,IAAI,CAAC,KAAK,EAAE;QAAE,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;IAErD,2EAA2E;IAC3E,kBAAkB;IAClB,MAAM,KAAK,GAAG,SAAS,CAAC,MAAM,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC,CAAC;IAEjE,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAC7C,MAAM,OAAO,GAAG,CAAC,GAAG,KAAK,CAAC,CAAC;IAC3B,MAAM,MAAM,GAAG,CAAC,CAAC,WAAW,EAAE,CAAC;IAC/B,IAAI,gBAAgB,GAAG,KAAK,CAAC;IAE7B,KAAK,MAAM,IAAI,IAAI,gBAAgB,EAAE,CAAC;QACpC,IAAI,OAAO,CAAC,MAAM,IAAI,WAAW;YAAE,MAAM;QACzC,MAAM,GAAG,GAAG,QAAQ,CAAC,WAAW,CAAC,IAAI,EAAE,mBAAmB,CAAC,CAAC;QAC5D,IAAI,GAAG,CAAC,SAAS;YAAE,gBAAgB,GAAG,IAAI,CAAC;QAC3C,KAAK,MAAM,IAAI,IAAI,GAAG,CAAC,KAAK,EAAE,CAAC;YAC7B,IAAI,OAAO,CAAC,MAAM,IAAI,WAAW;gBAAE,MAAM;YACzC,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;gBAAE,SAAS;YAChC,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;YACxD,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,KAAK,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;YAC9E,IAAI,CAAC,MAAM,IAAI,CAAC,MAAM;gBAAE,SAAS;YACjC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YAClB,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACrB,CAAC;IACH,CAAC;IAED,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,gBAAgB,IAAI,OAAO,CAAC,MAAM,IAAI,WAAW,EAAE,CAAC;AAC1F,CAAC"}
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `descry viz`'s HTTP layer. Plain `node:http` — no framework, no bundler,
|
|
3
|
+
* per this repo's own decision (tsc only). Serves the static vanilla
|
|
4
|
+
* frontend from `public/` and a handful of read-only JSON endpoints, every
|
|
5
|
+
* one of them a thin wrapper over an existing `@descryy/core` export. No new
|
|
6
|
+
* SQL, no new store access path, no new query beyond `findNodes`, `getNode`,
|
|
7
|
+
* `nodesOfType` and `bfs` (via `buildSubgraph`).
|
|
8
|
+
*
|
|
9
|
+
* Local mode, no hosted backend, no network call this process makes to
|
|
10
|
+
* anywhere: it opens the local graph file read-only and serves one machine.
|
|
11
|
+
*/
|
|
12
|
+
export interface VizServerOptions {
|
|
13
|
+
readonly graphPath: string;
|
|
14
|
+
readonly port?: number;
|
|
15
|
+
readonly readOnly?: boolean;
|
|
16
|
+
}
|
|
17
|
+
export interface VizServer {
|
|
18
|
+
readonly port: number;
|
|
19
|
+
readonly url: string;
|
|
20
|
+
close(): Promise<void>;
|
|
21
|
+
}
|
|
22
|
+
export declare function startVizServer(options: VizServerOptions): Promise<VizServer>;
|
|
23
|
+
//# sourceMappingURL=server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AA4BH,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAeD,wBAAgB,cAAc,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,SAAS,CAAC,CA+B5E"}
|
package/dist/server.js
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `descry viz`'s HTTP layer. Plain `node:http` — no framework, no bundler,
|
|
3
|
+
* per this repo's own decision (tsc only). Serves the static vanilla
|
|
4
|
+
* frontend from `public/` and a handful of read-only JSON endpoints, every
|
|
5
|
+
* one of them a thin wrapper over an existing `@descryy/core` export. No new
|
|
6
|
+
* SQL, no new store access path, no new query beyond `findNodes`, `getNode`,
|
|
7
|
+
* `nodesOfType` and `bfs` (via `buildSubgraph`).
|
|
8
|
+
*
|
|
9
|
+
* Local mode, no hosted backend, no network call this process makes to
|
|
10
|
+
* anywhere: it opens the local graph file read-only and serves one machine.
|
|
11
|
+
*/
|
|
12
|
+
import { createServer } from "node:http";
|
|
13
|
+
import { readFile } from "node:fs/promises";
|
|
14
|
+
import { existsSync } from "node:fs";
|
|
15
|
+
import { extname, join } from "node:path";
|
|
16
|
+
import { fileURLToPath } from "node:url";
|
|
17
|
+
import { isEdgeType, isNodeType } from "@descryy/ir";
|
|
18
|
+
import { createStoreProvider, openStore } from "@descryy/core";
|
|
19
|
+
import { buildSubgraph } from "./subgraph.js";
|
|
20
|
+
import { searchNodes } from "./search.js";
|
|
21
|
+
import { reliabilityClassFor } from "./reliability.js";
|
|
22
|
+
const PUBLIC_DIR = join(fileURLToPath(new URL(".", import.meta.url)), "..", "public");
|
|
23
|
+
// The .js entry's MIME type is IANA's own registered name for the format,
|
|
24
|
+
// not an engine decision about which programming language it understands —
|
|
25
|
+
// allowlisted in boundary-allowlist.json for exactly that reason, on this
|
|
26
|
+
// exact literal.
|
|
27
|
+
const CHARSET = "; charset=utf-8";
|
|
28
|
+
const CONTENT_TYPES = {
|
|
29
|
+
".html": `text/html${CHARSET}`,
|
|
30
|
+
".js": `text/javascript${CHARSET}`,
|
|
31
|
+
".css": `text/css${CHARSET}`,
|
|
32
|
+
".json": `application/json${CHARSET}`,
|
|
33
|
+
};
|
|
34
|
+
function json(res, status, body) {
|
|
35
|
+
const text = JSON.stringify(body);
|
|
36
|
+
res.writeHead(status, { "content-type": "application/json; charset=utf-8" });
|
|
37
|
+
res.end(text);
|
|
38
|
+
}
|
|
39
|
+
function parseTypes(raw, guard) {
|
|
40
|
+
if (raw === null || raw.trim() === "")
|
|
41
|
+
return undefined;
|
|
42
|
+
const values = raw.split(",").map((s) => s.trim()).filter((s) => s !== "");
|
|
43
|
+
const valid = values.filter(guard);
|
|
44
|
+
return valid.length === 0 ? undefined : valid;
|
|
45
|
+
}
|
|
46
|
+
export function startVizServer(options) {
|
|
47
|
+
if (!existsSync(options.graphPath)) {
|
|
48
|
+
throw new Error(`No graph at ${options.graphPath}. Run \`descry analyze\` (or the MCP server's analyze tool) first — ` +
|
|
49
|
+
"the visualizer reads an existing graph, it does not build one.");
|
|
50
|
+
}
|
|
51
|
+
const store = openStore(options.graphPath, { readOnly: options.readOnly ?? true });
|
|
52
|
+
const provider = createStoreProvider(store.driver);
|
|
53
|
+
const server = createServer((req, res) => {
|
|
54
|
+
void handle(req, res, store, provider);
|
|
55
|
+
});
|
|
56
|
+
return new Promise((resolve, reject) => {
|
|
57
|
+
server.on("error", reject);
|
|
58
|
+
server.listen(options.port ?? 0, "127.0.0.1", () => {
|
|
59
|
+
const address = server.address();
|
|
60
|
+
const port = typeof address === "object" && address !== null ? address.port : (options.port ?? 0);
|
|
61
|
+
resolve({
|
|
62
|
+
port,
|
|
63
|
+
url: `http://127.0.0.1:${port}/`,
|
|
64
|
+
close: () => new Promise((res2, rej2) => {
|
|
65
|
+
store.close();
|
|
66
|
+
server.close((err) => (err ? rej2(err) : res2()));
|
|
67
|
+
}),
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
async function handle(req, res, store, provider) {
|
|
73
|
+
const url = new URL(req.url ?? "/", "http://localhost");
|
|
74
|
+
try {
|
|
75
|
+
if (url.pathname === "/api/graph-info") {
|
|
76
|
+
const stamp = provider.baseStamp();
|
|
77
|
+
json(res, 200, { stamp });
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
if (url.pathname === "/api/search") {
|
|
81
|
+
const q = url.searchParams.get("q") ?? "";
|
|
82
|
+
const result = searchNodes(store.driver, provider, q);
|
|
83
|
+
json(res, 200, {
|
|
84
|
+
nodes: result.nodes.map((n) => ({ ...n, reliability: reliabilityClassFor(n.resolution) })),
|
|
85
|
+
truncated: result.truncated,
|
|
86
|
+
});
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
if (url.pathname === "/api/node") {
|
|
90
|
+
const id = url.searchParams.get("id");
|
|
91
|
+
if (id === null)
|
|
92
|
+
return json(res, 400, { error: "id is required" });
|
|
93
|
+
const node = provider.node(id);
|
|
94
|
+
if (node === undefined)
|
|
95
|
+
return json(res, 404, { error: `no node with id ${id}` });
|
|
96
|
+
json(res, 200, { node: { ...node, reliability: reliabilityClassFor(node.resolution) } });
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
if (url.pathname === "/api/subgraph") {
|
|
100
|
+
const idsRaw = url.searchParams.get("ids");
|
|
101
|
+
if (idsRaw === null || idsRaw.trim() === "")
|
|
102
|
+
return json(res, 400, { error: "ids is required" });
|
|
103
|
+
const seeds = idsRaw.split(",").map((s) => s.trim()).filter((s) => s !== "");
|
|
104
|
+
const depthRaw = url.searchParams.get("depth");
|
|
105
|
+
const depth = depthRaw === null ? 2 : Math.max(1, Math.min(6, Number(depthRaw)));
|
|
106
|
+
const direction = url.searchParams.get("direction");
|
|
107
|
+
const budgetRaw = url.searchParams.get("budget");
|
|
108
|
+
const budget = budgetRaw === null ? undefined : Math.max(10, Math.min(500, Number(budgetRaw)));
|
|
109
|
+
const edgeTypes = parseTypes(url.searchParams.get("edgeTypes"), (v) => isEdgeType(v));
|
|
110
|
+
const nodeTypesFilter = parseTypes(url.searchParams.get("nodeTypes"), (v) => isNodeType(v));
|
|
111
|
+
const subgraph = buildSubgraph(provider, seeds, {
|
|
112
|
+
maxDepth: depth,
|
|
113
|
+
...(direction === "in" || direction === "out" || direction === "both" ? { direction } : {}),
|
|
114
|
+
...(edgeTypes === undefined ? {} : { edgeTypes }),
|
|
115
|
+
...(budget === undefined ? {} : { budget }),
|
|
116
|
+
});
|
|
117
|
+
const nodes = nodeTypesFilter === undefined
|
|
118
|
+
? subgraph.nodes
|
|
119
|
+
: subgraph.nodes.filter((n) => nodeTypesFilter.includes(n.type) || seeds.includes(n.id));
|
|
120
|
+
const keepIds = new Set(nodes.map((n) => n.id));
|
|
121
|
+
const edges = subgraph.edges.filter((e) => keepIds.has(e.from) && keepIds.has(e.to));
|
|
122
|
+
json(res, 200, {
|
|
123
|
+
nodes: nodes.map((n) => ({ ...n, reliability: reliabilityClassFor(n.resolution) })),
|
|
124
|
+
edges,
|
|
125
|
+
truncated: subgraph.truncated,
|
|
126
|
+
stamp: subgraph.stamp,
|
|
127
|
+
notes: subgraph.notes,
|
|
128
|
+
});
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
// Static assets. `public/` is small and fixed at build time — a plain path
|
|
132
|
+
// join with a traversal guard is sufficient, no framework needed for it.
|
|
133
|
+
const pathname = url.pathname === "/" ? "/index.html" : url.pathname;
|
|
134
|
+
if (pathname.includes(".."))
|
|
135
|
+
return json(res, 400, { error: "bad path" });
|
|
136
|
+
const filePath = join(PUBLIC_DIR, pathname);
|
|
137
|
+
if (!filePath.startsWith(PUBLIC_DIR))
|
|
138
|
+
return json(res, 400, { error: "bad path" });
|
|
139
|
+
const body = await readFile(filePath);
|
|
140
|
+
const type = CONTENT_TYPES[extname(filePath)] ?? "application/octet-stream";
|
|
141
|
+
res.writeHead(200, { "content-type": type });
|
|
142
|
+
res.end(body);
|
|
143
|
+
}
|
|
144
|
+
catch (error) {
|
|
145
|
+
if (error.code === "ENOENT") {
|
|
146
|
+
res.writeHead(404, { "content-type": "text/plain" });
|
|
147
|
+
res.end("not found");
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
json(res, 500, { error: error.message });
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one piece of this package where a bug would be invisible and expensive:
|
|
3
|
+
* turning a search hit into a bounded neighbourhood, never the whole graph.
|
|
4
|
+
*
|
|
5
|
+
* "Don't try to visualize the entire 10,000+ node graph at once" is the
|
|
6
|
+
* user's own constraint. `bfs` (query/traverse.ts) already enforces a node
|
|
7
|
+
* budget and a depth cap — this module does not reimplement that, it composes
|
|
8
|
+
* it. What it adds is the one thing `bfs` does not do: after reachability is
|
|
9
|
+
* known, collect *every* edge between two reached nodes, not just the
|
|
10
|
+
* BFS-tree edge that first discovered each one. A caller-reaches-endpoint
|
|
11
|
+
* cross-language join is exactly the shape where a second real edge between
|
|
12
|
+
* two already-reached nodes would otherwise be invisible.
|
|
13
|
+
*/
|
|
14
|
+
import type { EdgeType, IREdge, IRNode } from "@descryy/ir";
|
|
15
|
+
import { type AdjacencyProvider, type Direction, type GraphStamp } from "@descryy/core";
|
|
16
|
+
export interface SubgraphOptions {
|
|
17
|
+
readonly direction?: Direction;
|
|
18
|
+
readonly maxDepth?: number;
|
|
19
|
+
/** Node budget. Defaults to 150 — the user's own "hairball" line is ~200. */
|
|
20
|
+
readonly budget?: number;
|
|
21
|
+
readonly edgeTypes?: readonly EdgeType[];
|
|
22
|
+
}
|
|
23
|
+
export interface Subgraph {
|
|
24
|
+
readonly nodes: readonly IRNode[];
|
|
25
|
+
readonly edges: readonly IREdge[];
|
|
26
|
+
readonly truncated: boolean;
|
|
27
|
+
readonly stamp: GraphStamp;
|
|
28
|
+
readonly notes: readonly string[];
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Seed(s) in, a bounded, renderable neighbourhood out.
|
|
32
|
+
*
|
|
33
|
+
* `edgeTypes` filters which edges *expand the walk* (an unchecked filter
|
|
34
|
+
* would silently widen the neighbourhood past what a filtered view implies)
|
|
35
|
+
* and, identically, which edges are collected in the second pass below — the
|
|
36
|
+
* two must agree or the rendered graph would show an edge type the user just
|
|
37
|
+
* filtered out.
|
|
38
|
+
*/
|
|
39
|
+
export declare function buildSubgraph(provider: AdjacencyProvider, seeds: readonly string[], options?: SubgraphOptions): Subgraph;
|
|
40
|
+
//# sourceMappingURL=subgraph.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"subgraph.d.ts","sourceRoot":"","sources":["../src/subgraph.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE5D,OAAO,EAAO,KAAK,iBAAiB,EAAE,KAAK,SAAS,EAAE,KAAK,UAAU,EAAE,MAAM,eAAe,CAAC;AAE7F,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,CAAC;IAC/B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,6EAA6E;IAC7E,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,QAAQ,EAAE,CAAC;CAC1C;AAED,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;IAC3B,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;CACnC;AAID;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,iBAAiB,EAC3B,KAAK,EAAE,SAAS,MAAM,EAAE,EACxB,OAAO,GAAE,eAAoB,GAC5B,QAAQ,CAqDV"}
|
package/dist/subgraph.js
ADDED
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"subgraph.js","sourceRoot":"","sources":["../src/subgraph.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@descryy/viz",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "A query-driven graph visualizer over the existing query layer: search a symbol, render its bounded neighbourhood, inspect the evidence behind a node or an edge. Never the whole graph at once.",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"engines": {
|
|
8
|
+
"node": ">=22.5"
|
|
9
|
+
},
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"default": "./dist/index.js"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist",
|
|
18
|
+
"public"
|
|
19
|
+
],
|
|
20
|
+
"publishConfig": {
|
|
21
|
+
"registry": "https://registry.npmjs.org",
|
|
22
|
+
"access": "public"
|
|
23
|
+
},
|
|
24
|
+
"scripts": {
|
|
25
|
+
"build": "tsc -b"
|
|
26
|
+
},
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"@descryy/core": "0.1.1",
|
|
29
|
+
"@descryy/ir": "0.1.1"
|
|
30
|
+
}
|
|
31
|
+
}
|