ts-knowledge-graph 0.1.2 → 0.1.6
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 +99 -41
- package/contribs/webview/README.md +83 -0
- package/contribs/webview/web/css/style.css +310 -0
- package/contribs/webview/web/index.html +109 -0
- package/contribs/webview/web/js/app.js +1249 -0
- package/contribs/webview/web/js_autogenerated/.gitignore +3 -0
- package/contribs/webview/web/js_autogenerated/kind_descriptions.js +39 -0
- package/contribs/webview/web/types/app_globals.d.ts +154 -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.map +1 -1
- package/dist/cli.js +16 -4
- package/dist/cli.js.map +1 -1
- package/dist/cluster/cluster_weights.d.ts +20 -0
- package/dist/cluster/cluster_weights.d.ts.map +1 -0
- package/dist/cluster/cluster_weights.js +32 -0
- package/dist/cluster/cluster_weights.js.map +1 -0
- package/dist/cluster/community_detector.d.ts +61 -0
- package/dist/cluster/community_detector.d.ts.map +1 -0
- package/dist/cluster/community_detector.js +120 -0
- package/dist/cluster/community_detector.js.map +1 -0
- package/dist/cluster/community_labeler.d.ts +84 -0
- package/dist/cluster/community_labeler.d.ts.map +1 -0
- package/dist/cluster/community_labeler.js +194 -0
- package/dist/cluster/community_labeler.js.map +1 -0
- package/dist/cluster/graph_clusterer.d.ts +47 -0
- package/dist/cluster/graph_clusterer.d.ts.map +1 -0
- package/dist/cluster/graph_clusterer.js +126 -0
- package/dist/cluster/graph_clusterer.js.map +1 -0
- 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 +94 -0
- package/dist/commands/benchmark_command.js.map +1 -0
- package/dist/commands/blast_radius_command.d.ts.map +1 -1
- package/dist/commands/blast_radius_command.js +7 -6
- package/dist/commands/blast_radius_command.js.map +1 -1
- package/dist/commands/cluster_command.d.ts +7 -0
- package/dist/commands/cluster_command.d.ts.map +1 -0
- package/dist/commands/cluster_command.js +55 -0
- package/dist/commands/cluster_command.js.map +1 -0
- package/dist/commands/command_helpers.d.ts +9 -4
- package/dist/commands/command_helpers.d.ts.map +1 -1
- package/dist/commands/command_helpers.js +13 -8
- package/dist/commands/command_helpers.js.map +1 -1
- 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 +139 -0
- package/dist/commands/cost_command.js.map +1 -0
- package/dist/commands/{load.d.ts → enrich_command.d.ts} +3 -2
- package/dist/commands/enrich_command.d.ts.map +1 -0
- package/dist/commands/enrich_command.js +64 -0
- package/dist/commands/enrich_command.js.map +1 -0
- package/dist/commands/extract_command.d.ts.map +1 -1
- package/dist/commands/extract_command.js +12 -6
- package/dist/commands/extract_command.js.map +1 -1
- 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 +68 -0
- package/dist/commands/hotspots_command.js.map +1 -0
- package/dist/commands/install_command.d.ts +15 -6
- package/dist/commands/install_command.d.ts.map +1 -1
- package/dist/commands/install_command.js +62 -25
- package/dist/commands/install_command.js.map +1 -1
- package/dist/commands/load_command.d.ts.map +1 -1
- package/dist/commands/load_command.js +20 -13
- package/dist/commands/load_command.js.map +1 -1
- package/dist/commands/neighbors_command.d.ts.map +1 -1
- package/dist/commands/neighbors_command.js +6 -5
- package/dist/commands/neighbors_command.js.map +1 -1
- package/dist/commands/references_command.d.ts.map +1 -1
- package/dist/commands/references_command.js +6 -5
- package/dist/commands/references_command.js.map +1 -1
- package/dist/commands/report_command.d.ts +16 -0
- package/dist/commands/report_command.d.ts.map +1 -0
- package/dist/commands/report_command.js +115 -0
- package/dist/commands/report_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 +27 -0
- package/dist/commands/web_command.d.ts.map +1 -1
- package/dist/commands/web_command.js +109 -3
- package/dist/commands/web_command.js.map +1 -1
- package/dist/commands/webview_command.d.ts +36 -0
- package/dist/commands/webview_command.d.ts.map +1 -0
- package/dist/commands/webview_command.js +186 -0
- package/dist/commands/webview_command.js.map +1 -0
- package/dist/enrich/cpu_profile.d.ts +160 -0
- package/dist/enrich/cpu_profile.d.ts.map +1 -0
- package/dist/enrich/cpu_profile.js +185 -0
- package/dist/enrich/cpu_profile.js.map +1 -0
- package/dist/enrich/runtime_enricher.d.ts +64 -0
- package/dist/enrich/runtime_enricher.d.ts.map +1 -0
- package/dist/enrich/runtime_enricher.js +98 -0
- package/dist/enrich/runtime_enricher.js.map +1 -0
- package/dist/enrich/runtime_join.d.ts +124 -0
- package/dist/enrich/runtime_join.d.ts.map +1 -0
- package/dist/enrich/runtime_join.js +270 -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/git_source.d.ts +23 -0
- package/dist/extract/git_source.d.ts.map +1 -0
- package/dist/extract/git_source.js +75 -0
- package/dist/extract/git_source.js.map +1 -0
- package/dist/extract/graph_builder.d.ts +8 -0
- package/dist/extract/graph_builder.d.ts.map +1 -1
- package/dist/extract/graph_builder.js +23 -1
- package/dist/extract/graph_builder.js.map +1 -1
- package/dist/extract/node_id.d.ts +16 -0
- package/dist/extract/node_id.d.ts.map +1 -1
- package/dist/extract/node_id.js +22 -0
- package/dist/extract/node_id.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 +25 -0
- package/dist/extract/semantic_extractor.d.ts.map +1 -1
- package/dist/extract/semantic_extractor.js +96 -2
- package/dist/extract/semantic_extractor.js.map +1 -1
- package/dist/extract/structural_extractor.d.ts +6 -0
- package/dist/extract/structural_extractor.d.ts.map +1 -1
- package/dist/extract/structural_extractor.js +22 -12
- package/dist/extract/structural_extractor.js.map +1 -1
- 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 +269 -0
- package/dist/query/graph_query.d.ts.map +1 -1
- package/dist/query/graph_query.js +585 -11
- package/dist/query/graph_query.js.map +1 -1
- package/dist/report/graph_report.d.ts +51 -0
- package/dist/report/graph_report.d.ts.map +1 -0
- package/dist/report/graph_report.js +312 -0
- package/dist/report/graph_report.js.map +1 -0
- package/dist/report/pdf_renderer.d.ts +22 -0
- package/dist/report/pdf_renderer.d.ts.map +1 -0
- package/dist/report/pdf_renderer.js +54 -0
- package/dist/report/pdf_renderer.js.map +1 -0
- package/dist/report/report_data.d.ts +128 -0
- package/dist/report/report_data.d.ts.map +1 -0
- package/dist/report/report_data.js +191 -0
- package/dist/report/report_data.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 +73 -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/schema/source_manifest.d.ts +30 -0
- package/dist/schema/source_manifest.d.ts.map +1 -0
- package/dist/schema/source_manifest.js +21 -0
- package/dist/schema/source_manifest.js.map +1 -0
- package/dist/store/jsonl_reader.d.ts +4 -0
- package/dist/store/jsonl_reader.d.ts.map +1 -1
- package/dist/store/jsonl_reader.js +13 -1
- package/dist/store/jsonl_reader.js.map +1 -1
- package/dist/store/jsonl_store.d.ts +2 -1
- package/dist/store/jsonl_store.d.ts.map +1 -1
- package/dist/store/jsonl_store.js +4 -1
- package/dist/store/jsonl_store.js.map +1 -1
- package/dist/store/kuzu_store.d.ts +59 -0
- package/dist/store/kuzu_store.d.ts.map +1 -1
- package/dist/store/kuzu_store.js +124 -5
- package/dist/store/kuzu_store.js.map +1 -1
- package/dist/store/output_folder.d.ts +43 -0
- package/dist/store/output_folder.d.ts.map +1 -0
- package/dist/store/output_folder.js +61 -0
- package/dist/store/output_folder.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/commands/code-graph-interview.md +123 -0
- package/dotclaude_folder/commands/code-graph-optimize.md +65 -0
- package/{skills/ts-knowledge-graph → dotclaude_folder/skills/code-graph-query}/SKILL.md +6 -6
- package/package.json +99 -10
- package/.env-sample +0 -34
- package/contribs/web_visualisation/README.md +0 -55
- package/contribs/web_visualisation/web/css/style.css +0 -115
- package/contribs/web_visualisation/web/data/.gitignore +0 -2
- package/contribs/web_visualisation/web/index.html +0 -58
- package/contribs/web_visualisation/web/js/app.js +0 -364
- 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/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/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/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/commands/blast-radius.d.ts +0 -5
- package/dist/commands/blast-radius.d.ts.map +0 -1
- package/dist/commands/blast-radius.js +0 -18
- package/dist/commands/blast-radius.js.map +0 -1
- package/dist/commands/blast_radius.d.ts +0 -5
- package/dist/commands/blast_radius.d.ts.map +0 -1
- package/dist/commands/blast_radius.js +0 -18
- package/dist/commands/blast_radius.js.map +0 -1
- package/dist/commands/calls.d.ts +0 -5
- package/dist/commands/calls.d.ts.map +0 -1
- package/dist/commands/calls.js +0 -7
- package/dist/commands/calls.js.map +0 -1
- package/dist/commands/command-helpers.d.ts +0 -15
- package/dist/commands/command-helpers.d.ts.map +0 -1
- package/dist/commands/command-helpers.js +0 -61
- package/dist/commands/command-helpers.js.map +0 -1
- package/dist/commands/dead-exports.d.ts +0 -5
- package/dist/commands/dead-exports.d.ts.map +0 -1
- package/dist/commands/dead-exports.js +0 -7
- package/dist/commands/dead-exports.js.map +0 -1
- package/dist/commands/dead_exports.d.ts +0 -5
- package/dist/commands/dead_exports.d.ts.map +0 -1
- package/dist/commands/dead_exports.js +0 -7
- package/dist/commands/dead_exports.js.map +0 -1
- package/dist/commands/extract.d.ts +0 -8
- package/dist/commands/extract.d.ts.map +0 -1
- package/dist/commands/extract.js +0 -49
- package/dist/commands/extract.js.map +0 -1
- package/dist/commands/find.d.ts +0 -5
- package/dist/commands/find.d.ts.map +0 -1
- package/dist/commands/find.js +0 -7
- package/dist/commands/find.js.map +0 -1
- package/dist/commands/load.d.ts.map +0 -1
- package/dist/commands/load.js +0 -28
- package/dist/commands/load.js.map +0 -1
- package/dist/commands/neighbors.d.ts +0 -5
- package/dist/commands/neighbors.d.ts.map +0 -1
- package/dist/commands/neighbors.js +0 -17
- package/dist/commands/neighbors.js.map +0 -1
- package/dist/commands/optimize.d.ts +0 -6
- package/dist/commands/optimize.d.ts.map +0 -1
- package/dist/commands/optimize.js +0 -59
- package/dist/commands/optimize.js.map +0 -1
- package/dist/commands/optimize_command.d.ts +0 -6
- package/dist/commands/optimize_command.d.ts.map +0 -1
- package/dist/commands/optimize_command.js +0 -59
- package/dist/commands/optimize_command.js.map +0 -1
- package/dist/commands/references.d.ts +0 -5
- package/dist/commands/references.d.ts.map +0 -1
- package/dist/commands/references.js +0 -17
- package/dist/commands/references.js.map +0 -1
- package/dist/commands/web.d.ts +0 -19
- package/dist/commands/web.d.ts.map +0 -1
- package/dist/commands/web.js +0 -120
- package/dist/commands/web.js.map +0 -1
- package/dist/commands/who-calls.d.ts +0 -5
- package/dist/commands/who-calls.d.ts.map +0 -1
- package/dist/commands/who-calls.js +0 -7
- package/dist/commands/who-calls.js.map +0 -1
- package/dist/commands/who_calls.d.ts +0 -5
- package/dist/commands/who_calls.d.ts.map +0 -1
- package/dist/commands/who_calls.js +0 -7
- package/dist/commands/who_calls.js.map +0 -1
- package/dist/extract/graph-builder.d.ts +0 -16
- 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 +0 -5
- package/dist/extract/project-loader.d.ts.map +0 -1
- package/dist/extract/project-loader.js +0 -19
- package/dist/extract/project-loader.js.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 +0 -254
- package/dist/extract/semantic-extractor.js.map +0 -1
- package/dist/extract/structural-extractor.d.ts +0 -18
- package/dist/extract/structural-extractor.d.ts.map +0 -1
- package/dist/extract/structural-extractor.js +0 -97
- 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/jsonl-reader.d.ts +0 -11
- package/dist/store/jsonl-reader.d.ts.map +0 -1
- package/dist/store/jsonl-reader.js +0 -19
- package/dist/store/jsonl-reader.js.map +0 -1
- package/dist/store/jsonl-store.d.ts +0 -7
- package/dist/store/jsonl-store.d.ts.map +0 -1
- package/dist/store/jsonl-store.js +0 -13
- package/dist/store/jsonl-store.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,98 @@
|
|
|
1
|
+
import { RUNTIME_MANIFEST_KEY } from '../schema/runtime_manifest.js';
|
|
2
|
+
import { CpuProfile } from './cpu_profile.js';
|
|
3
|
+
import { RuntimeJoin } from './runtime_join.js';
|
|
4
|
+
/** The telemetry source tag written into `metadata.runtime.source`. */
|
|
5
|
+
export const RUNTIME_SOURCE_CPU_PROFILE = 'v8-cpuprofile';
|
|
6
|
+
/** Namespaced key under which runtime metrics are stored on a node's metadata. */
|
|
7
|
+
export const RUNTIME_METADATA_KEY = 'runtime';
|
|
8
|
+
/** Edge kind under which the runtime call graph extracted from the profile is stored. */
|
|
9
|
+
export const RUNTIME_CALL_EDGE_KIND = 'CALLS_RUNTIME';
|
|
10
|
+
export class RuntimeEnricher {
|
|
11
|
+
/**
|
|
12
|
+
* Ingests a V8 CPU profile and attaches `metadata.runtime` (self time and
|
|
13
|
+
* sample count) onto the graph nodes whose ranges enclose the profiled
|
|
14
|
+
* frames. Existing metadata is preserved; only the `runtime` key is written,
|
|
15
|
+
* so re-running with the same profile is idempotent. Unmatched frames are
|
|
16
|
+
* counted and returned in the report rather than dropped silently.
|
|
17
|
+
*/
|
|
18
|
+
static async enrich(store, profileText, options) {
|
|
19
|
+
const profile = CpuProfile.parse(profileText);
|
|
20
|
+
const frames = CpuProfile.aggregate(profile);
|
|
21
|
+
const nodes = await store.readNodes();
|
|
22
|
+
const result = RuntimeJoin.join(nodes, frames, { root: options.root });
|
|
23
|
+
const nodeById = new Map(nodes.map((node) => [node.id, node]));
|
|
24
|
+
const updates = [];
|
|
25
|
+
const hotspots = [];
|
|
26
|
+
for (const [id, attribution] of result.attributions) {
|
|
27
|
+
const node = nodeById.get(id);
|
|
28
|
+
if (node === undefined) {
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
const metrics = RuntimeEnricher.toMetrics(attribution);
|
|
32
|
+
updates.push({
|
|
33
|
+
id,
|
|
34
|
+
metadata: { ...node.metadata, [RUNTIME_METADATA_KEY]: metrics },
|
|
35
|
+
});
|
|
36
|
+
hotspots.push({
|
|
37
|
+
id,
|
|
38
|
+
name: node.name,
|
|
39
|
+
kind: node.kind,
|
|
40
|
+
filePath: node.filePath,
|
|
41
|
+
selfMs: metrics.selfMs,
|
|
42
|
+
samples: metrics.samples,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
await store.writeNodeMetadata(updates);
|
|
46
|
+
const manifest = {
|
|
47
|
+
source: RUNTIME_SOURCE_CPU_PROFILE,
|
|
48
|
+
totalSamples: CpuProfile.totalSamples(profile),
|
|
49
|
+
matchedSamples: result.matchedSamples,
|
|
50
|
+
totalSelfMicros: result.matchedSelfMicros + result.droppedSelfMicros,
|
|
51
|
+
matchedSelfMicros: result.matchedSelfMicros,
|
|
52
|
+
};
|
|
53
|
+
await store.writeGraphMeta(RUNTIME_MANIFEST_KEY, manifest);
|
|
54
|
+
const callEdges = CpuProfile.callEdges(profile);
|
|
55
|
+
const edgeResult = RuntimeJoin.joinCallEdges(nodes, callEdges, { root: options.root });
|
|
56
|
+
await store.clearEdgesByKind(RUNTIME_CALL_EDGE_KIND);
|
|
57
|
+
await store.writeEdges(edgeResult.edges.map((edge) => RuntimeEnricher.toCallEdge(edge)));
|
|
58
|
+
hotspots.sort((a, b) => b.selfMs - a.selfMs || b.samples - a.samples);
|
|
59
|
+
return {
|
|
60
|
+
totalSamples: CpuProfile.totalSamples(profile),
|
|
61
|
+
matchedNodes: updates.length,
|
|
62
|
+
matchedFrames: result.matchedFrames,
|
|
63
|
+
matchedSamples: result.matchedSamples,
|
|
64
|
+
matchedSelfMs: RuntimeEnricher.microsToMs(result.matchedSelfMicros),
|
|
65
|
+
matchedByName: result.matchedByName,
|
|
66
|
+
matchedByRange: result.matchedByRange,
|
|
67
|
+
droppedFrames: result.droppedFrames,
|
|
68
|
+
droppedSamples: result.droppedSamples,
|
|
69
|
+
runtimeEdges: edgeResult.matchedEdges,
|
|
70
|
+
droppedCallEdges: edgeResult.droppedEdges,
|
|
71
|
+
dropped: result.dropped,
|
|
72
|
+
hotspots,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
static toMetrics(attribution) {
|
|
76
|
+
return {
|
|
77
|
+
source: RUNTIME_SOURCE_CPU_PROFILE,
|
|
78
|
+
samples: attribution.samples,
|
|
79
|
+
selfMicros: attribution.selfMicros,
|
|
80
|
+
selfMs: RuntimeEnricher.microsToMs(attribution.selfMicros),
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
/** Builds a `CALLS_RUNTIME` graph edge from a resolved runtime call edge, weighted by its sample count. */
|
|
84
|
+
static toCallEdge(edge) {
|
|
85
|
+
return {
|
|
86
|
+
id: `${RUNTIME_CALL_EDGE_KIND}:${edge.from}->${edge.to}`,
|
|
87
|
+
kind: RUNTIME_CALL_EDGE_KIND,
|
|
88
|
+
from: edge.from,
|
|
89
|
+
to: edge.to,
|
|
90
|
+
metadata: { source: RUNTIME_SOURCE_CPU_PROFILE, samples: edge.samples },
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
/** Converts microseconds to milliseconds, rounded to microsecond precision. */
|
|
94
|
+
static microsToMs(micros) {
|
|
95
|
+
return Math.round(micros) / 1000;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=runtime_enricher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime_enricher.js","sourceRoot":"","sources":["../../src/enrich/runtime_enricher.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { FrameSample, RuntimeCallEdge } from './cpu_profile.js';
|
|
2
|
+
/** A graph node reduced to the fields the join needs. */
|
|
3
|
+
export type RuntimeTargetNode = {
|
|
4
|
+
id: string;
|
|
5
|
+
kind: string;
|
|
6
|
+
name: string;
|
|
7
|
+
filePath: string;
|
|
8
|
+
startLine: number;
|
|
9
|
+
endLine: number;
|
|
10
|
+
};
|
|
11
|
+
/** Self time and sample count attributed to one graph node. */
|
|
12
|
+
export type RuntimeAttribution = {
|
|
13
|
+
samples: number;
|
|
14
|
+
selfMicros: number;
|
|
15
|
+
};
|
|
16
|
+
/** How a frame was attached to its node — by symbol name or by line range. */
|
|
17
|
+
export type MatchVia = 'name' | 'range';
|
|
18
|
+
/**
|
|
19
|
+
* A group of profile frames that could not be attached to a single graph node,
|
|
20
|
+
* aggregated by a human-readable label so coverage gaps are reportable rather
|
|
21
|
+
* than silently lost. `ambiguous` means the frame's name matched several nodes
|
|
22
|
+
* in the file and the (transpiled) line could not break the tie.
|
|
23
|
+
*/
|
|
24
|
+
export type DroppedFrameGroup = {
|
|
25
|
+
label: string;
|
|
26
|
+
reason: 'no-file' | 'no-node' | 'ambiguous';
|
|
27
|
+
samples: number;
|
|
28
|
+
selfMicros: number;
|
|
29
|
+
};
|
|
30
|
+
export type RuntimeJoinResult = {
|
|
31
|
+
attributions: Map<string, RuntimeAttribution>;
|
|
32
|
+
matchedFrames: number;
|
|
33
|
+
matchedSamples: number;
|
|
34
|
+
matchedSelfMicros: number;
|
|
35
|
+
matchedByName: number;
|
|
36
|
+
matchedByRange: number;
|
|
37
|
+
droppedFrames: number;
|
|
38
|
+
droppedSamples: number;
|
|
39
|
+
droppedSelfMicros: number;
|
|
40
|
+
dropped: DroppedFrameGroup[];
|
|
41
|
+
};
|
|
42
|
+
export type RuntimeJoinOptions = {
|
|
43
|
+
/** Project root the profile's absolute frame urls are resolved against. */
|
|
44
|
+
root: string;
|
|
45
|
+
};
|
|
46
|
+
/** A runtime call edge resolved onto graph node ids, weighted by the samples that flowed through it. */
|
|
47
|
+
export type RuntimeEdge = {
|
|
48
|
+
from: string;
|
|
49
|
+
to: string;
|
|
50
|
+
samples: number;
|
|
51
|
+
};
|
|
52
|
+
/** The outcome of joining the profile's call-tree edges onto graph nodes. */
|
|
53
|
+
export type RuntimeCallJoinResult = {
|
|
54
|
+
edges: RuntimeEdge[];
|
|
55
|
+
matchedEdges: number;
|
|
56
|
+
droppedEdges: number;
|
|
57
|
+
droppedSamples: number;
|
|
58
|
+
};
|
|
59
|
+
export declare class RuntimeJoin {
|
|
60
|
+
/**
|
|
61
|
+
* Joins profile frames onto graph nodes with a hybrid key that survives
|
|
62
|
+
* transpilation.
|
|
63
|
+
*
|
|
64
|
+
* For each frame the url is resolved to a graph `filePath`, then within that
|
|
65
|
+
* file:
|
|
66
|
+
* 1. **By name** — a frame's `functionName` matched against node names. A
|
|
67
|
+
* unique match wins outright; this is the path that works when a
|
|
68
|
+
* transpiler (tsx/esbuild) has collapsed line numbers, since the function
|
|
69
|
+
* name is still intact.
|
|
70
|
+
* 2. **By range** — when the name is absent or matches nothing, the innermost
|
|
71
|
+
* node whose `[startLine, endLine]` encloses the frame line. This is the
|
|
72
|
+
* precise path for line-preserving runs and also disambiguates a name that
|
|
73
|
+
* matched several nodes.
|
|
74
|
+
*
|
|
75
|
+
* Frames resolving to no in-project file, to a file with no matching node, or
|
|
76
|
+
* to an unbreakable name tie are counted and reported as dropped — never
|
|
77
|
+
* attached.
|
|
78
|
+
*/
|
|
79
|
+
static join(nodes: RuntimeTargetNode[], frames: FrameSample[], options: RuntimeJoinOptions): RuntimeJoinResult;
|
|
80
|
+
/**
|
|
81
|
+
* Joins the profile's call-tree edges onto graph node ids, reusing the same
|
|
82
|
+
* per-frame resolution as {@link join} (name match, then enclosing range) for
|
|
83
|
+
* both endpoints. Edges whose caller or callee resolves to nothing, and
|
|
84
|
+
* self-edges (recursion landing on one node), are dropped and counted; the
|
|
85
|
+
* survivors are aggregated by node pair, summing the samples that flowed through
|
|
86
|
+
* each call.
|
|
87
|
+
*/
|
|
88
|
+
static joinCallEdges(nodes: RuntimeTargetNode[], callEdges: RuntimeCallEdge[], options: RuntimeJoinOptions): RuntimeCallJoinResult;
|
|
89
|
+
/** Resolves a single frame to its graph node — file path, then name-or-range within that file — or undefined. */
|
|
90
|
+
private static resolveFrame;
|
|
91
|
+
private static indexByFile;
|
|
92
|
+
/**
|
|
93
|
+
* Resolves a frame to a node within its file, preferring a name match and
|
|
94
|
+
* falling back to an enclosing-range match.
|
|
95
|
+
*/
|
|
96
|
+
private static resolveNode;
|
|
97
|
+
/**
|
|
98
|
+
* Nodes in the file whose name equals the frame's function name (or its final
|
|
99
|
+
* dotted segment, so `Widget.render` matches a method named `render`). V8
|
|
100
|
+
* synthetic frames such as `(anonymous)` and `(root)` never match.
|
|
101
|
+
*/
|
|
102
|
+
private static nameMatches;
|
|
103
|
+
private static isMeaningfulName;
|
|
104
|
+
private static candidateNames;
|
|
105
|
+
/**
|
|
106
|
+
* Picks the innermost node whose range encloses `line`. Ties on range width
|
|
107
|
+
* break toward the later-starting node, which is the more deeply nested
|
|
108
|
+
* declaration. A non-positive line (V8 synthetic frame, or a transpiler that
|
|
109
|
+
* collapsed line numbers) encloses nothing.
|
|
110
|
+
*/
|
|
111
|
+
private static enclosingNode;
|
|
112
|
+
/**
|
|
113
|
+
* Maps a profile frame url to a graph `filePath`. Node-internal and empty
|
|
114
|
+
* urls (`node:`, `(root)`, `(idle)`) resolve to nothing. A url inside `root`
|
|
115
|
+
* resolves by relative path; otherwise a unique path-suffix match is used so
|
|
116
|
+
* a graph extracted under a different absolute prefix still attaches.
|
|
117
|
+
*/
|
|
118
|
+
private static resolveFilePath;
|
|
119
|
+
private static urlToAbsolute;
|
|
120
|
+
private static toPosix;
|
|
121
|
+
private static recordDrop;
|
|
122
|
+
private static dropLabel;
|
|
123
|
+
}
|
|
124
|
+
//# sourceMappingURL=runtime_join.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime_join.d.ts","sourceRoot":"","sources":["../../src/enrich/runtime_join.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
import { isAbsolute, relative, sep } from 'node:path';
|
|
2
|
+
import { fileURLToPath } from 'node:url';
|
|
3
|
+
/**
|
|
4
|
+
* Node kinds a CPU frame can plausibly name. Restricting name matching to these
|
|
5
|
+
* avoids attaching an executing frame to a same-named type or enum member.
|
|
6
|
+
*/
|
|
7
|
+
const NAME_MATCH_KINDS = new Set(['Function', 'Method', 'Property', 'Variable', 'Class']);
|
|
8
|
+
export class RuntimeJoin {
|
|
9
|
+
/**
|
|
10
|
+
* Joins profile frames onto graph nodes with a hybrid key that survives
|
|
11
|
+
* transpilation.
|
|
12
|
+
*
|
|
13
|
+
* For each frame the url is resolved to a graph `filePath`, then within that
|
|
14
|
+
* file:
|
|
15
|
+
* 1. **By name** — a frame's `functionName` matched against node names. A
|
|
16
|
+
* unique match wins outright; this is the path that works when a
|
|
17
|
+
* transpiler (tsx/esbuild) has collapsed line numbers, since the function
|
|
18
|
+
* name is still intact.
|
|
19
|
+
* 2. **By range** — when the name is absent or matches nothing, the innermost
|
|
20
|
+
* node whose `[startLine, endLine]` encloses the frame line. This is the
|
|
21
|
+
* precise path for line-preserving runs and also disambiguates a name that
|
|
22
|
+
* matched several nodes.
|
|
23
|
+
*
|
|
24
|
+
* Frames resolving to no in-project file, to a file with no matching node, or
|
|
25
|
+
* to an unbreakable name tie are counted and reported as dropped — never
|
|
26
|
+
* attached.
|
|
27
|
+
*/
|
|
28
|
+
static join(nodes, frames, options) {
|
|
29
|
+
const byFile = RuntimeJoin.indexByFile(nodes);
|
|
30
|
+
const filePaths = [...byFile.keys()];
|
|
31
|
+
const attributions = new Map();
|
|
32
|
+
const dropped = new Map();
|
|
33
|
+
let matchedFrames = 0;
|
|
34
|
+
let matchedSamples = 0;
|
|
35
|
+
let matchedSelfMicros = 0;
|
|
36
|
+
let matchedByName = 0;
|
|
37
|
+
let matchedByRange = 0;
|
|
38
|
+
let droppedFrames = 0;
|
|
39
|
+
let droppedSamples = 0;
|
|
40
|
+
let droppedSelfMicros = 0;
|
|
41
|
+
for (const frame of frames) {
|
|
42
|
+
const filePath = RuntimeJoin.resolveFilePath(frame.url, options.root, byFile, filePaths);
|
|
43
|
+
if (filePath === undefined) {
|
|
44
|
+
RuntimeJoin.recordDrop(dropped, frame, 'no-file');
|
|
45
|
+
droppedFrames += 1;
|
|
46
|
+
droppedSamples += frame.samples;
|
|
47
|
+
droppedSelfMicros += frame.selfMicros;
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
const resolution = RuntimeJoin.resolveNode(byFile.get(filePath) ?? [], frame);
|
|
51
|
+
if (resolution.node === undefined) {
|
|
52
|
+
RuntimeJoin.recordDrop(dropped, frame, resolution.reason);
|
|
53
|
+
droppedFrames += 1;
|
|
54
|
+
droppedSamples += frame.samples;
|
|
55
|
+
droppedSelfMicros += frame.selfMicros;
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
const current = attributions.get(resolution.node.id) ?? { samples: 0, selfMicros: 0 };
|
|
59
|
+
current.samples += frame.samples;
|
|
60
|
+
current.selfMicros += frame.selfMicros;
|
|
61
|
+
attributions.set(resolution.node.id, current);
|
|
62
|
+
matchedFrames += 1;
|
|
63
|
+
matchedSamples += frame.samples;
|
|
64
|
+
matchedSelfMicros += frame.selfMicros;
|
|
65
|
+
if (resolution.via === 'name') {
|
|
66
|
+
matchedByName += 1;
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
matchedByRange += 1;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return {
|
|
73
|
+
attributions,
|
|
74
|
+
matchedFrames,
|
|
75
|
+
matchedSamples,
|
|
76
|
+
matchedSelfMicros,
|
|
77
|
+
matchedByName,
|
|
78
|
+
matchedByRange,
|
|
79
|
+
droppedFrames,
|
|
80
|
+
droppedSamples,
|
|
81
|
+
droppedSelfMicros,
|
|
82
|
+
dropped: [...dropped.values()].sort((a, b) => b.selfMicros - a.selfMicros),
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Joins the profile's call-tree edges onto graph node ids, reusing the same
|
|
87
|
+
* per-frame resolution as {@link join} (name match, then enclosing range) for
|
|
88
|
+
* both endpoints. Edges whose caller or callee resolves to nothing, and
|
|
89
|
+
* self-edges (recursion landing on one node), are dropped and counted; the
|
|
90
|
+
* survivors are aggregated by node pair, summing the samples that flowed through
|
|
91
|
+
* each call.
|
|
92
|
+
*/
|
|
93
|
+
static joinCallEdges(nodes, callEdges, options) {
|
|
94
|
+
const byFile = RuntimeJoin.indexByFile(nodes);
|
|
95
|
+
const filePaths = [...byFile.keys()];
|
|
96
|
+
const aggregated = new Map();
|
|
97
|
+
let matchedEdges = 0;
|
|
98
|
+
let droppedEdges = 0;
|
|
99
|
+
let droppedSamples = 0;
|
|
100
|
+
for (const edge of callEdges) {
|
|
101
|
+
const from = RuntimeJoin.resolveFrame(edge.caller, byFile, filePaths, options.root);
|
|
102
|
+
const to = RuntimeJoin.resolveFrame(edge.callee, byFile, filePaths, options.root);
|
|
103
|
+
if (from === undefined || to === undefined || from.id === to.id) {
|
|
104
|
+
droppedEdges += 1;
|
|
105
|
+
droppedSamples += edge.samples;
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
const key = `${from.id}->${to.id}`;
|
|
109
|
+
const existing = aggregated.get(key);
|
|
110
|
+
if (existing === undefined) {
|
|
111
|
+
aggregated.set(key, { from: from.id, to: to.id, samples: edge.samples });
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
existing.samples += edge.samples;
|
|
115
|
+
}
|
|
116
|
+
matchedEdges += 1;
|
|
117
|
+
}
|
|
118
|
+
return { edges: [...aggregated.values()], matchedEdges, droppedEdges, droppedSamples };
|
|
119
|
+
}
|
|
120
|
+
/** Resolves a single frame to its graph node — file path, then name-or-range within that file — or undefined. */
|
|
121
|
+
static resolveFrame(frame, byFile, filePaths, root) {
|
|
122
|
+
const filePath = RuntimeJoin.resolveFilePath(frame.url, root, byFile, filePaths);
|
|
123
|
+
if (filePath === undefined) {
|
|
124
|
+
return undefined;
|
|
125
|
+
}
|
|
126
|
+
return RuntimeJoin.resolveNode(byFile.get(filePath) ?? [], frame).node;
|
|
127
|
+
}
|
|
128
|
+
static indexByFile(nodes) {
|
|
129
|
+
const byFile = new Map();
|
|
130
|
+
for (const node of nodes) {
|
|
131
|
+
const bucket = byFile.get(node.filePath);
|
|
132
|
+
if (bucket === undefined) {
|
|
133
|
+
byFile.set(node.filePath, [node]);
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
bucket.push(node);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return byFile;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Resolves a frame to a node within its file, preferring a name match and
|
|
143
|
+
* falling back to an enclosing-range match.
|
|
144
|
+
*/
|
|
145
|
+
static resolveNode(fileNodes, frame) {
|
|
146
|
+
const nameMatches = RuntimeJoin.nameMatches(fileNodes, frame.functionName);
|
|
147
|
+
if (nameMatches.length === 1) {
|
|
148
|
+
return { node: nameMatches[0], via: 'name' };
|
|
149
|
+
}
|
|
150
|
+
if (nameMatches.length > 1) {
|
|
151
|
+
const disambiguated = RuntimeJoin.enclosingNode(nameMatches, frame.line);
|
|
152
|
+
if (disambiguated !== undefined) {
|
|
153
|
+
return { node: disambiguated, via: 'name' };
|
|
154
|
+
}
|
|
155
|
+
return { node: undefined, reason: 'ambiguous' };
|
|
156
|
+
}
|
|
157
|
+
const enclosed = RuntimeJoin.enclosingNode(fileNodes, frame.line);
|
|
158
|
+
if (enclosed !== undefined) {
|
|
159
|
+
return { node: enclosed, via: 'range' };
|
|
160
|
+
}
|
|
161
|
+
return { node: undefined, reason: 'no-node' };
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Nodes in the file whose name equals the frame's function name (or its final
|
|
165
|
+
* dotted segment, so `Widget.render` matches a method named `render`). V8
|
|
166
|
+
* synthetic frames such as `(anonymous)` and `(root)` never match.
|
|
167
|
+
*/
|
|
168
|
+
static nameMatches(fileNodes, functionName) {
|
|
169
|
+
if (RuntimeJoin.isMeaningfulName(functionName) === false) {
|
|
170
|
+
return [];
|
|
171
|
+
}
|
|
172
|
+
const candidates = RuntimeJoin.candidateNames(functionName);
|
|
173
|
+
return fileNodes.filter((node) => NAME_MATCH_KINDS.has(node.kind) && candidates.includes(node.name));
|
|
174
|
+
}
|
|
175
|
+
static isMeaningfulName(functionName) {
|
|
176
|
+
return functionName.length > 0 && functionName.startsWith('(') === false;
|
|
177
|
+
}
|
|
178
|
+
static candidateNames(functionName) {
|
|
179
|
+
const dot = functionName.lastIndexOf('.');
|
|
180
|
+
if (dot >= 0 && dot < functionName.length - 1) {
|
|
181
|
+
return [functionName, functionName.slice(dot + 1)];
|
|
182
|
+
}
|
|
183
|
+
return [functionName];
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Picks the innermost node whose range encloses `line`. Ties on range width
|
|
187
|
+
* break toward the later-starting node, which is the more deeply nested
|
|
188
|
+
* declaration. A non-positive line (V8 synthetic frame, or a transpiler that
|
|
189
|
+
* collapsed line numbers) encloses nothing.
|
|
190
|
+
*/
|
|
191
|
+
static enclosingNode(nodes, line) {
|
|
192
|
+
if (line < 1) {
|
|
193
|
+
return undefined;
|
|
194
|
+
}
|
|
195
|
+
let best;
|
|
196
|
+
let bestSpan = Number.POSITIVE_INFINITY;
|
|
197
|
+
for (const node of nodes) {
|
|
198
|
+
if (node.startLine < 1 || node.endLine < node.startLine) {
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
if (line < node.startLine || line > node.endLine) {
|
|
202
|
+
continue;
|
|
203
|
+
}
|
|
204
|
+
const span = node.endLine - node.startLine;
|
|
205
|
+
if (span < bestSpan || (span === bestSpan && best !== undefined && node.startLine > best.startLine)) {
|
|
206
|
+
best = node;
|
|
207
|
+
bestSpan = span;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
return best;
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Maps a profile frame url to a graph `filePath`. Node-internal and empty
|
|
214
|
+
* urls (`node:`, `(root)`, `(idle)`) resolve to nothing. A url inside `root`
|
|
215
|
+
* resolves by relative path; otherwise a unique path-suffix match is used so
|
|
216
|
+
* a graph extracted under a different absolute prefix still attaches.
|
|
217
|
+
*/
|
|
218
|
+
static resolveFilePath(url, root, byFile, filePaths) {
|
|
219
|
+
if (url.length === 0 || url.startsWith('node:')) {
|
|
220
|
+
return undefined;
|
|
221
|
+
}
|
|
222
|
+
const absolute = RuntimeJoin.urlToAbsolute(url);
|
|
223
|
+
if (absolute === undefined) {
|
|
224
|
+
return byFile.has(url) ? url : undefined;
|
|
225
|
+
}
|
|
226
|
+
const normalizedAbsolute = RuntimeJoin.toPosix(absolute);
|
|
227
|
+
const candidate = RuntimeJoin.toPosix(relative(root, absolute));
|
|
228
|
+
if (candidate.length > 0 && candidate.startsWith('..') === false && isAbsolute(candidate) === false && byFile.has(candidate)) {
|
|
229
|
+
return candidate;
|
|
230
|
+
}
|
|
231
|
+
let suffixMatch;
|
|
232
|
+
for (const filePath of filePaths) {
|
|
233
|
+
if (normalizedAbsolute === filePath || normalizedAbsolute.endsWith(`/${filePath}`)) {
|
|
234
|
+
if (suffixMatch !== undefined && suffixMatch !== filePath) {
|
|
235
|
+
return undefined;
|
|
236
|
+
}
|
|
237
|
+
suffixMatch = filePath;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
return suffixMatch;
|
|
241
|
+
}
|
|
242
|
+
static urlToAbsolute(url) {
|
|
243
|
+
if (url.startsWith('file://')) {
|
|
244
|
+
return fileURLToPath(url);
|
|
245
|
+
}
|
|
246
|
+
if (isAbsolute(url)) {
|
|
247
|
+
return url;
|
|
248
|
+
}
|
|
249
|
+
return undefined;
|
|
250
|
+
}
|
|
251
|
+
static toPosix(value) {
|
|
252
|
+
return sep === '/' ? value : value.split(sep).join('/');
|
|
253
|
+
}
|
|
254
|
+
static recordDrop(dropped, frame, reason) {
|
|
255
|
+
const label = RuntimeJoin.dropLabel(frame, reason);
|
|
256
|
+
const key = `${reason}:${label}`;
|
|
257
|
+
const group = dropped.get(key) ?? { label, reason, samples: 0, selfMicros: 0 };
|
|
258
|
+
group.samples += frame.samples;
|
|
259
|
+
group.selfMicros += frame.selfMicros;
|
|
260
|
+
dropped.set(key, group);
|
|
261
|
+
}
|
|
262
|
+
static dropLabel(frame, reason) {
|
|
263
|
+
const name = frame.functionName.length > 0 ? frame.functionName : '(anonymous)';
|
|
264
|
+
if (reason === 'no-file') {
|
|
265
|
+
return frame.url.length === 0 ? name : `${name} ${frame.url}`;
|
|
266
|
+
}
|
|
267
|
+
return `${name} ${frame.url}:${frame.line}`;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
//# sourceMappingURL=runtime_join.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime_join.js","sourceRoot":"","sources":["../../src/enrich/runtime_join.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { SourceFile } from 'ts-morph';
|
|
2
|
+
import { Extraction } from './structural_extractor.js';
|
|
3
|
+
/**
|
|
4
|
+
* Detects outbound HTTP calls via `fetch(...)` and emits an `ExternalAPI` node for
|
|
5
|
+
* the called host plus a `CALLS_EXTERNAL` edge from the calling declaration. The
|
|
6
|
+
* host is taken from a static URL argument (`fetch('https://api.example.com/…')`);
|
|
7
|
+
* a call whose target is not statically a URL is attributed to a single generic
|
|
8
|
+
* `(dynamic)` node, so the outbound-I/O call site is still surfaced. Calls to the
|
|
9
|
+
* same host from one scope collapse to a single counted edge.
|
|
10
|
+
*
|
|
11
|
+
* Detection is purely syntactic (no symbol resolution), so it runs in the
|
|
12
|
+
* structural pass; a project that never calls `fetch` is unchanged. Other HTTP
|
|
13
|
+
* clients (axios, got, …) are out of scope for now — fetch first (#31 Part 2b).
|
|
14
|
+
*/
|
|
15
|
+
export declare class ApiExtractor {
|
|
16
|
+
static extract(sourceFile: SourceFile, rootPath: string): Extraction;
|
|
17
|
+
/** `fetch(...)` — the bare global, or `window` / `globalThis` / `self`.fetch(...). */
|
|
18
|
+
private static isFetchCall;
|
|
19
|
+
/** The host of a static URL argument, or {@link DYNAMIC_TARGET} when not statically a URL. */
|
|
20
|
+
private static targetHost;
|
|
21
|
+
/** The literal text of a string / no-substitution-template argument, else undefined. */
|
|
22
|
+
private static staticUrl;
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=api_extractor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api_extractor.d.ts","sourceRoot":"","sources":["../../src/extract/api_extractor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,UAAU,EAAc,MAAM,UAAU,CAAC;AAKxD,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAQvD;;;;;;;;;;;GAWG;AACH,qBAAa,YAAY;IACxB,MAAM,CAAC,OAAO,CAAC,UAAU,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,GAAG,UAAU;IAmBpE,sFAAsF;IACtF,OAAO,CAAC,MAAM,CAAC,WAAW;IAY1B,8FAA8F;IAC9F,OAAO,CAAC,MAAM,CAAC,UAAU;IAYzB,wFAAwF;IACxF,OAAO,CAAC,MAAM,CAAC,SAAS;CAOxB"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { Node, SyntaxKind } from 'ts-morph';
|
|
2
|
+
import { NodeId } from './node_id.js';
|
|
3
|
+
import { ScopeResolver } from './scope_resolver.js';
|
|
4
|
+
/** Receivers on which a `.fetch(...)` call is the WHATWG global rather than a user method. */
|
|
5
|
+
const FETCH_GLOBALS = new Set(['window', 'globalThis', 'self']);
|
|
6
|
+
/** Host used when a fetch target is not a static URL (a variable, a built string, a relative path). */
|
|
7
|
+
const DYNAMIC_TARGET = '(dynamic)';
|
|
8
|
+
/**
|
|
9
|
+
* Detects outbound HTTP calls via `fetch(...)` and emits an `ExternalAPI` node for
|
|
10
|
+
* the called host plus a `CALLS_EXTERNAL` edge from the calling declaration. The
|
|
11
|
+
* host is taken from a static URL argument (`fetch('https://api.example.com/…')`);
|
|
12
|
+
* a call whose target is not statically a URL is attributed to a single generic
|
|
13
|
+
* `(dynamic)` node, so the outbound-I/O call site is still surfaced. Calls to the
|
|
14
|
+
* same host from one scope collapse to a single counted edge.
|
|
15
|
+
*
|
|
16
|
+
* Detection is purely syntactic (no symbol resolution), so it runs in the
|
|
17
|
+
* structural pass; a project that never calls `fetch` is unchanged. Other HTTP
|
|
18
|
+
* clients (axios, got, …) are out of scope for now — fetch first (#31 Part 2b).
|
|
19
|
+
*/
|
|
20
|
+
export class ApiExtractor {
|
|
21
|
+
static extract(sourceFile, rootPath) {
|
|
22
|
+
const nodes = [];
|
|
23
|
+
const edges = [];
|
|
24
|
+
const moduleId = NodeId.forModule(sourceFile.getFilePath(), rootPath);
|
|
25
|
+
for (const call of sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression)) {
|
|
26
|
+
if (ApiExtractor.isFetchCall(call.getExpression()) === false) {
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
const host = ApiExtractor.targetHost(call.getArguments()[0]);
|
|
30
|
+
const apiId = NodeId.forExternalApi(host);
|
|
31
|
+
nodes.push({ id: apiId, kind: 'ExternalAPI', name: host, filePath: host });
|
|
32
|
+
const scopeId = ScopeResolver.enclosingId(call, moduleId, rootPath);
|
|
33
|
+
edges.push({ id: `CALLS_EXTERNAL:${scopeId}->${apiId}`, kind: 'CALLS_EXTERNAL', from: scopeId, to: apiId });
|
|
34
|
+
}
|
|
35
|
+
return { nodes, edges };
|
|
36
|
+
}
|
|
37
|
+
/** `fetch(...)` — the bare global, or `window` / `globalThis` / `self`.fetch(...). */
|
|
38
|
+
static isFetchCall(callee) {
|
|
39
|
+
if (Node.isIdentifier(callee) === true) {
|
|
40
|
+
return callee.getText() === 'fetch';
|
|
41
|
+
}
|
|
42
|
+
const access = callee.asKind(SyntaxKind.PropertyAccessExpression);
|
|
43
|
+
if (access === undefined || access.getName() !== 'fetch') {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
const target = access.getExpression();
|
|
47
|
+
return Node.isIdentifier(target) === true && FETCH_GLOBALS.has(target.getText()) === true;
|
|
48
|
+
}
|
|
49
|
+
/** The host of a static URL argument, or {@link DYNAMIC_TARGET} when not statically a URL. */
|
|
50
|
+
static targetHost(argument) {
|
|
51
|
+
const url = ApiExtractor.staticUrl(argument);
|
|
52
|
+
if (url === undefined) {
|
|
53
|
+
return DYNAMIC_TARGET;
|
|
54
|
+
}
|
|
55
|
+
try {
|
|
56
|
+
return new URL(url).host || DYNAMIC_TARGET;
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
return DYNAMIC_TARGET;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/** The literal text of a string / no-substitution-template argument, else undefined. */
|
|
63
|
+
static staticUrl(argument) {
|
|
64
|
+
const string = argument?.asKind(SyntaxKind.StringLiteral);
|
|
65
|
+
if (string !== undefined) {
|
|
66
|
+
return string.getLiteralText();
|
|
67
|
+
}
|
|
68
|
+
return argument?.asKind(SyntaxKind.NoSubstitutionTemplateLiteral)?.getLiteralText();
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=api_extractor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api_extractor.js","sourceRoot":"","sources":["../../src/extract/api_extractor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { SourceFile } from 'ts-morph';
|
|
2
|
+
import { Extraction } from './structural_extractor.js';
|
|
3
|
+
/**
|
|
4
|
+
* Detects configuration reads — `process.env.NAME` and `process.env['NAME']` —
|
|
5
|
+
* and emits one `ConfigFlag` node per distinct variable plus a `READS_CONFIG`
|
|
6
|
+
* edge from the enclosing declaration that performs the read. A variable read in
|
|
7
|
+
* several places collapses to one node (keyed by name) with one counted edge per
|
|
8
|
+
* reading scope.
|
|
9
|
+
*
|
|
10
|
+
* The detection is purely syntactic (no symbol resolution), so it runs in the
|
|
11
|
+
* structural pass and is always emitted. A project that never touches
|
|
12
|
+
* `process.env` produces no config nodes or edges, leaving its graph unchanged.
|
|
13
|
+
*/
|
|
14
|
+
export declare class ConfigExtractor {
|
|
15
|
+
static extract(sourceFile: SourceFile, rootPath: string): Extraction;
|
|
16
|
+
private static emit;
|
|
17
|
+
/** Whether a node is the `process.env` member access (`process` identifier, `env` name). */
|
|
18
|
+
private static isProcessEnv;
|
|
19
|
+
/** The literal key of `process.env['NAME']`, or undefined when the key is computed. */
|
|
20
|
+
private static stringArgument;
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=config_extractor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config_extractor.d.ts","sourceRoot":"","sources":["../../src/extract/config_extractor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,UAAU,EAAc,MAAM,UAAU,CAAC;AAKxD,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAEvD;;;;;;;;;;GAUG;AACH,qBAAa,eAAe;IAC3B,MAAM,CAAC,OAAO,CAAC,UAAU,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,GAAG,UAAU;IAuBpE,OAAO,CAAC,MAAM,CAAC,IAAI;IAiBnB,4FAA4F;IAC5F,OAAO,CAAC,MAAM,CAAC,YAAY;IAS3B,uFAAuF;IACvF,OAAO,CAAC,MAAM,CAAC,cAAc;CAK7B"}
|