@optave/codegraph 3.10.0 → 3.11.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +40 -33
- package/dist/ast-analysis/engine.d.ts.map +1 -1
- package/dist/ast-analysis/engine.js +91 -60
- package/dist/ast-analysis/engine.js.map +1 -1
- package/dist/ast-analysis/rules/index.d.ts.map +1 -1
- package/dist/ast-analysis/rules/index.js +77 -0
- package/dist/ast-analysis/rules/index.js.map +1 -1
- package/dist/ast-analysis/visitor-utils.d.ts +3 -0
- package/dist/ast-analysis/visitor-utils.d.ts.map +1 -1
- package/dist/ast-analysis/visitor-utils.js +83 -49
- package/dist/ast-analysis/visitor-utils.js.map +1 -1
- package/dist/ast-analysis/visitors/ast-store-visitor.d.ts.map +1 -1
- package/dist/ast-analysis/visitors/ast-store-visitor.js +78 -62
- package/dist/ast-analysis/visitors/ast-store-visitor.js.map +1 -1
- package/dist/ast-analysis/visitors/dataflow-visitor.d.ts.map +1 -1
- package/dist/ast-analysis/visitors/dataflow-visitor.js +61 -42
- package/dist/ast-analysis/visitors/dataflow-visitor.js.map +1 -1
- package/dist/cli/commands/audit.js +1 -1
- package/dist/cli/commands/audit.js.map +1 -1
- package/dist/cli/commands/build.d.ts.map +1 -1
- package/dist/cli/commands/build.js +2 -0
- package/dist/cli/commands/build.js.map +1 -1
- package/dist/cli/commands/check.js +1 -1
- package/dist/cli/commands/check.js.map +1 -1
- package/dist/cli/commands/children.js +1 -1
- package/dist/cli/commands/children.js.map +1 -1
- package/dist/cli/commands/diff-impact.js +1 -1
- package/dist/cli/commands/diff-impact.js.map +1 -1
- package/dist/cli/commands/embed.d.ts.map +1 -1
- package/dist/cli/commands/embed.js +49 -4
- package/dist/cli/commands/embed.js.map +1 -1
- package/dist/cli/commands/roles.js +1 -1
- package/dist/cli/commands/roles.js.map +1 -1
- package/dist/cli/commands/structure.js +1 -1
- package/dist/cli/commands/structure.js.map +1 -1
- package/dist/cli/shared/options.js +1 -1
- package/dist/cli/shared/options.js.map +1 -1
- package/dist/db/connection.d.ts.map +1 -1
- package/dist/db/connection.js +8 -0
- package/dist/db/connection.js.map +1 -1
- package/dist/domain/analysis/dependencies.d.ts.map +1 -1
- package/dist/domain/analysis/dependencies.js +106 -80
- package/dist/domain/analysis/dependencies.js.map +1 -1
- package/dist/domain/analysis/fn-impact.d.ts.map +1 -1
- package/dist/domain/analysis/fn-impact.js +77 -52
- package/dist/domain/analysis/fn-impact.js.map +1 -1
- package/dist/domain/analysis/module-map.d.ts.map +1 -1
- package/dist/domain/analysis/module-map.js +132 -121
- package/dist/domain/analysis/module-map.js.map +1 -1
- package/dist/domain/graph/builder/helpers.d.ts +4 -4
- package/dist/domain/graph/builder/helpers.d.ts.map +1 -1
- package/dist/domain/graph/builder/helpers.js +47 -33
- package/dist/domain/graph/builder/helpers.js.map +1 -1
- package/dist/domain/graph/builder/incremental.d.ts +6 -6
- package/dist/domain/graph/builder/incremental.d.ts.map +1 -1
- package/dist/domain/graph/builder/incremental.js +148 -99
- package/dist/domain/graph/builder/incremental.js.map +1 -1
- package/dist/domain/graph/builder/pipeline.d.ts +1 -0
- package/dist/domain/graph/builder/pipeline.d.ts.map +1 -1
- package/dist/domain/graph/builder/pipeline.js +23 -637
- package/dist/domain/graph/builder/pipeline.js.map +1 -1
- package/dist/domain/graph/builder/stages/build-edges.d.ts.map +1 -1
- package/dist/domain/graph/builder/stages/build-edges.js +141 -98
- package/dist/domain/graph/builder/stages/build-edges.js.map +1 -1
- package/dist/domain/graph/builder/stages/build-structure.d.ts.map +1 -1
- package/dist/domain/graph/builder/stages/build-structure.js +82 -65
- package/dist/domain/graph/builder/stages/build-structure.js.map +1 -1
- package/dist/domain/graph/builder/stages/detect-changes.d.ts.map +1 -1
- package/dist/domain/graph/builder/stages/detect-changes.js +84 -56
- package/dist/domain/graph/builder/stages/detect-changes.js.map +1 -1
- package/dist/domain/graph/builder/stages/finalize.d.ts.map +1 -1
- package/dist/domain/graph/builder/stages/finalize.js +60 -51
- package/dist/domain/graph/builder/stages/finalize.js.map +1 -1
- package/dist/domain/graph/builder/stages/insert-nodes.d.ts +8 -6
- package/dist/domain/graph/builder/stages/insert-nodes.d.ts.map +1 -1
- package/dist/domain/graph/builder/stages/insert-nodes.js +107 -122
- package/dist/domain/graph/builder/stages/insert-nodes.js.map +1 -1
- package/dist/domain/graph/builder/stages/native-db-lifecycle.d.ts +14 -0
- package/dist/domain/graph/builder/stages/native-db-lifecycle.d.ts.map +1 -0
- package/dist/domain/graph/builder/stages/native-db-lifecycle.js +77 -0
- package/dist/domain/graph/builder/stages/native-db-lifecycle.js.map +1 -0
- package/dist/domain/graph/builder/stages/native-orchestrator.d.ts +62 -0
- package/dist/domain/graph/builder/stages/native-orchestrator.d.ts.map +1 -0
- package/dist/domain/graph/builder/stages/native-orchestrator.js +747 -0
- package/dist/domain/graph/builder/stages/native-orchestrator.js.map +1 -0
- package/dist/domain/graph/builder/stages/resolve-imports.d.ts.map +1 -1
- package/dist/domain/graph/builder/stages/resolve-imports.js +73 -22
- package/dist/domain/graph/builder/stages/resolve-imports.js.map +1 -1
- package/dist/domain/graph/cycles.d.ts +6 -4
- package/dist/domain/graph/cycles.d.ts.map +1 -1
- package/dist/domain/graph/cycles.js +50 -55
- package/dist/domain/graph/cycles.js.map +1 -1
- package/dist/domain/graph/journal.d.ts.map +1 -1
- package/dist/domain/graph/journal.js +89 -70
- package/dist/domain/graph/journal.js.map +1 -1
- package/dist/domain/graph/watcher.d.ts.map +1 -1
- package/dist/domain/graph/watcher.js +28 -20
- package/dist/domain/graph/watcher.js.map +1 -1
- package/dist/domain/parser.d.ts +12 -23
- package/dist/domain/parser.d.ts.map +1 -1
- package/dist/domain/parser.js +153 -80
- package/dist/domain/parser.js.map +1 -1
- package/dist/domain/search/generator.d.ts +3 -1
- package/dist/domain/search/generator.d.ts.map +1 -1
- package/dist/domain/search/generator.js +68 -45
- package/dist/domain/search/generator.js.map +1 -1
- package/dist/domain/search/models.d.ts +18 -0
- package/dist/domain/search/models.d.ts.map +1 -1
- package/dist/domain/search/models.js +72 -4
- package/dist/domain/search/models.js.map +1 -1
- package/dist/domain/search/search/hybrid.d.ts.map +1 -1
- package/dist/domain/search/search/hybrid.js +49 -40
- package/dist/domain/search/search/hybrid.js.map +1 -1
- package/dist/domain/search/search/semantic.d.ts.map +1 -1
- package/dist/domain/search/search/semantic.js +69 -49
- package/dist/domain/search/search/semantic.js.map +1 -1
- package/dist/domain/wasm-worker-entry.js +209 -137
- package/dist/domain/wasm-worker-entry.js.map +1 -1
- package/dist/extractors/c.js +25 -6
- package/dist/extractors/c.js.map +1 -1
- package/dist/extractors/cpp.js +47 -6
- package/dist/extractors/cpp.js.map +1 -1
- package/dist/extractors/cuda.js +90 -14
- package/dist/extractors/cuda.js.map +1 -1
- package/dist/extractors/elixir.js +108 -4
- package/dist/extractors/elixir.js.map +1 -1
- package/dist/extractors/erlang.js +56 -20
- package/dist/extractors/erlang.js.map +1 -1
- package/dist/extractors/fsharp.d.ts +7 -0
- package/dist/extractors/fsharp.d.ts.map +1 -1
- package/dist/extractors/fsharp.js +94 -0
- package/dist/extractors/fsharp.js.map +1 -1
- package/dist/extractors/gleam.d.ts.map +1 -1
- package/dist/extractors/gleam.js +29 -33
- package/dist/extractors/gleam.js.map +1 -1
- package/dist/extractors/groovy.js +41 -1
- package/dist/extractors/groovy.js.map +1 -1
- package/dist/extractors/haskell.js +48 -4
- package/dist/extractors/haskell.js.map +1 -1
- package/dist/extractors/helpers.d.ts +79 -1
- package/dist/extractors/helpers.d.ts.map +1 -1
- package/dist/extractors/helpers.js +137 -0
- package/dist/extractors/helpers.js.map +1 -1
- package/dist/extractors/java.d.ts.map +1 -1
- package/dist/extractors/java.js +37 -49
- package/dist/extractors/java.js.map +1 -1
- package/dist/extractors/javascript.d.ts.map +1 -1
- package/dist/extractors/javascript.js +44 -44
- package/dist/extractors/javascript.js.map +1 -1
- package/dist/extractors/julia.js +198 -74
- package/dist/extractors/julia.js.map +1 -1
- package/dist/extractors/kotlin.js +4 -0
- package/dist/extractors/kotlin.js.map +1 -1
- package/dist/extractors/objc.js +184 -47
- package/dist/extractors/objc.js.map +1 -1
- package/dist/extractors/python.js +7 -4
- package/dist/extractors/python.js.map +1 -1
- package/dist/extractors/r.d.ts.map +1 -1
- package/dist/extractors/r.js +103 -87
- package/dist/extractors/r.js.map +1 -1
- package/dist/extractors/scala.d.ts.map +1 -1
- package/dist/extractors/scala.js +18 -32
- package/dist/extractors/scala.js.map +1 -1
- package/dist/extractors/solidity.d.ts.map +1 -1
- package/dist/extractors/solidity.js +55 -69
- package/dist/extractors/solidity.js.map +1 -1
- package/dist/extractors/verilog.js +80 -15
- package/dist/extractors/verilog.js.map +1 -1
- package/dist/features/boundaries.d.ts.map +1 -1
- package/dist/features/boundaries.js +49 -39
- package/dist/features/boundaries.js.map +1 -1
- package/dist/features/cfg.d.ts.map +1 -1
- package/dist/features/cfg.js +90 -63
- package/dist/features/cfg.js.map +1 -1
- package/dist/features/check.d.ts.map +1 -1
- package/dist/features/check.js +43 -34
- package/dist/features/check.js.map +1 -1
- package/dist/features/cochange.d.ts.map +1 -1
- package/dist/features/cochange.js +68 -56
- package/dist/features/cochange.js.map +1 -1
- package/dist/features/complexity.d.ts.map +1 -1
- package/dist/features/complexity.js +105 -75
- package/dist/features/complexity.js.map +1 -1
- package/dist/features/dataflow.d.ts.map +1 -1
- package/dist/features/dataflow.js +37 -29
- package/dist/features/dataflow.js.map +1 -1
- package/dist/features/flow.d.ts.map +1 -1
- package/dist/features/flow.js +31 -22
- package/dist/features/flow.js.map +1 -1
- package/dist/features/graph-enrichment.d.ts.map +1 -1
- package/dist/features/graph-enrichment.js +77 -70
- package/dist/features/graph-enrichment.js.map +1 -1
- package/dist/features/owners.d.ts +17 -26
- package/dist/features/owners.d.ts.map +1 -1
- package/dist/features/owners.js +120 -109
- package/dist/features/owners.js.map +1 -1
- package/dist/features/sequence.d.ts.map +1 -1
- package/dist/features/sequence.js +59 -54
- package/dist/features/sequence.js.map +1 -1
- package/dist/features/structure-query.d.ts.map +1 -1
- package/dist/features/structure-query.js +60 -60
- package/dist/features/structure-query.js.map +1 -1
- package/dist/features/structure.js +28 -36
- package/dist/features/structure.js.map +1 -1
- package/dist/graph/algorithms/leiden/optimiser.d.ts.map +1 -1
- package/dist/graph/algorithms/leiden/optimiser.js +100 -69
- package/dist/graph/algorithms/leiden/optimiser.js.map +1 -1
- package/dist/graph/classifiers/roles.d.ts.map +1 -1
- package/dist/graph/classifiers/roles.js +63 -59
- package/dist/graph/classifiers/roles.js.map +1 -1
- package/dist/infrastructure/config.d.ts +1 -1
- package/dist/infrastructure/config.d.ts.map +1 -1
- package/dist/infrastructure/config.js +1 -1
- package/dist/infrastructure/config.js.map +1 -1
- package/dist/mcp/tool-registry.d.ts.map +1 -1
- package/dist/mcp/tool-registry.js +4 -0
- package/dist/mcp/tool-registry.js.map +1 -1
- package/dist/mcp/tools/semantic-search.d.ts +1 -0
- package/dist/mcp/tools/semantic-search.d.ts.map +1 -1
- package/dist/mcp/tools/semantic-search.js +1 -0
- package/dist/mcp/tools/semantic-search.js.map +1 -1
- package/dist/presentation/cfg.d.ts.map +1 -1
- package/dist/presentation/cfg.js +44 -29
- package/dist/presentation/cfg.js.map +1 -1
- package/dist/presentation/flow.d.ts.map +1 -1
- package/dist/presentation/flow.js +58 -38
- package/dist/presentation/flow.js.map +1 -1
- package/dist/types.d.ts +16 -2
- package/dist/types.d.ts.map +1 -1
- package/grammars/tree-sitter-erlang.wasm +0 -0
- package/grammars/tree-sitter-fsharp.wasm +0 -0
- package/grammars/tree-sitter-fsharp_signature.wasm +0 -0
- package/grammars/tree-sitter-gleam.wasm +0 -0
- package/package.json +10 -10
- package/src/ast-analysis/engine.ts +145 -61
- package/src/ast-analysis/rules/index.ts +87 -0
- package/src/ast-analysis/visitor-utils.ts +86 -46
- package/src/ast-analysis/visitors/ast-store-visitor.ts +104 -69
- package/src/ast-analysis/visitors/dataflow-visitor.ts +86 -47
- package/src/cli/commands/audit.ts +1 -1
- package/src/cli/commands/build.ts +2 -0
- package/src/cli/commands/check.ts +1 -1
- package/src/cli/commands/children.ts +1 -1
- package/src/cli/commands/diff-impact.ts +1 -1
- package/src/cli/commands/embed.ts +54 -4
- package/src/cli/commands/roles.ts +1 -1
- package/src/cli/commands/structure.ts +1 -1
- package/src/cli/shared/options.ts +1 -1
- package/src/db/connection.ts +8 -0
- package/src/domain/analysis/dependencies.ts +166 -85
- package/src/domain/analysis/fn-impact.ts +120 -50
- package/src/domain/analysis/module-map.ts +175 -140
- package/src/domain/graph/builder/helpers.ts +85 -76
- package/src/domain/graph/builder/incremental.ts +223 -131
- package/src/domain/graph/builder/pipeline.ts +32 -785
- package/src/domain/graph/builder/stages/build-edges.ts +207 -142
- package/src/domain/graph/builder/stages/build-structure.ts +115 -82
- package/src/domain/graph/builder/stages/detect-changes.ts +107 -64
- package/src/domain/graph/builder/stages/finalize.ts +72 -70
- package/src/domain/graph/builder/stages/insert-nodes.ts +154 -120
- package/src/domain/graph/builder/stages/native-db-lifecycle.ts +74 -0
- package/src/domain/graph/builder/stages/native-orchestrator.ts +942 -0
- package/src/domain/graph/builder/stages/resolve-imports.ts +79 -25
- package/src/domain/graph/cycles.ts +51 -49
- package/src/domain/graph/journal.ts +84 -69
- package/src/domain/graph/watcher.ts +29 -25
- package/src/domain/parser.ts +170 -67
- package/src/domain/search/generator.ts +132 -74
- package/src/domain/search/models.ts +75 -4
- package/src/domain/search/search/hybrid.ts +53 -42
- package/src/domain/search/search/semantic.ts +105 -65
- package/src/domain/wasm-worker-entry.ts +243 -153
- package/src/extractors/c.ts +27 -8
- package/src/extractors/cpp.ts +50 -8
- package/src/extractors/cuda.ts +90 -16
- package/src/extractors/elixir.ts +103 -4
- package/src/extractors/erlang.ts +63 -20
- package/src/extractors/fsharp.ts +104 -0
- package/src/extractors/gleam.ts +40 -39
- package/src/extractors/groovy.ts +45 -1
- package/src/extractors/haskell.ts +45 -4
- package/src/extractors/helpers.ts +205 -1
- package/src/extractors/java.ts +42 -45
- package/src/extractors/javascript.ts +44 -43
- package/src/extractors/julia.ts +191 -77
- package/src/extractors/kotlin.ts +4 -0
- package/src/extractors/objc.ts +171 -47
- package/src/extractors/python.ts +5 -3
- package/src/extractors/r.ts +104 -82
- package/src/extractors/scala.ts +24 -36
- package/src/extractors/solidity.ts +59 -78
- package/src/extractors/verilog.ts +83 -15
- package/src/features/boundaries.ts +64 -46
- package/src/features/cfg.ts +145 -74
- package/src/features/check.ts +60 -43
- package/src/features/cochange.ts +95 -72
- package/src/features/complexity.ts +134 -79
- package/src/features/dataflow.ts +57 -34
- package/src/features/flow.ts +48 -24
- package/src/features/graph-enrichment.ts +105 -70
- package/src/features/owners.ts +186 -146
- package/src/features/sequence.ts +99 -69
- package/src/features/structure-query.ts +94 -79
- package/src/features/structure.ts +56 -56
- package/src/graph/algorithms/leiden/optimiser.ts +142 -87
- package/src/graph/classifiers/roles.ts +64 -54
- package/src/infrastructure/config.ts +1 -1
- package/src/mcp/tool-registry.ts +5 -0
- package/src/mcp/tools/semantic-search.ts +2 -0
- package/src/presentation/cfg.ts +48 -32
- package/src/presentation/flow.ts +100 -52
- package/src/types.ts +16 -1
|
@@ -92,23 +92,69 @@ function marshalSymbolBatches(allSymbols: Map<string, ExtractorOutput>): InsertN
|
|
|
92
92
|
return batches;
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
+
/** A single file_hashes row. */
|
|
96
|
+
interface FileHashRecord {
|
|
97
|
+
file: string;
|
|
98
|
+
hash: string;
|
|
99
|
+
mtime: number;
|
|
100
|
+
size: number;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** Resolve the (hash, mtime, size) tuple for a relPath, reading from disk if needed. */
|
|
104
|
+
function resolveHashFromPrecomputed(
|
|
105
|
+
relPath: string,
|
|
106
|
+
precomputed: PrecomputedFileData,
|
|
107
|
+
rootDir: string,
|
|
108
|
+
caller: string,
|
|
109
|
+
): FileHashRecord | null {
|
|
110
|
+
if (precomputed.hash) {
|
|
111
|
+
let mtime: number;
|
|
112
|
+
let size: number;
|
|
113
|
+
if (precomputed.stat) {
|
|
114
|
+
mtime = precomputed.stat.mtime;
|
|
115
|
+
size = precomputed.stat.size;
|
|
116
|
+
} else {
|
|
117
|
+
const rawStat = fileStat(path.join(rootDir, relPath));
|
|
118
|
+
mtime = rawStat ? rawStat.mtime : 0;
|
|
119
|
+
size = rawStat ? rawStat.size : 0;
|
|
120
|
+
}
|
|
121
|
+
return { file: relPath, hash: precomputed.hash, mtime, size };
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const absPath = path.join(rootDir, relPath);
|
|
125
|
+
let code: string | null;
|
|
126
|
+
try {
|
|
127
|
+
code = readFileSafe(absPath);
|
|
128
|
+
} catch (e) {
|
|
129
|
+
debug(`${caller}: readFileSafe failed for ${relPath}: ${toErrorMessage(e)}`);
|
|
130
|
+
code = null;
|
|
131
|
+
}
|
|
132
|
+
if (code === null) return null;
|
|
133
|
+
const stat = fileStat(absPath);
|
|
134
|
+
return {
|
|
135
|
+
file: relPath,
|
|
136
|
+
hash: fileHash(code),
|
|
137
|
+
mtime: stat ? stat.mtime : 0,
|
|
138
|
+
size: stat ? stat.size : 0,
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
|
|
95
142
|
/**
|
|
96
|
-
*
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
* `file_hashes`, which permanently breaks the JS-side fast-skip pre-flight on
|
|
101
|
-
* any subsequent no-op rebuild (#1068).
|
|
143
|
+
* Walk every collected file once and yield a `FileHashRecord` for it, plus one
|
|
144
|
+
* record per metadata-only update. Shared by `buildFileHashes` (native path)
|
|
145
|
+
* and `updateFileHashes` (JS fallback) so the iteration and hash-resolution
|
|
146
|
+
* logic stays in one place.
|
|
102
147
|
*
|
|
103
|
-
*
|
|
148
|
+
* Files marked `_reverseDepOnly` are skipped — their hashes are already
|
|
149
|
+
* correct in the DB.
|
|
104
150
|
*/
|
|
105
|
-
|
|
151
|
+
function* iterFileHashRecords(
|
|
106
152
|
filesToParse: FileToParse[],
|
|
107
153
|
precomputedData: Map<string, PrecomputedFileData>,
|
|
108
154
|
metadataUpdates: MetadataUpdate[],
|
|
109
155
|
rootDir: string,
|
|
110
|
-
|
|
111
|
-
|
|
156
|
+
caller: string,
|
|
157
|
+
): Generator<FileHashRecord> {
|
|
112
158
|
const seen = new Set<string>();
|
|
113
159
|
|
|
114
160
|
for (const item of filesToParse) {
|
|
@@ -117,47 +163,53 @@ export function buildFileHashes(
|
|
|
117
163
|
seen.add(relPath);
|
|
118
164
|
|
|
119
165
|
const precomputed = precomputedData.get(relPath);
|
|
120
|
-
if (precomputed?._reverseDepOnly)
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
} else {
|
|
130
|
-
const rawStat = fileStat(path.join(rootDir, relPath));
|
|
131
|
-
mtime = rawStat ? rawStat.mtime : 0;
|
|
132
|
-
size = rawStat ? rawStat.size : 0;
|
|
133
|
-
}
|
|
134
|
-
fileHashes.push({ file: relPath, hash: precomputed.hash, mtime, size });
|
|
135
|
-
} else {
|
|
136
|
-
const absPath = path.join(rootDir, relPath);
|
|
137
|
-
let code: string | null;
|
|
138
|
-
try {
|
|
139
|
-
code = readFileSafe(absPath);
|
|
140
|
-
} catch (e) {
|
|
141
|
-
debug(`buildFileHashes: readFileSafe failed for ${relPath}: ${toErrorMessage(e)}`);
|
|
142
|
-
code = null;
|
|
143
|
-
}
|
|
144
|
-
if (code !== null) {
|
|
145
|
-
const stat = fileStat(absPath);
|
|
146
|
-
const mtime = stat ? stat.mtime : 0;
|
|
147
|
-
const size = stat ? stat.size : 0;
|
|
148
|
-
fileHashes.push({ file: relPath, hash: fileHash(code), mtime, size });
|
|
149
|
-
}
|
|
150
|
-
}
|
|
166
|
+
if (precomputed?._reverseDepOnly) continue;
|
|
167
|
+
|
|
168
|
+
const record = resolveHashFromPrecomputed(
|
|
169
|
+
relPath,
|
|
170
|
+
precomputed ?? ({} as PrecomputedFileData),
|
|
171
|
+
rootDir,
|
|
172
|
+
caller,
|
|
173
|
+
);
|
|
174
|
+
if (record) yield record;
|
|
151
175
|
}
|
|
152
176
|
|
|
153
|
-
//
|
|
177
|
+
// Metadata-only updates (self-heal mtime/size without re-parse)
|
|
154
178
|
for (const item of metadataUpdates) {
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
179
|
+
yield {
|
|
180
|
+
file: item.relPath,
|
|
181
|
+
hash: item.hash,
|
|
182
|
+
mtime: item.stat ? item.stat.mtime : 0,
|
|
183
|
+
size: item.stat ? item.stat.size : 0,
|
|
184
|
+
};
|
|
158
185
|
}
|
|
186
|
+
}
|
|
159
187
|
|
|
160
|
-
|
|
188
|
+
/**
|
|
189
|
+
* Build file hash entries for every collected file, including those that
|
|
190
|
+
* produced zero symbols (empty files, parsers that silently no-op'd, or
|
|
191
|
+
* optional-language extensions whose grammar wasn't installed). Iterating the
|
|
192
|
+
* symbol map instead would skip such files and leave them missing from
|
|
193
|
+
* `file_hashes`, which permanently breaks the JS-side fast-skip pre-flight on
|
|
194
|
+
* any subsequent no-op rebuild (#1068).
|
|
195
|
+
*
|
|
196
|
+
* Exported for unit testing.
|
|
197
|
+
*/
|
|
198
|
+
export function buildFileHashes(
|
|
199
|
+
filesToParse: FileToParse[],
|
|
200
|
+
precomputedData: Map<string, PrecomputedFileData>,
|
|
201
|
+
metadataUpdates: MetadataUpdate[],
|
|
202
|
+
rootDir: string,
|
|
203
|
+
): FileHashRecord[] {
|
|
204
|
+
return [
|
|
205
|
+
...iterFileHashRecords(
|
|
206
|
+
filesToParse,
|
|
207
|
+
precomputedData,
|
|
208
|
+
metadataUpdates,
|
|
209
|
+
rootDir,
|
|
210
|
+
'buildFileHashes',
|
|
211
|
+
),
|
|
212
|
+
];
|
|
161
213
|
}
|
|
162
214
|
|
|
163
215
|
// ── Native fast-path ─────────────────────────────────────────────────
|
|
@@ -260,36 +312,38 @@ function insertDefinitionsAndExports(
|
|
|
260
312
|
|
|
261
313
|
// ── JS fallback: Phase 2+3 ──────────────────────────────────────────
|
|
262
314
|
|
|
263
|
-
|
|
315
|
+
/** Build the in-memory `name|kind|line` → node-id map for a single file. */
|
|
316
|
+
function loadFileNodeIdMap(db: BetterSqlite3Database, relPath: string): Map<string, number> {
|
|
317
|
+
const map = new Map<string, number>();
|
|
318
|
+
for (const row of bulkNodeIdsByFile(db, relPath)) {
|
|
319
|
+
map.set(`${row.name}|${row.kind}|${row.line}`, row.id);
|
|
320
|
+
}
|
|
321
|
+
return map;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* First pass: for every file, emit file→def containment edges and collect
|
|
326
|
+
* the child-node insertion rows.
|
|
327
|
+
*/
|
|
328
|
+
function collectChildRowsAndFileEdges(
|
|
264
329
|
db: BetterSqlite3Database,
|
|
265
330
|
allSymbols: Map<string, ExtractorOutput>,
|
|
331
|
+
childRows: unknown[][],
|
|
332
|
+
edgeRows: unknown[][],
|
|
266
333
|
): void {
|
|
267
|
-
const childRows: unknown[][] = [];
|
|
268
|
-
const edgeRows: unknown[][] = [];
|
|
269
|
-
|
|
270
334
|
for (const [relPath, symbols] of allSymbols) {
|
|
271
|
-
|
|
272
|
-
const nodeIdMap = new Map<string, number>();
|
|
273
|
-
for (const row of bulkNodeIdsByFile(db, relPath)) {
|
|
274
|
-
nodeIdMap.set(`${row.name}|${row.kind}|${row.line}`, row.id);
|
|
275
|
-
}
|
|
276
|
-
|
|
335
|
+
const nodeIdMap = loadFileNodeIdMap(db, relPath);
|
|
277
336
|
const fileId = nodeIdMap.get(`${relPath}|file|0`);
|
|
278
337
|
|
|
279
338
|
for (const def of symbols.definitions) {
|
|
280
339
|
const defId = nodeIdMap.get(`${def.name}|${def.kind}|${def.line}`);
|
|
281
340
|
|
|
282
|
-
// Containment edge: file -> definition
|
|
283
341
|
if (fileId && defId) {
|
|
284
342
|
edgeRows.push([fileId, defId, 'contains', 1.0, 0]);
|
|
285
343
|
}
|
|
286
|
-
|
|
287
|
-
if (!def.children?.length) continue;
|
|
288
|
-
if (!defId) continue;
|
|
344
|
+
if (!def.children?.length || !defId) continue;
|
|
289
345
|
|
|
290
346
|
for (const child of def.children) {
|
|
291
|
-
// Child node
|
|
292
|
-
const qualifiedName = `${def.name}.${child.name}`;
|
|
293
347
|
childRows.push([
|
|
294
348
|
child.name,
|
|
295
349
|
child.kind,
|
|
@@ -297,39 +351,55 @@ function insertChildrenAndEdges(
|
|
|
297
351
|
child.line,
|
|
298
352
|
child.endLine || null,
|
|
299
353
|
defId,
|
|
300
|
-
|
|
354
|
+
`${def.name}.${child.name}`,
|
|
301
355
|
def.name,
|
|
302
356
|
child.visibility || null,
|
|
303
357
|
]);
|
|
304
358
|
}
|
|
305
359
|
}
|
|
306
360
|
}
|
|
361
|
+
}
|
|
307
362
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
363
|
+
/**
|
|
364
|
+
* Second pass (after child nodes have been inserted): emit def→child
|
|
365
|
+
* containment edges and child→def `parameter_of` edges.
|
|
366
|
+
*/
|
|
367
|
+
function collectChildEdges(
|
|
368
|
+
db: BetterSqlite3Database,
|
|
369
|
+
allSymbols: Map<string, ExtractorOutput>,
|
|
370
|
+
edgeRows: unknown[][],
|
|
371
|
+
): void {
|
|
312
372
|
for (const [relPath, symbols] of allSymbols) {
|
|
313
|
-
const nodeIdMap =
|
|
314
|
-
for (const row of bulkNodeIdsByFile(db, relPath)) {
|
|
315
|
-
nodeIdMap.set(`${row.name}|${row.kind}|${row.line}`, row.id);
|
|
316
|
-
}
|
|
373
|
+
const nodeIdMap = loadFileNodeIdMap(db, relPath);
|
|
317
374
|
for (const def of symbols.definitions) {
|
|
318
375
|
if (!def.children?.length) continue;
|
|
319
376
|
const defId = nodeIdMap.get(`${def.name}|${def.kind}|${def.line}`);
|
|
320
377
|
if (!defId) continue;
|
|
321
378
|
for (const child of def.children) {
|
|
322
379
|
const childId = nodeIdMap.get(`${child.name}|${child.kind}|${child.line}`);
|
|
323
|
-
if (childId)
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
}
|
|
380
|
+
if (!childId) continue;
|
|
381
|
+
edgeRows.push([defId, childId, 'contains', 1.0, 0]);
|
|
382
|
+
if (child.kind === 'parameter') {
|
|
383
|
+
edgeRows.push([childId, defId, 'parameter_of', 1.0, 0]);
|
|
328
384
|
}
|
|
329
385
|
}
|
|
330
386
|
}
|
|
331
387
|
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
function insertChildrenAndEdges(
|
|
391
|
+
db: BetterSqlite3Database,
|
|
392
|
+
allSymbols: Map<string, ExtractorOutput>,
|
|
393
|
+
): void {
|
|
394
|
+
const childRows: unknown[][] = [];
|
|
395
|
+
const edgeRows: unknown[][] = [];
|
|
332
396
|
|
|
397
|
+
collectChildRowsAndFileEdges(db, allSymbols, childRows, edgeRows);
|
|
398
|
+
|
|
399
|
+
// Insert children first (so they exist for edge lookup)
|
|
400
|
+
batchInsertNodes(db, childRows);
|
|
401
|
+
|
|
402
|
+
collectChildEdges(db, allSymbols, edgeRows);
|
|
333
403
|
batchInsertEdges(db, edgeRows);
|
|
334
404
|
}
|
|
335
405
|
|
|
@@ -348,50 +418,14 @@ function updateFileHashes(
|
|
|
348
418
|
// Iterate every collected file (#1068): files that produced zero symbols
|
|
349
419
|
// (empty, parser no-op, or grammar-missing optional language) still need a
|
|
350
420
|
// hash row, otherwise the next no-op rebuild's fast-skip pre-flight rejects.
|
|
351
|
-
const
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
// no-op: file unchanged, hash already correct
|
|
360
|
-
} else if (precomputed?.hash) {
|
|
361
|
-
let mtime: number;
|
|
362
|
-
let size: number;
|
|
363
|
-
if (precomputed.stat) {
|
|
364
|
-
mtime = precomputed.stat.mtime;
|
|
365
|
-
size = precomputed.stat.size;
|
|
366
|
-
} else {
|
|
367
|
-
const rawStat = fileStat(path.join(rootDir, relPath));
|
|
368
|
-
mtime = rawStat ? rawStat.mtime : 0;
|
|
369
|
-
size = rawStat ? rawStat.size : 0;
|
|
370
|
-
}
|
|
371
|
-
upsertHash.run(relPath, precomputed.hash, mtime, size);
|
|
372
|
-
} else {
|
|
373
|
-
const absPath = path.join(rootDir, relPath);
|
|
374
|
-
let code: string | null;
|
|
375
|
-
try {
|
|
376
|
-
code = readFileSafe(absPath);
|
|
377
|
-
} catch (e) {
|
|
378
|
-
debug(`updateFileHashes: readFileSafe failed for ${relPath}: ${toErrorMessage(e)}`);
|
|
379
|
-
code = null;
|
|
380
|
-
}
|
|
381
|
-
if (code !== null) {
|
|
382
|
-
const stat = fileStat(absPath);
|
|
383
|
-
const mtime = stat ? stat.mtime : 0;
|
|
384
|
-
const size = stat ? stat.size : 0;
|
|
385
|
-
upsertHash.run(relPath, fileHash(code), mtime, size);
|
|
386
|
-
}
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
// Also update metadata-only entries (self-heal mtime/size without re-parse)
|
|
391
|
-
for (const item of metadataUpdates) {
|
|
392
|
-
const mtime = item.stat ? item.stat.mtime : 0;
|
|
393
|
-
const size = item.stat ? item.stat.size : 0;
|
|
394
|
-
upsertHash.run(item.relPath, item.hash, mtime, size);
|
|
421
|
+
for (const record of iterFileHashRecords(
|
|
422
|
+
filesToParse,
|
|
423
|
+
precomputedData,
|
|
424
|
+
metadataUpdates,
|
|
425
|
+
rootDir,
|
|
426
|
+
'updateFileHashes',
|
|
427
|
+
)) {
|
|
428
|
+
upsertHash.run(record.file, record.hash, record.mtime, record.size);
|
|
395
429
|
}
|
|
396
430
|
}
|
|
397
431
|
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NativeDatabase connection lifecycle helpers.
|
|
3
|
+
*
|
|
4
|
+
* The Rust orchestrator and the JS pipeline stages both juggle the same
|
|
5
|
+
* `nativeDb` handle (rusqlite) alongside `ctx.db` (better-sqlite3). These
|
|
6
|
+
* helpers centralise the open/close/reopen sequence so both call sites
|
|
7
|
+
* preserve the same WAL-safety invariants:
|
|
8
|
+
*
|
|
9
|
+
* - Always checkpoint WAL before closing rusqlite — otherwise better-sqlite3's
|
|
10
|
+
* internal WAL index can drift and surface as SQLITE_CORRUPT on the next
|
|
11
|
+
* read (#715, #736).
|
|
12
|
+
* - Always reopen better-sqlite3 after rusqlite writes to drop the stale
|
|
13
|
+
* page cache.
|
|
14
|
+
*
|
|
15
|
+
* Lives in its own module so `tryNativeOrchestrator` (in `native-orchestrator.ts`)
|
|
16
|
+
* and the JS pipeline stages driver (in `pipeline.ts`) can share the helpers
|
|
17
|
+
* without either file importing the other.
|
|
18
|
+
*/
|
|
19
|
+
import { openDb } from '../../../../db/index.js';
|
|
20
|
+
import { debug } from '../../../../infrastructure/logger.js';
|
|
21
|
+
import { loadNative } from '../../../../infrastructure/native.js';
|
|
22
|
+
import { toErrorMessage } from '../../../../shared/errors.js';
|
|
23
|
+
import type { PipelineContext } from '../context.js';
|
|
24
|
+
|
|
25
|
+
/** Checkpoint WAL through rusqlite and close the native connection. */
|
|
26
|
+
export function closeNativeDb(ctx: PipelineContext, label: string): void {
|
|
27
|
+
if (!ctx.nativeDb) return;
|
|
28
|
+
try {
|
|
29
|
+
ctx.nativeDb.exec('PRAGMA wal_checkpoint(TRUNCATE)');
|
|
30
|
+
} catch (e) {
|
|
31
|
+
debug(`${label} WAL checkpoint failed: ${toErrorMessage(e)}`);
|
|
32
|
+
}
|
|
33
|
+
try {
|
|
34
|
+
ctx.nativeDb.close();
|
|
35
|
+
} catch (e) {
|
|
36
|
+
debug(`${label} nativeDb close failed: ${toErrorMessage(e)}`);
|
|
37
|
+
}
|
|
38
|
+
ctx.nativeDb = undefined;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Try to reopen the native connection for a given pipeline phase. */
|
|
42
|
+
export function reopenNativeDb(ctx: PipelineContext, label: string): void {
|
|
43
|
+
if ((ctx.opts.engine ?? 'auto') === 'wasm') return;
|
|
44
|
+
const native = loadNative();
|
|
45
|
+
if (!native?.NativeDatabase) return;
|
|
46
|
+
try {
|
|
47
|
+
ctx.nativeDb = native.NativeDatabase.openReadWrite(ctx.dbPath);
|
|
48
|
+
} catch (e) {
|
|
49
|
+
debug(`reopen nativeDb for ${label} failed: ${toErrorMessage(e)}`);
|
|
50
|
+
ctx.nativeDb = undefined;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** Close nativeDb and clear stale references in engineOpts. */
|
|
55
|
+
export function suspendNativeDb(ctx: PipelineContext, label: string): void {
|
|
56
|
+
closeNativeDb(ctx, label);
|
|
57
|
+
if (ctx.engineOpts?.nativeDb) {
|
|
58
|
+
ctx.engineOpts.nativeDb = undefined;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* After native writes, reopen the JS db connection to get a fresh page cache.
|
|
64
|
+
* Rusqlite WAL truncation invalidates better-sqlite3's internal WAL index,
|
|
65
|
+
* causing SQLITE_CORRUPT on the next read (#715, #736).
|
|
66
|
+
*/
|
|
67
|
+
export function refreshJsDb(ctx: PipelineContext): void {
|
|
68
|
+
try {
|
|
69
|
+
ctx.db.close();
|
|
70
|
+
} catch (e) {
|
|
71
|
+
debug(`refreshJsDb close failed: ${toErrorMessage(e)}`);
|
|
72
|
+
}
|
|
73
|
+
ctx.db = openDb(ctx.dbPath);
|
|
74
|
+
}
|