ts-knowledge-graph 0.1.1 → 0.1.4
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 +104 -43
- package/contribs/web_visualisation/README.md +83 -0
- package/contribs/web_visualisation/web/css/style.css +219 -0
- package/contribs/web_visualisation/web/data/.gitignore +3 -0
- package/contribs/web_visualisation/web/data/kind_descriptions.js +38 -0
- package/contribs/web_visualisation/web/index.html +74 -0
- package/contribs/web_visualisation/web/js/app.js +910 -0
- package/contribs/web_visualisation/web/tsconfig.json +18 -0
- package/contribs/web_visualisation/web/types/app_globals.d.ts +146 -0
- package/dist/benchmark/benchmark_stats.d.ts +41 -0
- package/dist/benchmark/benchmark_stats.d.ts.map +1 -0
- package/dist/benchmark/benchmark_stats.js +61 -0
- package/dist/benchmark/benchmark_stats.js.map +1 -0
- package/dist/benchmark/node_benchmark.d.ts +78 -0
- package/dist/benchmark/node_benchmark.d.ts.map +1 -0
- package/dist/benchmark/node_benchmark.js +112 -0
- package/dist/benchmark/node_benchmark.js.map +1 -0
- package/dist/cli.d.ts +0 -9
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +32 -208
- package/dist/cli.js.map +1 -1
- package/dist/commands/benchmark_command.d.ts +11 -0
- package/dist/commands/benchmark_command.d.ts.map +1 -0
- package/dist/commands/benchmark_command.js +91 -0
- package/dist/commands/benchmark_command.js.map +1 -0
- package/dist/commands/blast_radius_command.d.ts +5 -0
- package/dist/commands/blast_radius_command.d.ts.map +1 -0
- package/dist/commands/blast_radius_command.js +18 -0
- package/dist/commands/blast_radius_command.js.map +1 -0
- package/dist/commands/calls_command.d.ts +5 -0
- package/dist/commands/calls_command.d.ts.map +1 -0
- package/dist/commands/calls_command.js +7 -0
- package/dist/commands/calls_command.js.map +1 -0
- package/dist/commands/command_helpers.d.ts +15 -0
- package/dist/commands/command_helpers.d.ts.map +1 -0
- package/dist/commands/command_helpers.js +61 -0
- package/dist/commands/command_helpers.js.map +1 -0
- package/dist/commands/cost_command.d.ts +13 -0
- package/dist/commands/cost_command.d.ts.map +1 -0
- package/dist/commands/cost_command.js +122 -0
- package/dist/commands/cost_command.js.map +1 -0
- package/dist/commands/dead_exports_command.d.ts +5 -0
- package/dist/commands/dead_exports_command.d.ts.map +1 -0
- package/dist/commands/dead_exports_command.js +7 -0
- package/dist/commands/dead_exports_command.js.map +1 -0
- package/dist/commands/enrich_command.d.ts +7 -0
- package/dist/commands/enrich_command.d.ts.map +1 -0
- package/dist/commands/enrich_command.js +62 -0
- package/dist/commands/enrich_command.js.map +1 -0
- package/dist/commands/extract_command.d.ts +8 -0
- package/dist/commands/extract_command.d.ts.map +1 -0
- package/dist/commands/extract_command.js +49 -0
- package/dist/commands/extract_command.js.map +1 -0
- package/dist/commands/find_command.d.ts +5 -0
- package/dist/commands/find_command.d.ts.map +1 -0
- package/dist/commands/find_command.js +7 -0
- package/dist/commands/find_command.js.map +1 -0
- package/dist/commands/hotspots_command.d.ts +7 -0
- package/dist/commands/hotspots_command.d.ts.map +1 -0
- package/dist/commands/hotspots_command.js +67 -0
- package/dist/commands/hotspots_command.js.map +1 -0
- package/dist/commands/install_command.d.ts +15 -0
- package/dist/commands/install_command.d.ts.map +1 -0
- package/dist/commands/install_command.js +41 -0
- package/dist/commands/install_command.js.map +1 -0
- package/dist/commands/load_command.d.ts +6 -0
- package/dist/commands/load_command.d.ts.map +1 -0
- package/dist/commands/load_command.js +30 -0
- package/dist/commands/load_command.js.map +1 -0
- package/dist/commands/neighbors_command.d.ts +5 -0
- package/dist/commands/neighbors_command.d.ts.map +1 -0
- package/dist/commands/neighbors_command.js +17 -0
- package/dist/commands/neighbors_command.js.map +1 -0
- package/dist/commands/references_command.d.ts +5 -0
- package/dist/commands/references_command.d.ts.map +1 -0
- package/dist/commands/references_command.js +17 -0
- package/dist/commands/references_command.js.map +1 -0
- package/dist/commands/verify_command.d.ts +8 -0
- package/dist/commands/verify_command.d.ts.map +1 -0
- package/dist/commands/verify_command.js +57 -0
- package/dist/commands/verify_command.js.map +1 -0
- package/dist/commands/web_command.d.ts +46 -0
- package/dist/commands/web_command.d.ts.map +1 -0
- package/dist/commands/web_command.js +226 -0
- package/dist/commands/web_command.js.map +1 -0
- package/dist/commands/who_calls_command.d.ts +5 -0
- package/dist/commands/who_calls_command.d.ts.map +1 -0
- package/dist/commands/who_calls_command.js +7 -0
- package/dist/commands/who_calls_command.js.map +1 -0
- package/dist/enrich/cpu_profile.d.ts +127 -0
- package/dist/enrich/cpu_profile.d.ts.map +1 -0
- package/dist/enrich/cpu_profile.js +97 -0
- package/dist/enrich/cpu_profile.js.map +1 -0
- package/dist/enrich/runtime_enricher.d.ts +56 -0
- package/dist/enrich/runtime_enricher.d.ts.map +1 -0
- package/dist/enrich/runtime_enricher.js +80 -0
- package/dist/enrich/runtime_enricher.js.map +1 -0
- package/dist/enrich/runtime_join.d.ts +100 -0
- package/dist/enrich/runtime_join.d.ts.map +1 -0
- package/dist/enrich/runtime_join.js +227 -0
- package/dist/enrich/runtime_join.js.map +1 -0
- package/dist/extract/api_extractor.d.ts +24 -0
- package/dist/extract/api_extractor.d.ts.map +1 -0
- package/dist/extract/api_extractor.js +71 -0
- package/dist/extract/api_extractor.js.map +1 -0
- package/dist/extract/config_extractor.d.ts +22 -0
- package/dist/extract/config_extractor.d.ts.map +1 -0
- package/dist/extract/config_extractor.js +61 -0
- package/dist/extract/config_extractor.js.map +1 -0
- package/dist/extract/endpoint_extractor.d.ts +36 -0
- package/dist/extract/endpoint_extractor.d.ts.map +1 -0
- package/dist/extract/endpoint_extractor.js +117 -0
- package/dist/extract/endpoint_extractor.js.map +1 -0
- package/dist/extract/{graph-builder.d.ts → graph_builder.d.ts} +9 -1
- package/dist/extract/graph_builder.d.ts.map +1 -0
- package/dist/extract/graph_builder.js +61 -0
- package/dist/extract/graph_builder.js.map +1 -0
- package/dist/extract/node_id.d.ts +24 -0
- package/dist/extract/node_id.d.ts.map +1 -0
- package/dist/extract/node_id.js +44 -0
- package/dist/extract/node_id.js.map +1 -0
- package/dist/extract/{project-loader.d.ts → project_loader.d.ts} +1 -1
- package/dist/extract/project_loader.d.ts.map +1 -0
- package/dist/extract/{project-loader.js → project_loader.js} +1 -1
- package/dist/extract/{project-loader.js.map → project_loader.js.map} +1 -1
- package/dist/extract/scope_resolver.d.ts +22 -0
- package/dist/extract/scope_resolver.d.ts.map +1 -0
- package/dist/extract/scope_resolver.js +53 -0
- package/dist/extract/scope_resolver.js.map +1 -0
- package/dist/extract/semantic_extractor.d.ts +47 -0
- package/dist/extract/semantic_extractor.d.ts.map +1 -0
- package/dist/extract/{semantic-extractor.js → semantic_extractor.js} +98 -4
- package/dist/extract/semantic_extractor.js.map +1 -0
- package/dist/extract/{structural-extractor.d.ts → structural_extractor.d.ts} +7 -1
- package/dist/extract/{structural-extractor.d.ts.map → structural_extractor.d.ts.map} +1 -1
- package/dist/extract/{structural-extractor.js → structural_extractor.js} +24 -14
- package/dist/extract/structural_extractor.js.map +1 -0
- package/dist/project_root.d.ts +7 -0
- package/dist/project_root.d.ts.map +1 -0
- package/dist/project_root.js +9 -0
- package/dist/project_root.js.map +1 -0
- package/dist/query/graph_query.d.ts +262 -0
- package/dist/query/graph_query.d.ts.map +1 -0
- package/dist/query/graph_query.js +604 -0
- package/dist/query/graph_query.js.map +1 -0
- package/dist/schema/edge.d.ts +40 -5
- package/dist/schema/edge.d.ts.map +1 -1
- package/dist/schema/edge.js +70 -0
- package/dist/schema/edge.js.map +1 -1
- package/dist/schema/node.d.ts +20 -5
- package/dist/schema/node.d.ts.map +1 -1
- package/dist/schema/node.js +36 -0
- package/dist/schema/node.js.map +1 -1
- package/dist/schema/runtime_manifest.d.ts +36 -0
- package/dist/schema/runtime_manifest.d.ts.map +1 -0
- package/dist/schema/runtime_manifest.js +23 -0
- package/dist/schema/runtime_manifest.js.map +1 -0
- package/dist/store/{jsonl-reader.d.ts → jsonl_reader.d.ts} +1 -1
- package/dist/store/{jsonl-reader.d.ts.map → jsonl_reader.d.ts.map} +1 -1
- package/dist/store/{jsonl-reader.js → jsonl_reader.js} +1 -1
- package/dist/store/{jsonl-reader.js.map → jsonl_reader.js.map} +1 -1
- package/dist/store/{jsonl-store.d.ts → jsonl_store.d.ts} +1 -1
- package/dist/store/{jsonl-store.d.ts.map → jsonl_store.d.ts.map} +1 -1
- package/dist/store/{jsonl-store.js → jsonl_store.js} +1 -1
- package/dist/store/{jsonl-store.js.map → jsonl_store.js.map} +1 -1
- package/dist/store/kuzu_store.d.ts +66 -0
- package/dist/store/kuzu_store.d.ts.map +1 -0
- package/dist/store/kuzu_store.js +156 -0
- package/dist/store/kuzu_store.js.map +1 -0
- package/dist/verify/project_verifier.d.ts +85 -0
- package/dist/verify/project_verifier.d.ts.map +1 -0
- package/dist/verify/project_verifier.js +138 -0
- package/dist/verify/project_verifier.js.map +1 -0
- package/dotclaude_folder/skills/code-graph-query/SKILL.md +91 -0
- package/package.json +88 -5
- package/.env-sample +0 -34
- package/dist/agent/agent-tools.d.ts +0 -13
- package/dist/agent/agent-tools.d.ts.map +0 -1
- package/dist/agent/agent-tools.js +0 -153
- package/dist/agent/agent-tools.js.map +0 -1
- package/dist/agent/code-editor.d.ts +0 -18
- package/dist/agent/code-editor.d.ts.map +0 -1
- package/dist/agent/code-editor.js +0 -43
- package/dist/agent/code-editor.js.map +0 -1
- package/dist/agent/optimizer-agent.d.ts +0 -30
- package/dist/agent/optimizer-agent.d.ts.map +0 -1
- package/dist/agent/optimizer-agent.js +0 -97
- package/dist/agent/optimizer-agent.js.map +0 -1
- package/dist/agent/verifier.d.ts +0 -9
- package/dist/agent/verifier.d.ts.map +0 -1
- package/dist/agent/verifier.js +0 -19
- package/dist/agent/verifier.js.map +0 -1
- package/dist/extract/graph-builder.d.ts.map +0 -1
- package/dist/extract/graph-builder.js +0 -39
- package/dist/extract/graph-builder.js.map +0 -1
- package/dist/extract/node-id.d.ts +0 -8
- package/dist/extract/node-id.d.ts.map +0 -1
- package/dist/extract/node-id.js +0 -22
- package/dist/extract/node-id.js.map +0 -1
- package/dist/extract/project-loader.d.ts.map +0 -1
- package/dist/extract/semantic-extractor.d.ts +0 -22
- package/dist/extract/semantic-extractor.d.ts.map +0 -1
- package/dist/extract/semantic-extractor.js.map +0 -1
- package/dist/extract/structural-extractor.js.map +0 -1
- package/dist/query/graph-query.d.ts +0 -28
- package/dist/query/graph-query.d.ts.map +0 -1
- package/dist/query/graph-query.js +0 -93
- package/dist/query/graph-query.js.map +0 -1
- package/dist/store/kuzu-store.d.ts +0 -14
- package/dist/store/kuzu-store.d.ts.map +0 -1
- package/dist/store/kuzu-store.js +0 -52
- package/dist/store/kuzu-store.js.map +0 -1
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "Bundler",
|
|
6
|
+
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
|
7
|
+
"strict": true,
|
|
8
|
+
"allowJs": true,
|
|
9
|
+
"checkJs": true,
|
|
10
|
+
"noEmit": true,
|
|
11
|
+
"noUnusedLocals": true,
|
|
12
|
+
"noUnusedParameters": true,
|
|
13
|
+
"forceConsistentCasingInFileNames": true,
|
|
14
|
+
"skipLibCheck": true,
|
|
15
|
+
"types": []
|
|
16
|
+
},
|
|
17
|
+
"include": ["js/app.js", "types/**/*.d.ts"]
|
|
18
|
+
}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ambient declarations for the static web viewer (`web/js/app.js`).
|
|
3
|
+
*
|
|
4
|
+
* The page ships as plain, build-free JavaScript and is type-checked in place
|
|
5
|
+
* with `// @ts-check`. This file gives that JavaScript precise types for the
|
|
6
|
+
* data it loads (the knowledge-graph JSONL records, mirrored from
|
|
7
|
+
* `src/schema`), and a deliberately loose surface for the Cytoscape.js library,
|
|
8
|
+
* which is loaded as a global from a CDN and has no bundled types here.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/* ---------- knowledge-graph data (mirrors src/schema) ---------- */
|
|
12
|
+
|
|
13
|
+
/** Source range of a symbol; mirrors `RangeSchema` in `src/schema/node.ts`. */
|
|
14
|
+
interface RawRange {
|
|
15
|
+
startLine: number;
|
|
16
|
+
startColumn: number;
|
|
17
|
+
endLine: number;
|
|
18
|
+
endColumn: number;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** Per-node runtime metrics attached by `enrich`, read from `metadata.runtime`. */
|
|
22
|
+
interface NodeRuntime {
|
|
23
|
+
selfMs?: number;
|
|
24
|
+
samples?: number;
|
|
25
|
+
source?: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** A graph node as serialised in `nodes.jsonl`; mirrors `GraphNodeSchema`. */
|
|
29
|
+
interface RawNode {
|
|
30
|
+
id: string;
|
|
31
|
+
kind: string;
|
|
32
|
+
name: string;
|
|
33
|
+
filePath: string;
|
|
34
|
+
range?: RawRange;
|
|
35
|
+
exported?: boolean;
|
|
36
|
+
metadata?: { runtime?: NodeRuntime | null; [key: string]: unknown } | null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** A graph edge as serialised in `edges.jsonl`; mirrors `GraphEdgeSchema`. */
|
|
40
|
+
interface RawEdge {
|
|
41
|
+
id: string;
|
|
42
|
+
kind: string;
|
|
43
|
+
from: string;
|
|
44
|
+
to: string;
|
|
45
|
+
metadata?: { count?: number; [key: string]: unknown } | null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** The embedded `window.GRAPH_DATA` payload written by `scripts/build-data.ts`. */
|
|
49
|
+
interface GraphData {
|
|
50
|
+
nodes: RawNode[];
|
|
51
|
+
edges: RawEdge[];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** The `window.KIND_DESCRIPTIONS` payload: one-line help per node/edge kind. */
|
|
55
|
+
interface KindDescriptions {
|
|
56
|
+
nodes: Record<string, string>;
|
|
57
|
+
edges: Record<string, string>;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** GitHub permalink descriptor; mirrors `GitHubSource` in `src/commands/web_command.ts`. */
|
|
61
|
+
interface GitHubSource {
|
|
62
|
+
baseUrl: string;
|
|
63
|
+
commit: string;
|
|
64
|
+
prefix: string;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** The `window.GRAPH_SOURCE` payload injected by the `web` command. */
|
|
68
|
+
interface GraphSource {
|
|
69
|
+
github?: GitHubSource;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** The single mutable view-model the viewer threads through its render functions. */
|
|
73
|
+
interface AppState {
|
|
74
|
+
nodes: RawNode[];
|
|
75
|
+
edges: RawEdge[];
|
|
76
|
+
cy: CyCore | undefined;
|
|
77
|
+
hiddenNodeKinds: Set<string>;
|
|
78
|
+
hiddenEdgeKinds: Set<string>;
|
|
79
|
+
hideIsolated: boolean;
|
|
80
|
+
onlyMeasured: boolean;
|
|
81
|
+
droppedFiles: { nodes: RawNode[] | undefined; edges: RawEdge[] | undefined };
|
|
82
|
+
encoding: 'structural' | 'runtime';
|
|
83
|
+
runtime: { maxSelfMs: number; measuredCount: number; totalSelfMs: number };
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/* ---------- Cytoscape.js (loaded as a CDN global, untyped) ---------- */
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* A Cytoscape collection or single element. Only the surface the viewer uses is
|
|
90
|
+
* declared. `data()` is intentionally `any`: it is Cytoscape's dynamic per-element
|
|
91
|
+
* bag and the one place this otherwise-strict file crosses an untyped boundary.
|
|
92
|
+
*/
|
|
93
|
+
interface CyCollection {
|
|
94
|
+
length: number;
|
|
95
|
+
data(name?: string): any;
|
|
96
|
+
id(): string;
|
|
97
|
+
degree(): number;
|
|
98
|
+
closedNeighborhood(): CyCollection;
|
|
99
|
+
connectedEdges(): CyCollection;
|
|
100
|
+
source(): CyCollection;
|
|
101
|
+
target(): CyCollection;
|
|
102
|
+
addClass(names: string): CyCollection;
|
|
103
|
+
removeClass(names: string): CyCollection;
|
|
104
|
+
toggleClass(names: string, flag?: boolean): CyCollection;
|
|
105
|
+
hasClass(name: string): boolean;
|
|
106
|
+
not(selector: string): CyCollection;
|
|
107
|
+
forEach(each: (element: CyCollection, index: number) => void): void;
|
|
108
|
+
some(test: (element: CyCollection, index: number) => boolean): boolean;
|
|
109
|
+
layout(options: unknown): { run(): CyCollection };
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/** A Cytoscape event; `target` is the element or core the event fired on. */
|
|
113
|
+
interface CyEvent {
|
|
114
|
+
target: any;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** The Cytoscape core instance returned by the `cytoscape(...)` factory. */
|
|
118
|
+
interface CyCore {
|
|
119
|
+
destroy(): void;
|
|
120
|
+
on(events: string, handler: (event: CyEvent) => void): CyCore;
|
|
121
|
+
on(events: string, selector: string, handler: (event: CyEvent) => void): CyCore;
|
|
122
|
+
style(style?: unknown): CyCore;
|
|
123
|
+
elements(selector?: string): CyCollection;
|
|
124
|
+
nodes(selector?: string): CyCollection;
|
|
125
|
+
edges(selector?: string): CyCollection;
|
|
126
|
+
batch(callback: () => void): void;
|
|
127
|
+
getElementById(id: string): CyCollection;
|
|
128
|
+
animate(options: unknown, params?: unknown): CyCore;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
interface CytoscapeOptions {
|
|
132
|
+
container?: HTMLElement | null;
|
|
133
|
+
elements?: unknown;
|
|
134
|
+
style?: unknown;
|
|
135
|
+
layout?: unknown;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
declare function cytoscape(options?: CytoscapeOptions): CyCore;
|
|
139
|
+
|
|
140
|
+
/* ---------- globals injected into the page ---------- */
|
|
141
|
+
|
|
142
|
+
interface Window {
|
|
143
|
+
GRAPH_DATA?: GraphData;
|
|
144
|
+
KIND_DESCRIPTIONS?: KindDescriptions;
|
|
145
|
+
GRAPH_SOURCE?: GraphSource | null;
|
|
146
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure statistics for the benchmark gate. Runtime measurement is noisy, so a
|
|
3
|
+
* single number lies: every benchmark reports the **median** of N runs plus the
|
|
4
|
+
* **spread**, and a baseline→after comparison is an advisory delta, never a
|
|
5
|
+
* deterministic guarantee. These helpers are deliberately side-effect-free so
|
|
6
|
+
* the variance handling is unit-tested directly, apart from any profiling.
|
|
7
|
+
*/
|
|
8
|
+
/** A node's metric distribution over N runs: the headline median plus the spread that qualifies it. */
|
|
9
|
+
export type BenchmarkStatsSummary = {
|
|
10
|
+
runs: number;
|
|
11
|
+
median: number;
|
|
12
|
+
min: number;
|
|
13
|
+
max: number;
|
|
14
|
+
mean: number;
|
|
15
|
+
/** `max - min`: the observed spread, a coarse read on how noisy the measurement was. */
|
|
16
|
+
spread: number;
|
|
17
|
+
/** The raw per-run samples, in run order. */
|
|
18
|
+
values: number[];
|
|
19
|
+
};
|
|
20
|
+
/** A baseline→after comparison of two medians. Negative `absolute`/`percent` means the metric went down (faster / fewer). */
|
|
21
|
+
export type BenchmarkDelta = {
|
|
22
|
+
baselineMedian: number;
|
|
23
|
+
currentMedian: number;
|
|
24
|
+
/** `currentMedian - baselineMedian`. */
|
|
25
|
+
absolute: number;
|
|
26
|
+
/** `absolute / baselineMedian`, e.g. `-0.38` for a 38% reduction. `NaN` when the baseline median is 0. */
|
|
27
|
+
percent: number;
|
|
28
|
+
};
|
|
29
|
+
export declare class BenchmarkStats {
|
|
30
|
+
/** The median of a non-empty sample set (mean of the two middle values when the count is even). */
|
|
31
|
+
static median(values: number[]): number;
|
|
32
|
+
/** Reduce per-run samples to the median + spread summary. Throws on an empty set. */
|
|
33
|
+
static summarize(values: number[]): BenchmarkStatsSummary;
|
|
34
|
+
/** Compare a prior baseline median against the current median. `percent` is `NaN` when the baseline is 0. */
|
|
35
|
+
static delta(baselineMedian: number, currentMedian: number): BenchmarkDelta;
|
|
36
|
+
/** Format a fractional change as a signed percentage, e.g. `-0.38` → `"-38.0%"`; `NaN` → `"n/a"`. */
|
|
37
|
+
static formatPercent(percent: number): string;
|
|
38
|
+
/** A one-word read on a delta for a lower-is-better metric (time / samples), within a noise tolerance. */
|
|
39
|
+
static direction(delta: BenchmarkDelta, spread: number): 'improved' | 'regressed' | 'unchanged';
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=benchmark_stats.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"benchmark_stats.d.ts","sourceRoot":"","sources":["../../src/benchmark/benchmark_stats.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,uGAAuG;AACvG,MAAM,MAAM,qBAAqB,GAAG;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,wFAAwF;IACxF,MAAM,EAAE,MAAM,CAAC;IACf,6CAA6C;IAC7C,MAAM,EAAE,MAAM,EAAE,CAAC;CACjB,CAAC;AAEF,6HAA6H;AAC7H,MAAM,MAAM,cAAc,GAAG;IAC5B,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,wCAAwC;IACxC,QAAQ,EAAE,MAAM,CAAC;IACjB,0GAA0G;IAC1G,OAAO,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,qBAAa,cAAc;IAC1B,mGAAmG;IACnG,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM;IAYvC,qFAAqF;IACrF,MAAM,CAAC,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,qBAAqB;IAkBzD,6GAA6G;IAC7G,MAAM,CAAC,KAAK,CAAC,cAAc,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,cAAc;IAM3E,qGAAqG;IACrG,MAAM,CAAC,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM;IAQ7C,0GAA0G;IAC1G,MAAM,CAAC,SAAS,CAAC,KAAK,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,GAAG,UAAU,GAAG,WAAW,GAAG,WAAW;CAM/F"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure statistics for the benchmark gate. Runtime measurement is noisy, so a
|
|
3
|
+
* single number lies: every benchmark reports the **median** of N runs plus the
|
|
4
|
+
* **spread**, and a baseline→after comparison is an advisory delta, never a
|
|
5
|
+
* deterministic guarantee. These helpers are deliberately side-effect-free so
|
|
6
|
+
* the variance handling is unit-tested directly, apart from any profiling.
|
|
7
|
+
*/
|
|
8
|
+
export class BenchmarkStats {
|
|
9
|
+
/** The median of a non-empty sample set (mean of the two middle values when the count is even). */
|
|
10
|
+
static median(values) {
|
|
11
|
+
if (values.length === 0) {
|
|
12
|
+
throw new Error('median of an empty sample set');
|
|
13
|
+
}
|
|
14
|
+
const sorted = [...values].sort((a, b) => a - b);
|
|
15
|
+
const mid = Math.floor(sorted.length / 2);
|
|
16
|
+
if (sorted.length % 2 === 1) {
|
|
17
|
+
return sorted[mid];
|
|
18
|
+
}
|
|
19
|
+
return (sorted[mid - 1] + sorted[mid]) / 2;
|
|
20
|
+
}
|
|
21
|
+
/** Reduce per-run samples to the median + spread summary. Throws on an empty set. */
|
|
22
|
+
static summarize(values) {
|
|
23
|
+
if (values.length === 0) {
|
|
24
|
+
throw new Error('cannot summarize a benchmark with no runs');
|
|
25
|
+
}
|
|
26
|
+
const min = Math.min(...values);
|
|
27
|
+
const max = Math.max(...values);
|
|
28
|
+
const mean = values.reduce((sum, value) => sum + value, 0) / values.length;
|
|
29
|
+
return {
|
|
30
|
+
runs: values.length,
|
|
31
|
+
median: BenchmarkStats.median(values),
|
|
32
|
+
min,
|
|
33
|
+
max,
|
|
34
|
+
mean,
|
|
35
|
+
spread: max - min,
|
|
36
|
+
values: [...values],
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
/** Compare a prior baseline median against the current median. `percent` is `NaN` when the baseline is 0. */
|
|
40
|
+
static delta(baselineMedian, currentMedian) {
|
|
41
|
+
const absolute = currentMedian - baselineMedian;
|
|
42
|
+
const percent = baselineMedian === 0 ? Number.NaN : absolute / baselineMedian;
|
|
43
|
+
return { baselineMedian, currentMedian, absolute, percent };
|
|
44
|
+
}
|
|
45
|
+
/** Format a fractional change as a signed percentage, e.g. `-0.38` → `"-38.0%"`; `NaN` → `"n/a"`. */
|
|
46
|
+
static formatPercent(percent) {
|
|
47
|
+
if (Number.isNaN(percent) === true) {
|
|
48
|
+
return 'n/a';
|
|
49
|
+
}
|
|
50
|
+
const sign = percent > 0 ? '+' : '';
|
|
51
|
+
return `${sign}${(percent * 100).toFixed(1)}%`;
|
|
52
|
+
}
|
|
53
|
+
/** A one-word read on a delta for a lower-is-better metric (time / samples), within a noise tolerance. */
|
|
54
|
+
static direction(delta, spread) {
|
|
55
|
+
if (Math.abs(delta.absolute) <= spread) {
|
|
56
|
+
return 'unchanged';
|
|
57
|
+
}
|
|
58
|
+
return delta.absolute < 0 ? 'improved' : 'regressed';
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=benchmark_stats.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"benchmark_stats.js","sourceRoot":"","sources":["../../src/benchmark/benchmark_stats.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { GraphQuery } from '../query/graph_query.js';
|
|
2
|
+
import { KuzuStore } from '../store/kuzu_store.js';
|
|
3
|
+
import { BenchmarkDelta, BenchmarkStatsSummary } from './benchmark_stats.js';
|
|
4
|
+
/**
|
|
5
|
+
* The benchmark gate: measure one target node's runtime metric over a repeatable
|
|
6
|
+
* workload, **before and after** an edit, and report the delta. It reuses the
|
|
7
|
+
* existing machinery end to end — a V8 CPU profile feeds {@link RuntimeEnricher}
|
|
8
|
+
* (the same `enrich` path) and the metric is read back through
|
|
9
|
+
* {@link GraphQuery.costAttribution} (the `cost` query).
|
|
10
|
+
*
|
|
11
|
+
* Honesty about noise is baked in: a benchmark runs the workload N times and
|
|
12
|
+
* reports the **median + spread**, and a baseline comparison is labelled
|
|
13
|
+
* **advisory** — distinct from the hard pass/fail of the `verify` gate. The
|
|
14
|
+
* profiling step is injected ({@link ProfileRun}) so the read + statistics logic
|
|
15
|
+
* is unit-tested without actually profiling.
|
|
16
|
+
*/
|
|
17
|
+
/** Which runtime metric to track. `self-time` is the node's own exclusive time; `inclusive-time` adds its callees. */
|
|
18
|
+
export type BenchmarkMetric = 'self-time' | 'inclusive-time' | 'samples';
|
|
19
|
+
export type BenchmarkTarget = {
|
|
20
|
+
id: string;
|
|
21
|
+
name: string;
|
|
22
|
+
kind: string;
|
|
23
|
+
filePath: string;
|
|
24
|
+
startLine: number;
|
|
25
|
+
};
|
|
26
|
+
export type BenchmarkReport = {
|
|
27
|
+
target: BenchmarkTarget;
|
|
28
|
+
metric: BenchmarkMetric;
|
|
29
|
+
unit: 'ms' | 'samples';
|
|
30
|
+
stats: BenchmarkStatsSummary;
|
|
31
|
+
/** Present only when a prior baseline median was supplied; always advisory. */
|
|
32
|
+
delta: BenchmarkDelta | null;
|
|
33
|
+
/** A human note stating the result is advisory and noisy, not a guarantee. */
|
|
34
|
+
advisory: string;
|
|
35
|
+
};
|
|
36
|
+
export type NodeBenchmarkOptions = {
|
|
37
|
+
/** Symbol name (or substring / kind) resolved against the current graph to pick the target node. */
|
|
38
|
+
target: string;
|
|
39
|
+
/** Default `self-time`. */
|
|
40
|
+
metric?: BenchmarkMetric;
|
|
41
|
+
/** Number of profiling runs. Default 5, clamped to [1, 50]. */
|
|
42
|
+
runs?: number;
|
|
43
|
+
/** A prior baseline median to compare the current median against (advisory delta). */
|
|
44
|
+
baselineMedian?: number;
|
|
45
|
+
};
|
|
46
|
+
/** Everything one real profiling run needs: the workload entry, the project root, and a scratch dir for `.cpuprofile`s. */
|
|
47
|
+
export type ProfileConfig = {
|
|
48
|
+
/** Path to a repeatable workload entry (a `.ts`/`.js` file) that exercises the target under load. */
|
|
49
|
+
workload: string;
|
|
50
|
+
/** Project root the profile's absolute frame paths resolve against (passed to `enrich`). */
|
|
51
|
+
root: string;
|
|
52
|
+
/** Scratch directory for the per-run `.cpuprofile`. */
|
|
53
|
+
profileDir: string;
|
|
54
|
+
};
|
|
55
|
+
/** One profiling run: leave a fresh `metadata.runtime` on the graph in the store. Injectable so tests skip profiling. */
|
|
56
|
+
export type ProfileRun = (run: number) => Promise<void>;
|
|
57
|
+
export declare class NodeBenchmark {
|
|
58
|
+
/**
|
|
59
|
+
* Profile the workload `runs` times, reading the target's metric after each,
|
|
60
|
+
* and return the median + spread (plus an advisory delta when a baseline is
|
|
61
|
+
* given). `profileRun` defaults to a real V8 profile + enrich; tests inject a
|
|
62
|
+
* fake that writes a known metric so this method's read/stats logic is covered
|
|
63
|
+
* without profiling.
|
|
64
|
+
*/
|
|
65
|
+
static measure(store: KuzuStore, options: NodeBenchmarkOptions, profileRun: ProfileRun): Promise<BenchmarkReport>;
|
|
66
|
+
/** Resolve a target name to exactly one node in the current graph, or throw with guidance. */
|
|
67
|
+
static resolveTarget(query: GraphQuery, target: string): Promise<BenchmarkTarget>;
|
|
68
|
+
/** Read the target's current metric via the cost model (the `cost` query). */
|
|
69
|
+
static readMetric(query: GraphQuery, id: string, metric: BenchmarkMetric): Promise<number>;
|
|
70
|
+
/** A real profiling run: V8-profile the workload, then enrich the graph with the measured self time. */
|
|
71
|
+
static profileAndEnrich(store: KuzuStore, config: ProfileConfig): Promise<void>;
|
|
72
|
+
private static clampRuns;
|
|
73
|
+
private static buildAdvisory;
|
|
74
|
+
private static round;
|
|
75
|
+
private static runWorkload;
|
|
76
|
+
private static newestProfile;
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=node_benchmark.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node_benchmark.d.ts","sourceRoot":"","sources":["../../src/benchmark/node_benchmark.ts"],"names":[],"mappings":"AAIA,OAAO,EAAc,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACjE,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAE,cAAc,EAAkB,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAE7F;;;;;;;;;;;;GAYG;AAEH,sHAAsH;AACtH,MAAM,MAAM,eAAe,GAAG,WAAW,GAAG,gBAAgB,GAAG,SAAS,CAAC;AAEzE,MAAM,MAAM,eAAe,GAAG;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC7B,MAAM,EAAE,eAAe,CAAC;IACxB,MAAM,EAAE,eAAe,CAAC;IACxB,IAAI,EAAE,IAAI,GAAG,SAAS,CAAC;IACvB,KAAK,EAAE,qBAAqB,CAAC;IAC7B,+EAA+E;IAC/E,KAAK,EAAE,cAAc,GAAG,IAAI,CAAC;IAC7B,8EAA8E;IAC9E,QAAQ,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IAClC,oGAAoG;IACpG,MAAM,EAAE,MAAM,CAAC;IACf,2BAA2B;IAC3B,MAAM,CAAC,EAAE,eAAe,CAAC;IACzB,+DAA+D;IAC/D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,sFAAsF;IACtF,cAAc,CAAC,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF,2HAA2H;AAC3H,MAAM,MAAM,aAAa,GAAG;IAC3B,qGAAqG;IACrG,QAAQ,EAAE,MAAM,CAAC;IACjB,4FAA4F;IAC5F,IAAI,EAAE,MAAM,CAAC;IACb,uDAAuD;IACvD,UAAU,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,yHAAyH;AACzH,MAAM,MAAM,UAAU,GAAG,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;AAMxD,qBAAa,aAAa;IACzB;;;;;;OAMG;WACU,OAAO,CAAC,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,oBAAoB,EAAE,UAAU,EAAE,UAAU,GAAG,OAAO,CAAC,eAAe,CAAC;IAoBvH,8FAA8F;WACjF,aAAa,CAAC,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;IAcvF,8EAA8E;WACjE,UAAU,CAAC,KAAK,EAAE,UAAU,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,MAAM,CAAC;IAShG,wGAAwG;WAC3F,gBAAgB,CAAC,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IAQrF,OAAO,CAAC,MAAM,CAAC,SAAS;IAOxB,OAAO,CAAC,MAAM,CAAC,aAAa;IAS5B,OAAO,CAAC,MAAM,CAAC,KAAK;IAIpB,OAAO,CAAC,MAAM,CAAC,WAAW;mBAmBL,aAAa;CAclC"}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { mkdir, readdir, readFile, rm, stat } from 'node:fs/promises';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { RuntimeEnricher } from '../enrich/runtime_enricher.js';
|
|
5
|
+
import { GraphQuery } from '../query/graph_query.js';
|
|
6
|
+
import { BenchmarkStats } from './benchmark_stats.js';
|
|
7
|
+
const DEFAULT_RUNS = 5;
|
|
8
|
+
const MIN_RUNS = 1;
|
|
9
|
+
const MAX_RUNS = 50;
|
|
10
|
+
export class NodeBenchmark {
|
|
11
|
+
/**
|
|
12
|
+
* Profile the workload `runs` times, reading the target's metric after each,
|
|
13
|
+
* and return the median + spread (plus an advisory delta when a baseline is
|
|
14
|
+
* given). `profileRun` defaults to a real V8 profile + enrich; tests inject a
|
|
15
|
+
* fake that writes a known metric so this method's read/stats logic is covered
|
|
16
|
+
* without profiling.
|
|
17
|
+
*/
|
|
18
|
+
static async measure(store, options, profileRun) {
|
|
19
|
+
const query = new GraphQuery(store);
|
|
20
|
+
const target = await NodeBenchmark.resolveTarget(query, options.target);
|
|
21
|
+
const metric = options.metric ?? 'self-time';
|
|
22
|
+
const runs = NodeBenchmark.clampRuns(options.runs ?? DEFAULT_RUNS);
|
|
23
|
+
const values = [];
|
|
24
|
+
for (let run = 0; run < runs; run += 1) {
|
|
25
|
+
await profileRun(run);
|
|
26
|
+
values.push(await NodeBenchmark.readMetric(query, target.id, metric));
|
|
27
|
+
}
|
|
28
|
+
const stats = BenchmarkStats.summarize(values);
|
|
29
|
+
const delta = options.baselineMedian === undefined
|
|
30
|
+
? null
|
|
31
|
+
: BenchmarkStats.delta(options.baselineMedian, stats.median);
|
|
32
|
+
const unit = metric === 'samples' ? 'samples' : 'ms';
|
|
33
|
+
return { target, metric, unit, stats, delta, advisory: NodeBenchmark.buildAdvisory(stats, unit) };
|
|
34
|
+
}
|
|
35
|
+
/** Resolve a target name to exactly one node in the current graph, or throw with guidance. */
|
|
36
|
+
static async resolveTarget(query, target) {
|
|
37
|
+
const matches = await query.find(target);
|
|
38
|
+
if (matches.length === 0) {
|
|
39
|
+
throw new Error(`no node matches "${target}" — resolve a symbol name with \`find\` first`);
|
|
40
|
+
}
|
|
41
|
+
const exact = matches.filter((match) => match.name === target);
|
|
42
|
+
const chosen = exact.length === 1 ? exact[0] : matches.length === 1 ? matches[0] : null;
|
|
43
|
+
if (chosen === null) {
|
|
44
|
+
const candidates = (exact.length > 0 ? exact : matches).slice(0, 8).map((match) => match.id).join(', ');
|
|
45
|
+
throw new Error(`"${target}" is ambiguous — narrow it to one of: ${candidates}`);
|
|
46
|
+
}
|
|
47
|
+
return { id: chosen.id, name: chosen.name, kind: chosen.kind, filePath: chosen.filePath, startLine: chosen.startLine };
|
|
48
|
+
}
|
|
49
|
+
/** Read the target's current metric via the cost model (the `cost` query). */
|
|
50
|
+
static async readMetric(query, id, metric) {
|
|
51
|
+
const by = metric === 'samples' ? 'samples' : 'self-time';
|
|
52
|
+
const attribution = await query.costAttribution(id, { by });
|
|
53
|
+
if (attribution.node === null) {
|
|
54
|
+
throw new Error(`target node ${id} is no longer in the graph — rebuild after the edit`);
|
|
55
|
+
}
|
|
56
|
+
return metric === 'inclusive-time' ? attribution.node.inclusiveCost : attribution.node.selfCost;
|
|
57
|
+
}
|
|
58
|
+
/** A real profiling run: V8-profile the workload, then enrich the graph with the measured self time. */
|
|
59
|
+
static async profileAndEnrich(store, config) {
|
|
60
|
+
await rm(config.profileDir, { recursive: true, force: true });
|
|
61
|
+
await mkdir(config.profileDir, { recursive: true });
|
|
62
|
+
const profilePath = await NodeBenchmark.runWorkload(config);
|
|
63
|
+
const profileText = await readFile(profilePath, 'utf8');
|
|
64
|
+
await RuntimeEnricher.enrich(store, profileText, { root: config.root });
|
|
65
|
+
}
|
|
66
|
+
static clampRuns(runs) {
|
|
67
|
+
if (Number.isFinite(runs) === false) {
|
|
68
|
+
return DEFAULT_RUNS;
|
|
69
|
+
}
|
|
70
|
+
return Math.min(MAX_RUNS, Math.max(MIN_RUNS, Math.floor(runs)));
|
|
71
|
+
}
|
|
72
|
+
static buildAdvisory(stats, unit) {
|
|
73
|
+
const spread = `${NodeBenchmark.round(stats.spread)} ${unit}`;
|
|
74
|
+
return (`Advisory — runtime measurement is noisy. This is the median of ${stats.runs} run(s) ` +
|
|
75
|
+
`(spread ${spread}), not a deterministic guarantee. Unlike the verify gate (hard pass/fail), ` +
|
|
76
|
+
`a benchmark delta is indicative, not proof.`);
|
|
77
|
+
}
|
|
78
|
+
static round(value) {
|
|
79
|
+
return Math.round(value * 1000) / 1000;
|
|
80
|
+
}
|
|
81
|
+
static runWorkload(config) {
|
|
82
|
+
return new Promise((resolvePromise, reject) => {
|
|
83
|
+
const args = ['--cpu-prof', '--cpu-prof-dir', config.profileDir, '--import', 'tsx', config.workload];
|
|
84
|
+
const child = spawn('node', args, { cwd: config.root, stdio: ['ignore', 'ignore', 'pipe'] });
|
|
85
|
+
let stderr = '';
|
|
86
|
+
child.stderr.on('data', (chunk) => {
|
|
87
|
+
stderr += chunk.toString();
|
|
88
|
+
});
|
|
89
|
+
child.on('error', reject);
|
|
90
|
+
child.on('close', (code) => {
|
|
91
|
+
if (code !== 0) {
|
|
92
|
+
reject(new Error(`workload "${config.workload}" exited with code ${code}\n${stderr.trim()}`));
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
NodeBenchmark.newestProfile(config.profileDir).then(resolvePromise, reject);
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
static async newestProfile(profileDir) {
|
|
100
|
+
const entries = (await readdir(profileDir)).filter((name) => name.endsWith('.cpuprofile'));
|
|
101
|
+
if (entries.length === 0) {
|
|
102
|
+
throw new Error(`no .cpuprofile written to ${profileDir} — did the workload run under --cpu-prof?`);
|
|
103
|
+
}
|
|
104
|
+
const withMtime = await Promise.all(entries.map(async (name) => {
|
|
105
|
+
const path = join(profileDir, name);
|
|
106
|
+
return { path, mtimeMs: (await stat(path)).mtimeMs };
|
|
107
|
+
}));
|
|
108
|
+
withMtime.sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
109
|
+
return withMtime[0].path;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
//# sourceMappingURL=node_benchmark.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node_benchmark.js","sourceRoot":"","sources":["../../src/benchmark/node_benchmark.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.d.ts
CHANGED
|
@@ -1,14 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
export declare class Cli {
|
|
3
3
|
static run(argv: string[]): void;
|
|
4
|
-
private static registerQuery;
|
|
5
|
-
private static extract;
|
|
6
|
-
private static load;
|
|
7
|
-
private static optimize;
|
|
8
|
-
private static withQuery;
|
|
9
|
-
private static printRefs;
|
|
10
|
-
private static printNeighbors;
|
|
11
|
-
private static printBreakdown;
|
|
12
|
-
private static countBy;
|
|
13
4
|
}
|
|
14
5
|
//# sourceMappingURL=cli.d.ts.map
|
package/dist/cli.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAmBA,qBAAa,GAAG;IACf,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI;CAyBhC"}
|