@lsctech/polaris 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 +87 -0
- package/dist/__mocks__/tool-server-linear.js +34 -0
- package/dist/cli/adopt-approve.js +60 -0
- package/dist/cli/adopt-cognition.js +199 -0
- package/dist/cli/adopt-instructions.js +143 -0
- package/dist/cli/adopt-rules.js +169 -0
- package/dist/cli/adopt-scan.js +25 -0
- package/dist/cli/adopt-smartdocs.js +192 -0
- package/dist/cli/adoption-inventory.js +350 -0
- package/dist/cli/adoption-plan.js +222 -0
- package/dist/cli/args.js +28 -0
- package/dist/cli/args.test.js +30 -0
- package/dist/cli/branding.js +41 -0
- package/dist/cli/graph.js +358 -0
- package/dist/cli/index.js +127 -0
- package/dist/cli/index.test.js +162 -0
- package/dist/cli/init-detect.js +61 -0
- package/dist/cli/init.js +492 -0
- package/dist/cli/init.test.js +259 -0
- package/dist/cli/librarian.js +32 -0
- package/dist/cli/medic.js +96 -0
- package/dist/cli/startup.test.js +23 -0
- package/dist/cli/tracker.js +130 -0
- package/dist/cli/tracker.test.js +126 -0
- package/dist/cli/version.js +10 -0
- package/dist/cli/version.test.js +11 -0
- package/dist/cli/worker.js +287 -0
- package/dist/cluster-state/fixtures/test-clusters.json +19 -0
- package/dist/cluster-state/store.js +302 -0
- package/dist/cluster-state/store.test.js +145 -0
- package/dist/cluster-state/types.js +2 -0
- package/dist/cognition/archive.js +158 -0
- package/dist/cognition/closeout-librarian-types.js +161 -0
- package/dist/cognition/cognition.test.js +623 -0
- package/dist/cognition/index.js +41 -0
- package/dist/cognition/librarian-dispatch.js +391 -0
- package/dist/cognition/librarian-dispatch.test.js +247 -0
- package/dist/cognition/librarian-packet.js +313 -0
- package/dist/cognition/librarian-types.js +57 -0
- package/dist/cognition/route-cognition-delta.js +350 -0
- package/dist/cognition/summary-delta.js +279 -0
- package/dist/cognition/validate.js +162 -0
- package/dist/config/defaults.js +98 -0
- package/dist/config/doctor.js +273 -0
- package/dist/config/index.js +14 -0
- package/dist/config/loader.js +58 -0
- package/dist/config/provider-detect.js +60 -0
- package/dist/config/provider-detect.test.js +119 -0
- package/dist/config/schema.js +2 -0
- package/dist/config/show.js +44 -0
- package/dist/config/show.test.js +42 -0
- package/dist/config/validator.js +675 -0
- package/dist/config/validator.test.js +77 -0
- package/dist/docs/canon-check.js +334 -0
- package/dist/docs/canon-check.test.js +207 -0
- package/dist/docs/doctrine.js +133 -0
- package/dist/docs/doctrine.test.js +139 -0
- package/dist/docs/index.js +168 -0
- package/dist/docs/index.test.js +83 -0
- package/dist/docs/ingest.js +295 -0
- package/dist/docs/ingest.test.js +71 -0
- package/dist/docs/migrate.js +165 -0
- package/dist/docs/migrate.test.js +136 -0
- package/dist/docs/seed-instructions.js +147 -0
- package/dist/docs/seed-instructions.test.js +137 -0
- package/dist/docs/validate-instructions.js +267 -0
- package/dist/docs/validate-instructions.test.js +238 -0
- package/dist/finalize/artifact-policy.js +214 -0
- package/dist/finalize/artifact-policy.test.js +84 -0
- package/dist/finalize/delivery-integrity.js +141 -0
- package/dist/finalize/finalize.test.js +210 -0
- package/dist/finalize/github.js +41 -0
- package/dist/finalize/index.js +474 -0
- package/dist/finalize/linear.js +213 -0
- package/dist/finalize/run-report.js +47 -0
- package/dist/finalize/steps/01-map-update.js +11 -0
- package/dist/finalize/steps/02-map-validate.js +11 -0
- package/dist/finalize/steps/03-schema-validate.js +11 -0
- package/dist/finalize/steps/04-run-checks.js +118 -0
- package/dist/finalize/steps/05-generate-report.js +14 -0
- package/dist/finalize/steps/06-commit.js +39 -0
- package/dist/finalize/steps/07-push.js +10 -0
- package/dist/finalize/steps/08-create-pr.js +9 -0
- package/dist/finalize/steps/09-update-state.js +9 -0
- package/dist/finalize/steps/10-append-jsonl.js +23 -0
- package/dist/finalize/steps/11-update-linear.js +22 -0
- package/dist/finalize/steps/12-archive.js +21 -0
- package/dist/graph/adapter/c/adapter.js +45 -0
- package/dist/graph/adapter/c/extract.js +135 -0
- package/dist/graph/adapter/c/index.js +10 -0
- package/dist/graph/adapter/c/runtime.js +67 -0
- package/dist/graph/adapter/cpp/adapter.js +45 -0
- package/dist/graph/adapter/cpp/extract.js +159 -0
- package/dist/graph/adapter/cpp/index.js +10 -0
- package/dist/graph/adapter/cpp/runtime.js +61 -0
- package/dist/graph/adapter/csharp/adapter.js +45 -0
- package/dist/graph/adapter/csharp/extract.js +127 -0
- package/dist/graph/adapter/csharp/index.js +10 -0
- package/dist/graph/adapter/csharp/runtime.js +61 -0
- package/dist/graph/adapter/dart/adapter.js +56 -0
- package/dist/graph/adapter/dart/extract.js +130 -0
- package/dist/graph/adapter/dart/index.js +10 -0
- package/dist/graph/adapter/dart/runtime.js +61 -0
- package/dist/graph/adapter/go/adapter.js +45 -0
- package/dist/graph/adapter/go/extract.js +112 -0
- package/dist/graph/adapter/go/index.js +10 -0
- package/dist/graph/adapter/go/runtime.js +58 -0
- package/dist/graph/adapter/kotlin-java/extract-common.js +83 -0
- package/dist/graph/adapter/kotlin-java/index.js +15 -0
- package/dist/graph/adapter/kotlin-java/java-adapter.js +45 -0
- package/dist/graph/adapter/kotlin-java/java-extract.js +42 -0
- package/dist/graph/adapter/kotlin-java/kotlin-adapter.js +48 -0
- package/dist/graph/adapter/kotlin-java/kotlin-extract.js +58 -0
- package/dist/graph/adapter/kotlin-java/runtime.js +75 -0
- package/dist/graph/adapter/python/adapter.js +45 -0
- package/dist/graph/adapter/python/extract.js +121 -0
- package/dist/graph/adapter/python/index.js +10 -0
- package/dist/graph/adapter/python/runtime.js +70 -0
- package/dist/graph/adapter/registry.js +82 -0
- package/dist/graph/adapter/rust/adapter.js +45 -0
- package/dist/graph/adapter/rust/extract.js +110 -0
- package/dist/graph/adapter/rust/index.js +10 -0
- package/dist/graph/adapter/rust/runtime.js +74 -0
- package/dist/graph/adapter/shell/adapter.js +34 -0
- package/dist/graph/adapter/shell/extract.js +98 -0
- package/dist/graph/adapter/shell/index.js +8 -0
- package/dist/graph/adapter/svelte/adapter.js +91 -0
- package/dist/graph/adapter/svelte/extract.js +41 -0
- package/dist/graph/adapter/svelte/index.js +8 -0
- package/dist/graph/adapter/swift/adapter.js +45 -0
- package/dist/graph/adapter/swift/extract.js +120 -0
- package/dist/graph/adapter/swift/index.js +10 -0
- package/dist/graph/adapter/swift/runtime.js +70 -0
- package/dist/graph/adapter/types.js +2 -0
- package/dist/graph/adapter/typescript-javascript/adapter.js +72 -0
- package/dist/graph/adapter/typescript-javascript/extract.js +115 -0
- package/dist/graph/adapter/typescript-javascript/index.js +10 -0
- package/dist/graph/adapter/typescript-javascript/runtime.js +97 -0
- package/dist/graph/capability/index.js +105 -0
- package/dist/graph/governance.js +104 -0
- package/dist/graph/parser/extract.js +5 -0
- package/dist/graph/parser/loader.js +2 -0
- package/dist/graph/parser/pipeline.js +194 -0
- package/dist/graph/query/index.js +277 -0
- package/dist/graph/query/types.js +5 -0
- package/dist/graph/resolver/build-edges.js +211 -0
- package/dist/graph/resolver/index.js +23 -0
- package/dist/graph/resolver/resolve-imports.js +168 -0
- package/dist/graph/store/adapter.js +132 -0
- package/dist/graph/store/queries.js +124 -0
- package/dist/graph/store/types.js +4 -0
- package/dist/ignore/defaults.js +41 -0
- package/dist/ignore/parser.js +39 -0
- package/dist/lint/map-reference-validator.js +72 -0
- package/dist/loop/abort.js +188 -0
- package/dist/loop/abort.test.js +151 -0
- package/dist/loop/adapters/__tests__/terminal-cli.test.js +92 -0
- package/dist/loop/adapters/agent-subtask.js +137 -0
- package/dist/loop/adapters/agent-subtask.test.js +151 -0
- package/dist/loop/adapters/cli-subtask-bridge.js +108 -0
- package/dist/loop/adapters/index.js +9 -0
- package/dist/loop/adapters/registry.js +17 -0
- package/dist/loop/adapters/terminal-cli.js +403 -0
- package/dist/loop/adapters/types.js +2 -0
- package/dist/loop/adapters/worker-instructions.js +85 -0
- package/dist/loop/body-parser.js +226 -0
- package/dist/loop/bootstrap-packet.js +97 -0
- package/dist/loop/bootstrap-packet.test.js +171 -0
- package/dist/loop/branch-governance.js +54 -0
- package/dist/loop/budget.js +63 -0
- package/dist/loop/budget.test.js +163 -0
- package/dist/loop/checkpoint.js +325 -0
- package/dist/loop/compact-return.js +59 -0
- package/dist/loop/continue.js +645 -0
- package/dist/loop/continue.test.js +337 -0
- package/dist/loop/dispatch-boundary.js +335 -0
- package/dist/loop/dispatch-state.js +223 -0
- package/dist/loop/dispatch-state.test.js +186 -0
- package/dist/loop/dispatch.js +1236 -0
- package/dist/loop/dispatch.test.js +499 -0
- package/dist/loop/ephemeral-smoke.test.js +160 -0
- package/dist/loop/evidence-backfill.js +216 -0
- package/dist/loop/execution-adapter.js +141 -0
- package/dist/loop/execution-adapter.test.js +66 -0
- package/dist/loop/finalize-evidence.js +169 -0
- package/dist/loop/git-custody.js +250 -0
- package/dist/loop/impl-packet-input.js +38 -0
- package/dist/loop/index.js +270 -0
- package/dist/loop/ledger.js +49 -0
- package/dist/loop/ledger.test.js +127 -0
- package/dist/loop/lifecycle-dispatch.js +348 -0
- package/dist/loop/lifecycle-dispatch.test.js +318 -0
- package/dist/loop/lifecycle.js +93 -0
- package/dist/loop/lifecycle.test.js +150 -0
- package/dist/loop/orphan-recovery.js +276 -0
- package/dist/loop/orphan-recovery.test.js +236 -0
- package/dist/loop/parent.js +1566 -0
- package/dist/loop/parent.test.js +738 -0
- package/dist/loop/provider-sealed-packet.test.js +164 -0
- package/dist/loop/resume.js +295 -0
- package/dist/loop/resume.test.js +172 -0
- package/dist/loop/run-bootstrap.js +234 -0
- package/dist/loop/run-preflight.js +203 -0
- package/dist/loop/status.js +665 -0
- package/dist/loop/status.test.js +235 -0
- package/dist/loop/wait.js +86 -0
- package/dist/loop/worker-packet.js +406 -0
- package/dist/loop/worker-packet.test.js +243 -0
- package/dist/loop/worker-prompt.js +260 -0
- package/dist/loop/worker-prompt.test.js +247 -0
- package/dist/loop/worker.js +498 -0
- package/dist/loop/worker.test.js +372 -0
- package/dist/map/atlas.js +77 -0
- package/dist/map/backfill.js +171 -0
- package/dist/map/backfill.test.js +120 -0
- package/dist/map/index.js +255 -0
- package/dist/map/inference.js +73 -0
- package/dist/map/inference.test.js +35 -0
- package/dist/map/query.js +157 -0
- package/dist/map/query.test.js +174 -0
- package/dist/map/update.js +355 -0
- package/dist/map/update.test.js +67 -0
- package/dist/map/validate.js +153 -0
- package/dist/map/validate.test.js +90 -0
- package/dist/map/welfare.js +86 -0
- package/dist/mcp/lib/invoke.js +35 -0
- package/dist/mcp/lib/redact.js +24 -0
- package/dist/mcp/lib/redact.test.js +112 -0
- package/dist/mcp/lib/root.js +41 -0
- package/dist/mcp/lib/root.test.js +114 -0
- package/dist/mcp/server.js +15 -0
- package/dist/mcp/tools/claim-child.js +119 -0
- package/dist/mcp/tools/claim-child.test.js +69 -0
- package/dist/mcp/tools/current-state.js +73 -0
- package/dist/mcp/tools/current-state.test.js +114 -0
- package/dist/mcp/tools/dispatch-result.js +186 -0
- package/dist/mcp/tools/dispatch-result.test.js +109 -0
- package/dist/mcp/tools/index.js +222 -0
- package/dist/mcp/tools/index.test.js +172 -0
- package/dist/mcp/tools/linear.js +74 -0
- package/dist/mcp/tools/loop-continue.js +159 -0
- package/dist/mcp/tools/loop-continue.test.js +186 -0
- package/dist/mcp/tools/loop-dry-run.js +15 -0
- package/dist/mcp/tools/status.js +65 -0
- package/dist/medic/chart-id.js +75 -0
- package/dist/medic/chart-schema.js +139 -0
- package/dist/runs/index.js +119 -0
- package/dist/runs/index.test.js +184 -0
- package/dist/runs/ledger-tail.js +15 -0
- package/dist/runs/list.js +29 -0
- package/dist/runs/reconcile.js +74 -0
- package/dist/runs/show.js +10 -0
- package/dist/runtime/audit/logger.js +15 -0
- package/dist/runtime/audit/reader.js +42 -0
- package/dist/runtime/checkpoint.js +89 -0
- package/dist/runtime/continuation/confirmed.js +225 -0
- package/dist/runtime/continuation/confirmed.test.js +421 -0
- package/dist/runtime/continuation/dry-run.js +74 -0
- package/dist/runtime/execution-window.js +106 -0
- package/dist/runtime/scheduling/child-selector.js +16 -0
- package/dist/runtime/state.js +43 -0
- package/dist/runtime/status.js +46 -0
- package/dist/runtime/verification/envelope.js +80 -0
- package/dist/runtime/verification/fingerprint.js +26 -0
- package/dist/skill-packet/cli.test.js +105 -0
- package/dist/skill-packet/generator.js +225 -0
- package/dist/skill-packet/generator.test.js +159 -0
- package/dist/skill-packet/index.js +49 -0
- package/dist/skill-packet/types.js +2 -0
- package/dist/smartdocs-engine/audit.js +194 -0
- package/dist/smartdocs-engine/audit.test.js +122 -0
- package/dist/smartdocs-engine/canon-check.js +340 -0
- package/dist/smartdocs-engine/canon-check.test.js +234 -0
- package/dist/smartdocs-engine/doctrine.js +633 -0
- package/dist/smartdocs-engine/doctrine.test.js +374 -0
- package/dist/smartdocs-engine/index.js +456 -0
- package/dist/smartdocs-engine/index.test.js +82 -0
- package/dist/smartdocs-engine/ingest.js +495 -0
- package/dist/smartdocs-engine/ingest.test.js +163 -0
- package/dist/smartdocs-engine/migrate.js +217 -0
- package/dist/smartdocs-engine/migrate.test.js +200 -0
- package/dist/smartdocs-engine/seed-instructions.js +335 -0
- package/dist/smartdocs-engine/seed-instructions.test.js +275 -0
- package/dist/smartdocs-engine/smartdoc-ignore.js +258 -0
- package/dist/smartdocs-engine/smartdoc-ignore.test.js +146 -0
- package/dist/smartdocs-engine/validate-instructions.js +366 -0
- package/dist/smartdocs-engine/validate-instructions.test.js +283 -0
- package/dist/tracker/adapters/github/index.js +305 -0
- package/dist/tracker/adapters/jira/index.js +293 -0
- package/dist/tracker/adapters/linear/http-errors.test.js +51 -0
- package/dist/tracker/adapters/linear/index.js +621 -0
- package/dist/tracker/adapters/linear/index.test.js +238 -0
- package/dist/tracker/adapters/linear.js +50 -0
- package/dist/tracker/adapters/local/index.js +10 -0
- package/dist/tracker/adapters/local-file/index.js +159 -0
- package/dist/tracker/adapters/mcp-bridge.js +158 -0
- package/dist/tracker/adapters/mcp-bridge.test.js +12 -0
- package/dist/tracker/adapters/spec/index.js +146 -0
- package/dist/tracker/capabilities.js +2 -0
- package/dist/tracker/index.js +49 -0
- package/dist/tracker/lifecycle-policy.js +139 -0
- package/dist/tracker/lifecycle-transition.js +164 -0
- package/dist/tracker/local-graph.js +98 -0
- package/dist/tracker/migration.js +56 -0
- package/dist/tracker/schema.js +86 -0
- package/dist/tracker/sync/index.js +404 -0
- package/dist/tracker/sync/queue-store.js +95 -0
- package/dist/tracker/sync/queue-store.test.js +104 -0
- package/dist/tracker/types.js +11 -0
- package/dist/types/linear.js +3 -0
- package/dist/types/result-packet.js +12 -0
- package/dist/types/runtime-state.js +2 -0
- package/dist/types/work-contract.js +2 -0
- package/dist/utils/monotonic-timestamp.js +13 -0
- package/dist/utils/monotonic-timestamp.test.js +19 -0
- package/package.json +64 -0
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.runExtractionPipeline = runExtractionPipeline;
|
|
4
|
+
const node_fs_1 = require("node:fs");
|
|
5
|
+
const node_crypto_1 = require("node:crypto");
|
|
6
|
+
const node_path_1 = require("node:path");
|
|
7
|
+
const registry_js_1 = require("../adapter/registry.js");
|
|
8
|
+
const index_js_1 = require("../capability/index.js");
|
|
9
|
+
const queries_js_1 = require("../store/queries.js");
|
|
10
|
+
async function runExtractionPipeline(filePaths, options) {
|
|
11
|
+
const adapterRegistry = options.adapterRegistry ?? (0, registry_js_1.getDefaultAdapterRegistry)();
|
|
12
|
+
const db = options.graphStore.getDatabase();
|
|
13
|
+
const warningMessages = [];
|
|
14
|
+
const sortedPaths = Array.from(new Set(filePaths)).sort((left, right) => left.localeCompare(right));
|
|
15
|
+
const capabilityRegistry = new index_js_1.GraphCapabilityRegistry(adapterRegistry.getAll().map((adapter) => adapter.languageId));
|
|
16
|
+
let succeededFiles = 0;
|
|
17
|
+
let failedFiles = 0;
|
|
18
|
+
let persistedNodes = 0;
|
|
19
|
+
let persistedSymbols = 0;
|
|
20
|
+
const fallbackFiles = [];
|
|
21
|
+
for (const filePath of sortedPaths) {
|
|
22
|
+
const ext = (0, node_path_1.extname)(filePath).toLowerCase();
|
|
23
|
+
const adapter = adapterRegistry.getForExtension(ext);
|
|
24
|
+
const languageId = adapter?.languageId ?? index_js_1.GraphCapabilityRegistry.unsupportedLanguageId(ext);
|
|
25
|
+
try {
|
|
26
|
+
if (!adapter) {
|
|
27
|
+
const fileId = makeDeterministicId("file", filePath);
|
|
28
|
+
persistUnsupportedFile(db, filePath, languageId, fileId);
|
|
29
|
+
fallbackFiles.push({ path: filePath, ext });
|
|
30
|
+
succeededFiles += 1;
|
|
31
|
+
persistedNodes += 1;
|
|
32
|
+
const warning = `Falling back to file node for unsupported file type: ${filePath}`;
|
|
33
|
+
warningMessages.push(warning);
|
|
34
|
+
capabilityRegistry.noteUnsupportedExtension(ext);
|
|
35
|
+
capabilityRegistry.recordFileLevel(languageId, [warning]);
|
|
36
|
+
options.logger?.warn(warning);
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
const source = (0, node_fs_1.readFileSync)(filePath, "utf-8");
|
|
40
|
+
const extracted = await adapter.extractSymbols(filePath, source);
|
|
41
|
+
const fileId = makeDeterministicId("file", filePath);
|
|
42
|
+
const symbols = extracted.symbols.map((entry, index) => toPersistableSymbol(fileId, filePath, entry, index));
|
|
43
|
+
persistFileSymbols(db, filePath, extracted.language, fileId, symbols);
|
|
44
|
+
succeededFiles += 1;
|
|
45
|
+
persistedNodes += symbols.length;
|
|
46
|
+
persistedSymbols += symbols.length;
|
|
47
|
+
capabilityRegistry.recordSymbolLevel(adapter.languageId, symbols.length);
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
failedFiles += 1;
|
|
51
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
52
|
+
const warning = `Extraction failed for ${filePath}: ${message}`;
|
|
53
|
+
warningMessages.push(warning);
|
|
54
|
+
if (!adapter) {
|
|
55
|
+
capabilityRegistry.noteUnsupportedExtension(ext);
|
|
56
|
+
}
|
|
57
|
+
capabilityRegistry.recordFailure(languageId, warning);
|
|
58
|
+
options.logger?.warn(warning);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return {
|
|
62
|
+
processedFiles: sortedPaths.length,
|
|
63
|
+
succeededFiles,
|
|
64
|
+
failedFiles,
|
|
65
|
+
persistedNodes,
|
|
66
|
+
persistedSymbols,
|
|
67
|
+
fallbackFiles,
|
|
68
|
+
warnings: warningMessages,
|
|
69
|
+
capability: capabilityRegistry.buildReport(),
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
function toPersistableSymbol(fileId, filePath, entry, index) {
|
|
73
|
+
const symbolIdSeed = [
|
|
74
|
+
filePath,
|
|
75
|
+
entry.kind,
|
|
76
|
+
entry.name,
|
|
77
|
+
entry.startLine,
|
|
78
|
+
entry.startColumn,
|
|
79
|
+
entry.endLine,
|
|
80
|
+
entry.endColumn,
|
|
81
|
+
index,
|
|
82
|
+
].join(":");
|
|
83
|
+
const symbolId = makeDeterministicId("symbol", symbolIdSeed);
|
|
84
|
+
const nodeId = makeDeterministicId("node", symbolIdSeed);
|
|
85
|
+
const node = {
|
|
86
|
+
id: nodeId,
|
|
87
|
+
type: mapKindToNodeType(entry.kind),
|
|
88
|
+
fileId,
|
|
89
|
+
name: entry.name,
|
|
90
|
+
startLine: entry.startLine,
|
|
91
|
+
startColumn: entry.startColumn,
|
|
92
|
+
endLine: entry.endLine,
|
|
93
|
+
endColumn: entry.endColumn,
|
|
94
|
+
};
|
|
95
|
+
const symbol = {
|
|
96
|
+
id: symbolId,
|
|
97
|
+
nodeId,
|
|
98
|
+
fileId,
|
|
99
|
+
name: entry.name,
|
|
100
|
+
kind: entry.kind,
|
|
101
|
+
signature: entry.signature,
|
|
102
|
+
exported: entry.exported,
|
|
103
|
+
};
|
|
104
|
+
return { node, symbol };
|
|
105
|
+
}
|
|
106
|
+
function persistFileSymbols(db, filePath, language, fileId, symbols) {
|
|
107
|
+
db.exec("BEGIN IMMEDIATE TRANSACTION");
|
|
108
|
+
try {
|
|
109
|
+
db.prepare(`
|
|
110
|
+
INSERT INTO files (id, path, language)
|
|
111
|
+
VALUES (?1, ?2, ?3)
|
|
112
|
+
ON CONFLICT(path) DO UPDATE SET
|
|
113
|
+
id = excluded.id,
|
|
114
|
+
language = excluded.language
|
|
115
|
+
`).run(fileId, filePath, normalizeLanguage(language, filePath));
|
|
116
|
+
db.prepare("DELETE FROM symbols WHERE file_id = ?1").run(fileId);
|
|
117
|
+
db.prepare("DELETE FROM nodes WHERE file_id = ?1").run(fileId);
|
|
118
|
+
for (const item of symbols) {
|
|
119
|
+
(0, queries_js_1.insertNode)(db, item.node);
|
|
120
|
+
db.prepare(`
|
|
121
|
+
INSERT INTO symbols (id, node_id, file_id, name, kind, signature, exported)
|
|
122
|
+
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)
|
|
123
|
+
ON CONFLICT(id) DO UPDATE SET
|
|
124
|
+
node_id = excluded.node_id,
|
|
125
|
+
file_id = excluded.file_id,
|
|
126
|
+
name = excluded.name,
|
|
127
|
+
kind = excluded.kind,
|
|
128
|
+
signature = excluded.signature,
|
|
129
|
+
exported = excluded.exported
|
|
130
|
+
`).run(item.symbol.id, item.symbol.nodeId, item.symbol.fileId, item.symbol.name, item.symbol.kind, item.symbol.signature ?? null, item.symbol.exported ? 1 : 0);
|
|
131
|
+
}
|
|
132
|
+
db.exec("COMMIT");
|
|
133
|
+
}
|
|
134
|
+
catch (error) {
|
|
135
|
+
db.exec("ROLLBACK");
|
|
136
|
+
throw error;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
function persistUnsupportedFile(db, filePath, language, fileId) {
|
|
140
|
+
db.exec("BEGIN IMMEDIATE TRANSACTION");
|
|
141
|
+
try {
|
|
142
|
+
db.prepare(`
|
|
143
|
+
INSERT INTO files (id, path, language)
|
|
144
|
+
VALUES (?1, ?2, ?3)
|
|
145
|
+
ON CONFLICT(path) DO UPDATE SET
|
|
146
|
+
id = excluded.id,
|
|
147
|
+
language = excluded.language
|
|
148
|
+
`).run(fileId, filePath, normalizeLanguage(language, filePath));
|
|
149
|
+
db.prepare("DELETE FROM symbols WHERE file_id = ?1").run(fileId);
|
|
150
|
+
db.prepare("DELETE FROM nodes WHERE file_id = ?1").run(fileId);
|
|
151
|
+
(0, queries_js_1.insertNode)(db, {
|
|
152
|
+
id: fileId,
|
|
153
|
+
type: "FILE",
|
|
154
|
+
fileId,
|
|
155
|
+
});
|
|
156
|
+
db.exec("COMMIT");
|
|
157
|
+
}
|
|
158
|
+
catch (error) {
|
|
159
|
+
db.exec("ROLLBACK");
|
|
160
|
+
throw error;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
function normalizeLanguage(language, filePath) {
|
|
164
|
+
const extension = (0, node_path_1.extname)(filePath).toLowerCase();
|
|
165
|
+
if (language === "typescript" || extension === ".ts" || extension === ".tsx") {
|
|
166
|
+
return "ts";
|
|
167
|
+
}
|
|
168
|
+
if (language === "javascript" ||
|
|
169
|
+
extension === ".js" ||
|
|
170
|
+
extension === ".jsx" ||
|
|
171
|
+
extension === ".mjs" ||
|
|
172
|
+
extension === ".cjs") {
|
|
173
|
+
return "js";
|
|
174
|
+
}
|
|
175
|
+
return language;
|
|
176
|
+
}
|
|
177
|
+
function mapKindToNodeType(kind) {
|
|
178
|
+
switch (kind) {
|
|
179
|
+
case "function":
|
|
180
|
+
return "FUNCTION";
|
|
181
|
+
case "class":
|
|
182
|
+
return "CLASS";
|
|
183
|
+
case "method":
|
|
184
|
+
return "METHOD";
|
|
185
|
+
case "import":
|
|
186
|
+
return "IMPORT";
|
|
187
|
+
default:
|
|
188
|
+
return "SYMBOL";
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
function makeDeterministicId(prefix, input) {
|
|
192
|
+
const digest = (0, node_crypto_1.createHash)("sha256").update(input).digest("hex").slice(0, 24);
|
|
193
|
+
return `${prefix}-${digest}`;
|
|
194
|
+
}
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.configureGraphQuery = configureGraphQuery;
|
|
4
|
+
exports.lookupSymbol = lookupSymbol;
|
|
5
|
+
exports.getCallers = getCallers;
|
|
6
|
+
exports.getCallees = getCallees;
|
|
7
|
+
exports.getImpactedFiles = getImpactedFiles;
|
|
8
|
+
exports.getGraphStats = getGraphStats;
|
|
9
|
+
const types_js_1 = require("./types.js");
|
|
10
|
+
const FILE_NODE_PREFIX = "node-file-";
|
|
11
|
+
const queryState = {
|
|
12
|
+
defaultStore: null,
|
|
13
|
+
overrideStore: null,
|
|
14
|
+
};
|
|
15
|
+
function configureGraphQuery(options) {
|
|
16
|
+
queryState.overrideStore = options.graphStore ?? null;
|
|
17
|
+
}
|
|
18
|
+
function lookupSymbol(name, file) {
|
|
19
|
+
const db = getDatabase();
|
|
20
|
+
const row = db
|
|
21
|
+
.prepare(`
|
|
22
|
+
SELECT
|
|
23
|
+
s.id,
|
|
24
|
+
s.node_id AS nodeId,
|
|
25
|
+
s.name,
|
|
26
|
+
s.kind,
|
|
27
|
+
s.signature,
|
|
28
|
+
s.exported,
|
|
29
|
+
s.file_id AS fileId,
|
|
30
|
+
f.path AS filePath
|
|
31
|
+
FROM symbols s
|
|
32
|
+
JOIN files f ON f.id = s.file_id
|
|
33
|
+
WHERE s.name = ?1
|
|
34
|
+
AND (?2 IS NULL OR f.path = ?2)
|
|
35
|
+
ORDER BY s.id
|
|
36
|
+
LIMIT 1
|
|
37
|
+
`)
|
|
38
|
+
.get(name, file ?? null);
|
|
39
|
+
if (!row) {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
return toGraphSymbol(row);
|
|
43
|
+
}
|
|
44
|
+
function getCallers(symbolId) {
|
|
45
|
+
const db = getDatabase();
|
|
46
|
+
return lookupRelatedSymbols(db, symbolId, "CALLS", "to_node_id", "from_node_id");
|
|
47
|
+
}
|
|
48
|
+
function getCallees(symbolId) {
|
|
49
|
+
const db = getDatabase();
|
|
50
|
+
return lookupRelatedSymbols(db, symbolId, "CALLS", "from_node_id", "to_node_id");
|
|
51
|
+
}
|
|
52
|
+
function getImpactedFiles(symbolId) {
|
|
53
|
+
const db = getDatabase();
|
|
54
|
+
const root = lookupSymbolRowById(db, symbolId);
|
|
55
|
+
if (!root) {
|
|
56
|
+
return [];
|
|
57
|
+
}
|
|
58
|
+
const impactedFiles = new Map();
|
|
59
|
+
const visitedSymbolNodes = new Set();
|
|
60
|
+
const visitedFileNodes = new Set();
|
|
61
|
+
const queue = [];
|
|
62
|
+
addImpactedFile(impactedFiles, {
|
|
63
|
+
fileId: root.fileId,
|
|
64
|
+
path: root.filePath,
|
|
65
|
+
language: lookupLanguageByFileId(db, root.fileId),
|
|
66
|
+
});
|
|
67
|
+
queue.push({ kind: "symbol", nodeId: root.nodeId, depth: 0 });
|
|
68
|
+
while (queue.length > 0) {
|
|
69
|
+
const current = queue.shift();
|
|
70
|
+
if (!current) {
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
if (current.depth > types_js_1.DEFAULT_IMPACT_MAX_DEPTH) {
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
if (current.kind === "symbol") {
|
|
77
|
+
if (visitedSymbolNodes.has(current.nodeId)) {
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
visitedSymbolNodes.add(current.nodeId);
|
|
81
|
+
const symbolFile = lookupFileBySymbolNodeId(db, current.nodeId);
|
|
82
|
+
if (symbolFile) {
|
|
83
|
+
addImpactedFile(impactedFiles, symbolFile);
|
|
84
|
+
queue.push({
|
|
85
|
+
kind: "file",
|
|
86
|
+
nodeId: `${FILE_NODE_PREFIX}${symbolFile.fileId}`,
|
|
87
|
+
depth: current.depth + 1,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
const outgoingCalls = db
|
|
91
|
+
.prepare(`
|
|
92
|
+
SELECT e.to_node_id AS nodeId
|
|
93
|
+
FROM edges e
|
|
94
|
+
WHERE e.type = 'CALLS'
|
|
95
|
+
AND e.from_node_id = ?1
|
|
96
|
+
ORDER BY e.to_node_id
|
|
97
|
+
`)
|
|
98
|
+
.all(current.nodeId);
|
|
99
|
+
for (const edge of outgoingCalls) {
|
|
100
|
+
queue.push({ kind: "symbol", nodeId: edge.nodeId, depth: current.depth + 1 });
|
|
101
|
+
}
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
if (visitedFileNodes.has(current.nodeId)) {
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
visitedFileNodes.add(current.nodeId);
|
|
108
|
+
const fileByNode = lookupFileByFileNodeId(db, current.nodeId);
|
|
109
|
+
if (fileByNode) {
|
|
110
|
+
addImpactedFile(impactedFiles, fileByNode);
|
|
111
|
+
}
|
|
112
|
+
const importedFiles = db
|
|
113
|
+
.prepare(`
|
|
114
|
+
SELECT e.to_node_id AS fileNodeId
|
|
115
|
+
FROM edges e
|
|
116
|
+
WHERE e.type = 'IMPORTS'
|
|
117
|
+
AND e.from_node_id = ?1
|
|
118
|
+
ORDER BY e.to_node_id
|
|
119
|
+
`)
|
|
120
|
+
.all(current.nodeId);
|
|
121
|
+
for (const imported of importedFiles) {
|
|
122
|
+
const importedFile = lookupFileByFileNodeId(db, imported.fileNodeId);
|
|
123
|
+
if (importedFile) {
|
|
124
|
+
addImpactedFile(impactedFiles, importedFile);
|
|
125
|
+
}
|
|
126
|
+
queue.push({ kind: "file", nodeId: imported.fileNodeId, depth: current.depth + 1 });
|
|
127
|
+
}
|
|
128
|
+
const symbolsInFile = db
|
|
129
|
+
.prepare(`
|
|
130
|
+
SELECT e.from_node_id AS symbolNodeId
|
|
131
|
+
FROM edges e
|
|
132
|
+
WHERE e.type = 'DEFINED_IN'
|
|
133
|
+
AND e.to_node_id = ?1
|
|
134
|
+
ORDER BY e.from_node_id
|
|
135
|
+
`)
|
|
136
|
+
.all(current.nodeId);
|
|
137
|
+
for (const symbol of symbolsInFile) {
|
|
138
|
+
queue.push({ kind: "symbol", nodeId: symbol.symbolNodeId, depth: current.depth + 1 });
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return Array.from(impactedFiles.values()).sort((left, right) => left.path.localeCompare(right.path));
|
|
142
|
+
}
|
|
143
|
+
function getGraphStats() {
|
|
144
|
+
const db = getDatabase();
|
|
145
|
+
const row = db
|
|
146
|
+
.prepare(`
|
|
147
|
+
SELECT
|
|
148
|
+
(SELECT COUNT(*) FROM nodes) AS nodeCount,
|
|
149
|
+
(SELECT COUNT(*) FROM edges) AS edgeCount,
|
|
150
|
+
(SELECT COUNT(*) FROM files) AS fileCount,
|
|
151
|
+
(SELECT COUNT(*) FROM symbols) AS symbolCount
|
|
152
|
+
`)
|
|
153
|
+
.get();
|
|
154
|
+
return {
|
|
155
|
+
version: types_js_1.GRAPH_QUERY_RESPONSE_VERSION,
|
|
156
|
+
nodeCount: row.nodeCount,
|
|
157
|
+
edgeCount: row.edgeCount,
|
|
158
|
+
fileCount: row.fileCount,
|
|
159
|
+
symbolCount: row.symbolCount,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
function getDatabase() {
|
|
163
|
+
const activeStore = queryState.overrideStore ?? getDefaultStore();
|
|
164
|
+
return activeStore.getDatabase();
|
|
165
|
+
}
|
|
166
|
+
function getDefaultStore() {
|
|
167
|
+
if (queryState.defaultStore) {
|
|
168
|
+
return queryState.defaultStore;
|
|
169
|
+
}
|
|
170
|
+
throw new Error("No graph store configured. Call configureGraphQuery() before using the query API.");
|
|
171
|
+
}
|
|
172
|
+
function lookupRelatedSymbols(db, symbolId, edgeType, sourceColumn, targetColumn) {
|
|
173
|
+
const source = lookupSymbolRowById(db, symbolId);
|
|
174
|
+
if (!source) {
|
|
175
|
+
return [];
|
|
176
|
+
}
|
|
177
|
+
const rows = db
|
|
178
|
+
.prepare(`
|
|
179
|
+
SELECT DISTINCT
|
|
180
|
+
s.id,
|
|
181
|
+
s.node_id AS nodeId,
|
|
182
|
+
s.name,
|
|
183
|
+
s.kind,
|
|
184
|
+
s.signature,
|
|
185
|
+
s.exported,
|
|
186
|
+
s.file_id AS fileId,
|
|
187
|
+
f.path AS filePath
|
|
188
|
+
FROM edges e
|
|
189
|
+
JOIN symbols s ON s.node_id = e.${targetColumn}
|
|
190
|
+
JOIN files f ON f.id = s.file_id
|
|
191
|
+
WHERE e.type = ?1
|
|
192
|
+
AND e.${sourceColumn} = ?2
|
|
193
|
+
ORDER BY s.id
|
|
194
|
+
`)
|
|
195
|
+
.all(edgeType, source.nodeId);
|
|
196
|
+
return rows.map((row) => toGraphSymbol(row));
|
|
197
|
+
}
|
|
198
|
+
function lookupSymbolRowById(db, symbolId) {
|
|
199
|
+
const row = db
|
|
200
|
+
.prepare(`
|
|
201
|
+
SELECT
|
|
202
|
+
s.id,
|
|
203
|
+
s.node_id AS nodeId,
|
|
204
|
+
s.name,
|
|
205
|
+
s.kind,
|
|
206
|
+
s.signature,
|
|
207
|
+
s.exported,
|
|
208
|
+
s.file_id AS fileId,
|
|
209
|
+
f.path AS filePath
|
|
210
|
+
FROM symbols s
|
|
211
|
+
JOIN files f ON f.id = s.file_id
|
|
212
|
+
WHERE s.id = ?1
|
|
213
|
+
LIMIT 1
|
|
214
|
+
`)
|
|
215
|
+
.get(symbolId);
|
|
216
|
+
return row ?? null;
|
|
217
|
+
}
|
|
218
|
+
function lookupFileBySymbolNodeId(db, symbolNodeId) {
|
|
219
|
+
const row = db
|
|
220
|
+
.prepare(`
|
|
221
|
+
SELECT DISTINCT
|
|
222
|
+
f.id AS fileId,
|
|
223
|
+
f.path,
|
|
224
|
+
f.language
|
|
225
|
+
FROM symbols s
|
|
226
|
+
JOIN files f ON f.id = s.file_id
|
|
227
|
+
WHERE s.node_id = ?1
|
|
228
|
+
LIMIT 1
|
|
229
|
+
`)
|
|
230
|
+
.get(symbolNodeId);
|
|
231
|
+
return row ?? null;
|
|
232
|
+
}
|
|
233
|
+
function lookupFileByFileNodeId(db, fileNodeId) {
|
|
234
|
+
const row = db
|
|
235
|
+
.prepare(`
|
|
236
|
+
SELECT
|
|
237
|
+
f.id AS fileId,
|
|
238
|
+
f.path,
|
|
239
|
+
f.language
|
|
240
|
+
FROM nodes n
|
|
241
|
+
JOIN files f ON f.id = n.file_id
|
|
242
|
+
WHERE n.id = ?1
|
|
243
|
+
AND n.type = 'FILE'
|
|
244
|
+
LIMIT 1
|
|
245
|
+
`)
|
|
246
|
+
.get(fileNodeId);
|
|
247
|
+
return row ?? null;
|
|
248
|
+
}
|
|
249
|
+
function lookupLanguageByFileId(db, fileId) {
|
|
250
|
+
const row = db
|
|
251
|
+
.prepare(`
|
|
252
|
+
SELECT language
|
|
253
|
+
FROM files
|
|
254
|
+
WHERE id = ?1
|
|
255
|
+
LIMIT 1
|
|
256
|
+
`)
|
|
257
|
+
.get(fileId);
|
|
258
|
+
return row?.language ?? "unknown";
|
|
259
|
+
}
|
|
260
|
+
function addImpactedFile(target, file) {
|
|
261
|
+
target.set(file.path, {
|
|
262
|
+
version: types_js_1.GRAPH_QUERY_RESPONSE_VERSION,
|
|
263
|
+
path: file.path,
|
|
264
|
+
language: file.language,
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
function toGraphSymbol(row) {
|
|
268
|
+
return {
|
|
269
|
+
version: types_js_1.GRAPH_QUERY_RESPONSE_VERSION,
|
|
270
|
+
id: row.id,
|
|
271
|
+
name: row.name,
|
|
272
|
+
kind: row.kind,
|
|
273
|
+
signature: row.signature,
|
|
274
|
+
exported: row.exported === 1,
|
|
275
|
+
filePath: row.filePath,
|
|
276
|
+
};
|
|
277
|
+
}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.buildEdges = buildEdges;
|
|
4
|
+
const node_crypto_1 = require("node:crypto");
|
|
5
|
+
const queries_js_1 = require("../store/queries.js");
|
|
6
|
+
const FILE_NODE_PREFIX = "node-file-";
|
|
7
|
+
const UNRESOLVED_NODE_PREFIX = "node-unresolved-call-";
|
|
8
|
+
const UNRESOLVED_SYMBOL_PREFIX = "symbol-unresolved-call-";
|
|
9
|
+
const UNRESOLVED_SIGNATURE = "__UNRESOLVED__";
|
|
10
|
+
function buildEdges(db, resolvedImports) {
|
|
11
|
+
const files = db
|
|
12
|
+
.prepare(`
|
|
13
|
+
SELECT id, path
|
|
14
|
+
FROM files
|
|
15
|
+
`)
|
|
16
|
+
.all();
|
|
17
|
+
for (const file of files) {
|
|
18
|
+
upsertFileNode(db, file.id, file.path);
|
|
19
|
+
}
|
|
20
|
+
const symbols = db
|
|
21
|
+
.prepare(`
|
|
22
|
+
SELECT id, node_id AS nodeId, file_id AS fileId, name, kind, signature
|
|
23
|
+
FROM symbols
|
|
24
|
+
`)
|
|
25
|
+
.all();
|
|
26
|
+
const symbolById = new Map();
|
|
27
|
+
const symbolsByName = new Map();
|
|
28
|
+
for (const symbol of symbols) {
|
|
29
|
+
symbolById.set(symbol.id, symbol);
|
|
30
|
+
if (symbol.kind === "import") {
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
const existing = symbolsByName.get(symbol.name) ?? [];
|
|
34
|
+
existing.push(symbol);
|
|
35
|
+
symbolsByName.set(symbol.name, existing);
|
|
36
|
+
}
|
|
37
|
+
for (const list of symbolsByName.values()) {
|
|
38
|
+
list.sort((left, right) => left.id.localeCompare(right.id));
|
|
39
|
+
}
|
|
40
|
+
let importsEdges = 0;
|
|
41
|
+
let unresolvedImports = 0;
|
|
42
|
+
for (const resolvedImport of resolvedImports) {
|
|
43
|
+
const fromFileNodeId = toFileNodeId(resolvedImport.importerFileId);
|
|
44
|
+
const targetFileNodeId = toFileNodeId(resolvedImport.resolvedFileId);
|
|
45
|
+
const importEdge = {
|
|
46
|
+
id: makeEdgeId("imports", fromFileNodeId, targetFileNodeId, resolvedImport.importSpecifier),
|
|
47
|
+
type: "IMPORTS",
|
|
48
|
+
fromNodeId: fromFileNodeId,
|
|
49
|
+
toNodeId: targetFileNodeId,
|
|
50
|
+
metadata: JSON.stringify({
|
|
51
|
+
importSpecifier: resolvedImport.importSpecifier,
|
|
52
|
+
resolvedSymbolIds: resolvedImport.resolvedSymbolIds,
|
|
53
|
+
UNRESOLVED: resolvedImport.unresolved,
|
|
54
|
+
}),
|
|
55
|
+
};
|
|
56
|
+
(0, queries_js_1.insertEdge)(db, importEdge);
|
|
57
|
+
importsEdges += 1;
|
|
58
|
+
if (resolvedImport.unresolved) {
|
|
59
|
+
unresolvedImports += 1;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
let definedInEdges = 0;
|
|
63
|
+
for (const symbol of symbols) {
|
|
64
|
+
const edge = {
|
|
65
|
+
id: makeEdgeId("defined-in", symbol.nodeId, toFileNodeId(symbol.fileId), symbol.id),
|
|
66
|
+
type: "DEFINED_IN",
|
|
67
|
+
fromNodeId: symbol.nodeId,
|
|
68
|
+
toNodeId: toFileNodeId(symbol.fileId),
|
|
69
|
+
metadata: null,
|
|
70
|
+
};
|
|
71
|
+
(0, queries_js_1.insertEdge)(db, edge);
|
|
72
|
+
definedInEdges += 1;
|
|
73
|
+
}
|
|
74
|
+
let callsEdges = 0;
|
|
75
|
+
let unresolvedCalls = 0;
|
|
76
|
+
for (const symbol of symbols) {
|
|
77
|
+
if ((symbol.kind !== "function" && symbol.kind !== "method") || !symbol.signature) {
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
const callees = extractCallNames(symbol.signature);
|
|
81
|
+
for (const calleeName of callees) {
|
|
82
|
+
if (calleeName === symbol.name) {
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
const resolvedCallee = resolveCalleeSymbol(calleeName, symbol.fileId, symbolsByName);
|
|
86
|
+
if (resolvedCallee) {
|
|
87
|
+
const edge = {
|
|
88
|
+
id: makeEdgeId("calls", symbol.nodeId, resolvedCallee.nodeId, calleeName),
|
|
89
|
+
type: "CALLS",
|
|
90
|
+
fromNodeId: symbol.nodeId,
|
|
91
|
+
toNodeId: resolvedCallee.nodeId,
|
|
92
|
+
metadata: JSON.stringify({
|
|
93
|
+
calleeName,
|
|
94
|
+
UNRESOLVED: false,
|
|
95
|
+
}),
|
|
96
|
+
};
|
|
97
|
+
(0, queries_js_1.insertEdge)(db, edge);
|
|
98
|
+
callsEdges += 1;
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
const unresolved = upsertUnresolvedCallStub(db, symbol.fileId, calleeName);
|
|
102
|
+
const unresolvedDefinedIn = {
|
|
103
|
+
id: makeEdgeId("defined-in", unresolved.nodeId, toFileNodeId(symbol.fileId), unresolved.nodeId),
|
|
104
|
+
type: "DEFINED_IN",
|
|
105
|
+
fromNodeId: unresolved.nodeId,
|
|
106
|
+
toNodeId: toFileNodeId(symbol.fileId),
|
|
107
|
+
metadata: JSON.stringify({
|
|
108
|
+
UNRESOLVED: true,
|
|
109
|
+
}),
|
|
110
|
+
};
|
|
111
|
+
(0, queries_js_1.insertEdge)(db, unresolvedDefinedIn);
|
|
112
|
+
definedInEdges += 1;
|
|
113
|
+
const edge = {
|
|
114
|
+
id: makeEdgeId("calls", symbol.nodeId, unresolved.nodeId, calleeName),
|
|
115
|
+
type: "CALLS",
|
|
116
|
+
fromNodeId: symbol.nodeId,
|
|
117
|
+
toNodeId: unresolved.nodeId,
|
|
118
|
+
metadata: JSON.stringify({
|
|
119
|
+
calleeName,
|
|
120
|
+
UNRESOLVED: true,
|
|
121
|
+
}),
|
|
122
|
+
};
|
|
123
|
+
(0, queries_js_1.insertEdge)(db, edge);
|
|
124
|
+
callsEdges += 1;
|
|
125
|
+
unresolvedCalls += 1;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return {
|
|
129
|
+
callsEdges,
|
|
130
|
+
importsEdges,
|
|
131
|
+
definedInEdges,
|
|
132
|
+
unresolvedImports,
|
|
133
|
+
unresolvedCalls,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
function upsertFileNode(db, fileId, filePath) {
|
|
137
|
+
const node = {
|
|
138
|
+
id: toFileNodeId(fileId),
|
|
139
|
+
type: "FILE",
|
|
140
|
+
fileId,
|
|
141
|
+
name: filePath,
|
|
142
|
+
};
|
|
143
|
+
(0, queries_js_1.insertNode)(db, node);
|
|
144
|
+
}
|
|
145
|
+
function upsertUnresolvedCallStub(db, callerFileId, calleeName) {
|
|
146
|
+
const digest = makeDigest(`${callerFileId}:${calleeName}`);
|
|
147
|
+
const nodeId = `${UNRESOLVED_NODE_PREFIX}${digest}`;
|
|
148
|
+
const symbolId = `${UNRESOLVED_SYMBOL_PREFIX}${digest}`;
|
|
149
|
+
const node = {
|
|
150
|
+
id: nodeId,
|
|
151
|
+
type: "SYMBOL",
|
|
152
|
+
fileId: callerFileId,
|
|
153
|
+
name: `UNRESOLVED:${calleeName}`,
|
|
154
|
+
};
|
|
155
|
+
(0, queries_js_1.insertNode)(db, node);
|
|
156
|
+
db.prepare(`
|
|
157
|
+
INSERT INTO symbols (id, node_id, file_id, name, kind, signature, exported)
|
|
158
|
+
VALUES (?1, ?2, ?3, ?4, ?5, ?6, 0)
|
|
159
|
+
ON CONFLICT(id) DO UPDATE SET
|
|
160
|
+
node_id = excluded.node_id,
|
|
161
|
+
file_id = excluded.file_id,
|
|
162
|
+
name = excluded.name,
|
|
163
|
+
kind = excluded.kind,
|
|
164
|
+
signature = excluded.signature,
|
|
165
|
+
exported = excluded.exported
|
|
166
|
+
`).run(symbolId, nodeId, callerFileId, calleeName, "unknown", UNRESOLVED_SIGNATURE);
|
|
167
|
+
return { nodeId };
|
|
168
|
+
}
|
|
169
|
+
function resolveCalleeSymbol(calleeName, callerFileId, symbolsByName) {
|
|
170
|
+
const candidates = symbolsByName.get(calleeName);
|
|
171
|
+
if (!candidates || candidates.length === 0) {
|
|
172
|
+
return null;
|
|
173
|
+
}
|
|
174
|
+
const local = candidates.find((entry) => entry.fileId === callerFileId);
|
|
175
|
+
return local ?? candidates[0] ?? null;
|
|
176
|
+
}
|
|
177
|
+
function extractCallNames(signature) {
|
|
178
|
+
const matches = new Set();
|
|
179
|
+
for (const match of signature.matchAll(/\b([A-Za-z_$][A-Za-z0-9_$]*)\s*\(/g)) {
|
|
180
|
+
const name = match[1];
|
|
181
|
+
if (!name || RESERVED_CALL_TOKENS.has(name)) {
|
|
182
|
+
continue;
|
|
183
|
+
}
|
|
184
|
+
matches.add(name);
|
|
185
|
+
}
|
|
186
|
+
return Array.from(matches.values()).sort((left, right) => left.localeCompare(right));
|
|
187
|
+
}
|
|
188
|
+
function makeEdgeId(prefix, fromNodeId, toNodeId, suffix) {
|
|
189
|
+
return `${prefix}-${makeDigest(`${fromNodeId}:${toNodeId}:${suffix}`)}`;
|
|
190
|
+
}
|
|
191
|
+
function toFileNodeId(fileId) {
|
|
192
|
+
return `${FILE_NODE_PREFIX}${fileId}`;
|
|
193
|
+
}
|
|
194
|
+
function makeDigest(input) {
|
|
195
|
+
return (0, node_crypto_1.createHash)("sha256").update(input).digest("hex").slice(0, 24);
|
|
196
|
+
}
|
|
197
|
+
const RESERVED_CALL_TOKENS = new Set([
|
|
198
|
+
"if",
|
|
199
|
+
"for",
|
|
200
|
+
"while",
|
|
201
|
+
"switch",
|
|
202
|
+
"catch",
|
|
203
|
+
"function",
|
|
204
|
+
"return",
|
|
205
|
+
"new",
|
|
206
|
+
"typeof",
|
|
207
|
+
"await",
|
|
208
|
+
"import",
|
|
209
|
+
"super",
|
|
210
|
+
"console",
|
|
211
|
+
]);
|