@optave/codegraph 3.8.0 → 3.9.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 +13 -8
- package/dist/ast-analysis/engine.d.ts.map +1 -1
- package/dist/ast-analysis/engine.js +137 -86
- package/dist/ast-analysis/engine.js.map +1 -1
- package/dist/ast-analysis/metrics.d.ts +0 -3
- package/dist/ast-analysis/metrics.d.ts.map +1 -1
- package/dist/ast-analysis/metrics.js +30 -13
- package/dist/ast-analysis/metrics.js.map +1 -1
- package/dist/ast-analysis/shared.d.ts.map +1 -1
- package/dist/ast-analysis/shared.js +24 -19
- package/dist/ast-analysis/shared.js.map +1 -1
- package/dist/ast-analysis/visitor-utils.d.ts.map +1 -1
- package/dist/ast-analysis/visitor-utils.js +55 -39
- package/dist/ast-analysis/visitor-utils.js.map +1 -1
- package/dist/ast-analysis/visitor.d.ts.map +1 -1
- package/dist/ast-analysis/visitor.js +91 -70
- package/dist/ast-analysis/visitor.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 +54 -58
- package/dist/ast-analysis/visitors/ast-store-visitor.js.map +1 -1
- package/dist/ast-analysis/visitors/complexity-visitor.d.ts.map +1 -1
- package/dist/ast-analysis/visitors/complexity-visitor.js +81 -39
- package/dist/ast-analysis/visitors/complexity-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 +57 -38
- package/dist/ast-analysis/visitors/dataflow-visitor.js.map +1 -1
- package/dist/cli/commands/branch-compare.d.ts.map +1 -1
- package/dist/cli/commands/branch-compare.js +4 -0
- package/dist/cli/commands/branch-compare.js.map +1 -1
- package/dist/cli/commands/diff-impact.d.ts.map +1 -1
- package/dist/cli/commands/diff-impact.js +2 -1
- package/dist/cli/commands/diff-impact.js.map +1 -1
- package/dist/cli/commands/info.d.ts.map +1 -1
- package/dist/cli/commands/info.js +3 -2
- package/dist/cli/commands/info.js.map +1 -1
- package/dist/cli/commands/watch.d.ts.map +1 -1
- package/dist/cli/commands/watch.js +16 -2
- package/dist/cli/commands/watch.js.map +1 -1
- package/dist/db/connection.d.ts.map +1 -1
- package/dist/db/connection.js +29 -26
- package/dist/db/connection.js.map +1 -1
- package/dist/db/query-builder.d.ts.map +1 -1
- package/dist/db/query-builder.js +16 -5
- package/dist/db/query-builder.js.map +1 -1
- package/dist/db/repository/base.d.ts +16 -0
- package/dist/db/repository/base.d.ts.map +1 -1
- package/dist/db/repository/base.js +31 -0
- package/dist/db/repository/base.js.map +1 -1
- package/dist/db/repository/native-repository.d.ts +7 -1
- package/dist/db/repository/native-repository.d.ts.map +1 -1
- package/dist/db/repository/native-repository.js +100 -1
- package/dist/db/repository/native-repository.js.map +1 -1
- package/dist/db/repository/nodes.d.ts.map +1 -1
- package/dist/db/repository/nodes.js +8 -4
- package/dist/db/repository/nodes.js.map +1 -1
- package/dist/db/repository/sqlite-repository.d.ts +4 -0
- package/dist/db/repository/sqlite-repository.d.ts.map +1 -1
- package/dist/db/repository/sqlite-repository.js +51 -0
- package/dist/db/repository/sqlite-repository.js.map +1 -1
- package/dist/domain/analysis/brief.d.ts.map +1 -1
- package/dist/domain/analysis/brief.js +13 -17
- package/dist/domain/analysis/brief.js.map +1 -1
- package/dist/domain/analysis/context.d.ts.map +1 -1
- package/dist/domain/analysis/context.js +14 -11
- package/dist/domain/analysis/context.js.map +1 -1
- package/dist/domain/analysis/dependencies.d.ts.map +1 -1
- package/dist/domain/analysis/dependencies.js +64 -59
- package/dist/domain/analysis/dependencies.js.map +1 -1
- package/dist/domain/analysis/fn-impact.d.ts +2 -7
- package/dist/domain/analysis/fn-impact.d.ts.map +1 -1
- package/dist/domain/analysis/fn-impact.js +33 -31
- package/dist/domain/analysis/fn-impact.js.map +1 -1
- package/dist/domain/analysis/implementations.d.ts.map +1 -1
- package/dist/domain/analysis/implementations.js +11 -19
- package/dist/domain/analysis/implementations.js.map +1 -1
- package/dist/domain/analysis/module-map.d.ts.map +1 -1
- package/dist/domain/analysis/module-map.js +55 -76
- package/dist/domain/analysis/module-map.js.map +1 -1
- package/dist/domain/analysis/query-helpers.d.ts +7 -0
- package/dist/domain/analysis/query-helpers.d.ts.map +1 -1
- package/dist/domain/analysis/query-helpers.js +15 -1
- package/dist/domain/analysis/query-helpers.js.map +1 -1
- package/dist/domain/graph/builder/pipeline.d.ts.map +1 -1
- package/dist/domain/graph/builder/pipeline.js +352 -107
- 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 +49 -18
- package/dist/domain/graph/builder/stages/build-edges.js.map +1 -1
- package/dist/domain/graph/builder/stages/detect-changes.js +2 -2
- package/dist/domain/graph/builder/stages/detect-changes.js.map +1 -1
- package/dist/domain/graph/builder/stages/finalize.js +2 -2
- package/dist/domain/graph/builder/stages/finalize.js.map +1 -1
- package/dist/domain/graph/builder/stages/insert-nodes.d.ts.map +1 -1
- package/dist/domain/graph/builder/stages/insert-nodes.js +32 -21
- package/dist/domain/graph/builder/stages/insert-nodes.js.map +1 -1
- package/dist/domain/graph/builder/stages/resolve-imports.d.ts.map +1 -1
- package/dist/domain/graph/builder/stages/resolve-imports.js +95 -84
- package/dist/domain/graph/builder/stages/resolve-imports.js.map +1 -1
- package/dist/domain/graph/cycles.d.ts +6 -0
- package/dist/domain/graph/cycles.d.ts.map +1 -1
- package/dist/domain/graph/cycles.js +114 -22
- package/dist/domain/graph/cycles.js.map +1 -1
- package/dist/domain/graph/resolve.js +1 -1
- package/dist/domain/graph/resolve.js.map +1 -1
- package/dist/domain/graph/watcher.d.ts +2 -0
- package/dist/domain/graph/watcher.d.ts.map +1 -1
- package/dist/domain/graph/watcher.js +170 -75
- package/dist/domain/graph/watcher.js.map +1 -1
- package/dist/domain/parser.d.ts +3 -4
- package/dist/domain/parser.d.ts.map +1 -1
- package/dist/domain/parser.js +141 -89
- package/dist/domain/parser.js.map +1 -1
- package/dist/domain/search/generator.js +1 -1
- package/dist/domain/search/generator.js.map +1 -1
- package/dist/domain/search/models.d.ts +4 -3
- package/dist/domain/search/models.d.ts.map +1 -1
- package/dist/domain/search/models.js +23 -8
- 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 +29 -18
- package/dist/domain/search/search/hybrid.js.map +1 -1
- package/dist/extractors/go.js +36 -33
- package/dist/extractors/go.js.map +1 -1
- package/dist/extractors/helpers.d.ts.map +1 -1
- package/dist/extractors/helpers.js +40 -29
- package/dist/extractors/helpers.js.map +1 -1
- package/dist/extractors/java.js +58 -46
- package/dist/extractors/java.js.map +1 -1
- package/dist/extractors/javascript.js +65 -54
- package/dist/extractors/javascript.js.map +1 -1
- package/dist/extractors/kotlin.js +84 -78
- package/dist/extractors/kotlin.js.map +1 -1
- package/dist/extractors/python.js +29 -24
- package/dist/extractors/python.js.map +1 -1
- package/dist/extractors/rust.js +41 -32
- package/dist/extractors/rust.js.map +1 -1
- package/dist/extractors/solidity.js +58 -67
- package/dist/extractors/solidity.js.map +1 -1
- package/dist/extractors/swift.js +83 -81
- package/dist/extractors/swift.js.map +1 -1
- package/dist/extractors/zig.js +58 -60
- package/dist/extractors/zig.js.map +1 -1
- package/dist/features/ast.d.ts +16 -14
- package/dist/features/ast.d.ts.map +1 -1
- package/dist/features/ast.js +83 -81
- package/dist/features/ast.js.map +1 -1
- package/dist/features/audit.d.ts.map +1 -1
- package/dist/features/audit.js +8 -6
- package/dist/features/audit.js.map +1 -1
- package/dist/features/branch-compare.d.ts.map +1 -1
- package/dist/features/branch-compare.js +69 -72
- package/dist/features/branch-compare.js.map +1 -1
- package/dist/features/communities.d.ts.map +1 -1
- package/dist/features/communities.js +19 -7
- package/dist/features/communities.js.map +1 -1
- package/dist/features/complexity.d.ts.map +1 -1
- package/dist/features/complexity.js +120 -125
- package/dist/features/complexity.js.map +1 -1
- package/dist/features/dataflow.d.ts.map +1 -1
- package/dist/features/dataflow.js +136 -137
- package/dist/features/dataflow.js.map +1 -1
- package/dist/features/flow.d.ts.map +1 -1
- package/dist/features/flow.js +84 -79
- package/dist/features/flow.js.map +1 -1
- package/dist/features/structure-query.d.ts.map +1 -1
- package/dist/features/structure-query.js +69 -65
- package/dist/features/structure-query.js.map +1 -1
- package/dist/graph/algorithms/leiden/optimiser.d.ts.map +1 -1
- package/dist/graph/algorithms/leiden/optimiser.js +70 -55
- package/dist/graph/algorithms/leiden/optimiser.js.map +1 -1
- package/dist/graph/algorithms/leiden/partition.d.ts.map +1 -1
- package/dist/graph/algorithms/leiden/partition.js +288 -266
- package/dist/graph/algorithms/leiden/partition.js.map +1 -1
- package/dist/graph/model.d.ts.map +1 -1
- package/dist/graph/model.js +5 -1
- package/dist/graph/model.js.map +1 -1
- package/dist/infrastructure/config.d.ts.map +1 -1
- package/dist/infrastructure/config.js +6 -4
- package/dist/infrastructure/config.js.map +1 -1
- package/dist/infrastructure/suppress.d.ts +25 -0
- package/dist/infrastructure/suppress.d.ts.map +1 -0
- package/dist/infrastructure/suppress.js +43 -0
- package/dist/infrastructure/suppress.js.map +1 -0
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +29 -24
- package/dist/mcp/server.js.map +1 -1
- package/dist/presentation/dataflow.d.ts.map +1 -1
- package/dist/presentation/dataflow.js +47 -38
- package/dist/presentation/dataflow.js.map +1 -1
- package/dist/presentation/diff-impact-mermaid.d.ts.map +1 -1
- package/dist/presentation/diff-impact-mermaid.js +60 -51
- package/dist/presentation/diff-impact-mermaid.js.map +1 -1
- package/dist/presentation/queries-cli/exports.d.ts.map +1 -1
- package/dist/presentation/queries-cli/exports.js +20 -14
- package/dist/presentation/queries-cli/exports.js.map +1 -1
- package/dist/presentation/queries-cli/impact.d.ts.map +1 -1
- package/dist/presentation/queries-cli/impact.js +15 -13
- package/dist/presentation/queries-cli/impact.js.map +1 -1
- package/dist/presentation/queries-cli/inspect.d.ts.map +1 -1
- package/dist/presentation/queries-cli/inspect.js +101 -79
- package/dist/presentation/queries-cli/inspect.js.map +1 -1
- package/dist/presentation/queries-cli/overview.d.ts.map +1 -1
- package/dist/presentation/queries-cli/overview.js +25 -16
- package/dist/presentation/queries-cli/overview.js.map +1 -1
- package/dist/presentation/queries-cli/path.js +26 -20
- package/dist/presentation/queries-cli/path.js.map +1 -1
- package/dist/presentation/result-formatter.d.ts +10 -0
- package/dist/presentation/result-formatter.d.ts.map +1 -1
- package/dist/presentation/result-formatter.js +16 -1
- package/dist/presentation/result-formatter.js.map +1 -1
- package/dist/presentation/viewer.d.ts.map +1 -1
- package/dist/presentation/viewer.js +18 -12
- package/dist/presentation/viewer.js.map +1 -1
- package/dist/shared/errors.d.ts +5 -0
- package/dist/shared/errors.d.ts.map +1 -1
- package/dist/shared/errors.js +5 -0
- package/dist/shared/errors.js.map +1 -1
- package/dist/shared/hierarchy.d.ts +8 -2
- package/dist/shared/hierarchy.d.ts.map +1 -1
- package/dist/shared/hierarchy.js +42 -1
- package/dist/shared/hierarchy.js.map +1 -1
- package/dist/shared/normalize.d.ts +6 -1
- package/dist/shared/normalize.d.ts.map +1 -1
- package/dist/shared/normalize.js +20 -12
- package/dist/shared/normalize.js.map +1 -1
- package/dist/shared/paginate.d.ts +0 -9
- package/dist/shared/paginate.d.ts.map +1 -1
- package/dist/shared/paginate.js +0 -15
- package/dist/shared/paginate.js.map +1 -1
- package/dist/types.d.ts +12 -5
- package/dist/types.d.ts.map +1 -1
- package/grammars/tree-sitter-erlang.wasm +0 -0
- package/grammars/tree-sitter-gleam.wasm +0 -0
- package/package.json +9 -9
- package/src/ast-analysis/engine.ts +176 -104
- package/src/ast-analysis/metrics.ts +33 -11
- package/src/ast-analysis/shared.ts +33 -24
- package/src/ast-analysis/visitor-utils.ts +52 -32
- package/src/ast-analysis/visitor.ts +132 -71
- package/src/ast-analysis/visitors/ast-store-visitor.ts +53 -50
- package/src/ast-analysis/visitors/complexity-visitor.ts +89 -40
- package/src/ast-analysis/visitors/dataflow-visitor.ts +87 -43
- package/src/cli/commands/branch-compare.ts +4 -0
- package/src/cli/commands/diff-impact.ts +2 -1
- package/src/cli/commands/info.ts +3 -2
- package/src/cli/commands/watch.ts +16 -2
- package/src/db/connection.ts +29 -28
- package/src/db/query-builder.ts +15 -3
- package/src/db/repository/base.ts +34 -0
- package/src/db/repository/native-repository.ts +104 -1
- package/src/db/repository/nodes.ts +13 -8
- package/src/db/repository/sqlite-repository.ts +55 -0
- package/src/domain/analysis/brief.ts +15 -25
- package/src/domain/analysis/context.ts +17 -10
- package/src/domain/analysis/dependencies.ts +77 -81
- package/src/domain/analysis/fn-impact.ts +36 -43
- package/src/domain/analysis/implementations.ts +11 -17
- package/src/domain/analysis/module-map.ts +58 -92
- package/src/domain/analysis/query-helpers.ts +18 -1
- package/src/domain/graph/builder/pipeline.ts +409 -99
- package/src/domain/graph/builder/stages/build-edges.ts +45 -19
- package/src/domain/graph/builder/stages/detect-changes.ts +2 -2
- package/src/domain/graph/builder/stages/finalize.ts +2 -2
- package/src/domain/graph/builder/stages/insert-nodes.ts +59 -34
- package/src/domain/graph/builder/stages/resolve-imports.ts +122 -100
- package/src/domain/graph/cycles.ts +110 -23
- package/src/domain/graph/resolve.ts +1 -1
- package/src/domain/graph/watcher.ts +202 -96
- package/src/domain/parser.ts +143 -89
- package/src/domain/search/generator.ts +1 -1
- package/src/domain/search/models.ts +26 -7
- package/src/domain/search/search/hybrid.ts +69 -51
- package/src/extractors/go.ts +43 -33
- package/src/extractors/helpers.ts +37 -23
- package/src/extractors/java.ts +66 -47
- package/src/extractors/javascript.ts +66 -54
- package/src/extractors/kotlin.ts +84 -77
- package/src/extractors/python.ts +31 -25
- package/src/extractors/rust.ts +37 -29
- package/src/extractors/solidity.ts +57 -61
- package/src/extractors/swift.ts +81 -80
- package/src/extractors/zig.ts +58 -61
- package/src/features/ast.ts +130 -110
- package/src/features/audit.ts +8 -6
- package/src/features/branch-compare.ts +105 -79
- package/src/features/communities.ts +25 -10
- package/src/features/complexity.ts +171 -134
- package/src/features/dataflow.ts +165 -175
- package/src/features/flow.ts +129 -92
- package/src/features/structure-query.ts +79 -64
- package/src/graph/algorithms/leiden/optimiser.ts +99 -55
- package/src/graph/algorithms/leiden/partition.ts +359 -294
- package/src/graph/model.ts +6 -1
- package/src/infrastructure/config.ts +6 -4
- package/src/infrastructure/suppress.ts +47 -0
- package/src/mcp/server.ts +53 -37
- package/src/presentation/dataflow.ts +50 -44
- package/src/presentation/diff-impact-mermaid.ts +104 -62
- package/src/presentation/queries-cli/exports.ts +21 -13
- package/src/presentation/queries-cli/impact.ts +15 -13
- package/src/presentation/queries-cli/inspect.ts +100 -81
- package/src/presentation/queries-cli/overview.ts +26 -16
- package/src/presentation/queries-cli/path.ts +33 -25
- package/src/presentation/result-formatter.ts +19 -1
- package/src/presentation/viewer.ts +42 -14
- package/src/shared/errors.ts +6 -0
- package/src/shared/hierarchy.ts +50 -2
- package/src/shared/normalize.ts +31 -12
- package/src/shared/paginate.ts +0 -17
- package/src/types.ts +26 -5
|
@@ -6,12 +6,21 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import path from 'node:path';
|
|
8
8
|
import { performance } from 'node:perf_hooks';
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
closeDbPair,
|
|
11
|
+
getBuildMeta,
|
|
12
|
+
initSchema,
|
|
13
|
+
MIGRATIONS,
|
|
14
|
+
openDb,
|
|
15
|
+
setBuildMeta,
|
|
16
|
+
} from '../../../db/index.js';
|
|
10
17
|
import { detectWorkspaces, loadConfig } from '../../../infrastructure/config.js';
|
|
11
|
-
import { info, warn } from '../../../infrastructure/logger.js';
|
|
18
|
+
import { debug, info, warn } from '../../../infrastructure/logger.js';
|
|
12
19
|
import { loadNative } from '../../../infrastructure/native.js';
|
|
20
|
+
import { semverCompare } from '../../../infrastructure/update-check.js';
|
|
21
|
+
import { toErrorMessage } from '../../../shared/errors.js';
|
|
13
22
|
import { CODEGRAPH_VERSION } from '../../../shared/version.js';
|
|
14
|
-
import type { BuildGraphOpts, BuildResult } from '../../../types.js';
|
|
23
|
+
import type { BuildGraphOpts, BuildResult, Definition, ExtractorOutput } from '../../../types.js';
|
|
15
24
|
import { getActiveEngine } from '../../parser.js';
|
|
16
25
|
import { setWorkspaces } from '../resolve.js';
|
|
17
26
|
import { PipelineContext } from './context.js';
|
|
@@ -50,8 +59,10 @@ function initializeEngine(ctx: PipelineContext): void {
|
|
|
50
59
|
? () => {
|
|
51
60
|
try {
|
|
52
61
|
ctx.nativeDb?.exec('PRAGMA wal_checkpoint(TRUNCATE)');
|
|
53
|
-
} catch {
|
|
54
|
-
|
|
62
|
+
} catch (e) {
|
|
63
|
+
debug(
|
|
64
|
+
`resumeJsDb: WAL checkpoint failed (nativeDb may already be closed): ${toErrorMessage(e)}`,
|
|
65
|
+
);
|
|
55
66
|
}
|
|
56
67
|
}
|
|
57
68
|
: undefined,
|
|
@@ -123,7 +134,9 @@ function setupPipeline(ctx: PipelineContext): void {
|
|
|
123
134
|
// Use NativeDatabase for schema init when native engine is available (Phase 6.13).
|
|
124
135
|
// better-sqlite3 (ctx.db) is still always opened — needed for queries and stages
|
|
125
136
|
// that haven't been migrated to rusqlite yet.
|
|
126
|
-
|
|
137
|
+
// Skip native DB entirely when user explicitly requested --engine wasm.
|
|
138
|
+
const enginePref = ctx.opts.engine || 'auto';
|
|
139
|
+
const native = enginePref !== 'wasm' ? loadNative() : null;
|
|
127
140
|
if (native?.NativeDatabase) {
|
|
128
141
|
try {
|
|
129
142
|
ctx.nativeDb = native.NativeDatabase.openReadWrite(ctx.dbPath);
|
|
@@ -132,11 +145,11 @@ function setupPipeline(ctx: PipelineContext): void {
|
|
|
132
145
|
// with no cross-library WAL frames (#715, #717).
|
|
133
146
|
ctx.nativeDb.exec('PRAGMA wal_checkpoint(TRUNCATE)');
|
|
134
147
|
} catch (err) {
|
|
135
|
-
warn(`NativeDatabase setup failed, falling back to JS: ${(err
|
|
148
|
+
warn(`NativeDatabase setup failed, falling back to JS: ${toErrorMessage(err)}`);
|
|
136
149
|
try {
|
|
137
150
|
ctx.nativeDb?.close();
|
|
138
|
-
} catch {
|
|
139
|
-
|
|
151
|
+
} catch (e) {
|
|
152
|
+
debug(`setupNativeDb: close failed during fallback: ${toErrorMessage(e)}`);
|
|
140
153
|
}
|
|
141
154
|
ctx.nativeDb = undefined;
|
|
142
155
|
}
|
|
@@ -185,6 +198,59 @@ function formatTimingResult(ctx: PipelineContext): BuildResult {
|
|
|
185
198
|
};
|
|
186
199
|
}
|
|
187
200
|
|
|
201
|
+
// ── NativeDb lifecycle helpers ──────────────────────────────────────────
|
|
202
|
+
|
|
203
|
+
/** Checkpoint WAL through rusqlite and close the native connection. */
|
|
204
|
+
function closeNativeDb(ctx: PipelineContext, label: string): void {
|
|
205
|
+
if (!ctx.nativeDb) return;
|
|
206
|
+
try {
|
|
207
|
+
ctx.nativeDb.exec('PRAGMA wal_checkpoint(TRUNCATE)');
|
|
208
|
+
} catch (e) {
|
|
209
|
+
debug(`${label} WAL checkpoint failed: ${toErrorMessage(e)}`);
|
|
210
|
+
}
|
|
211
|
+
try {
|
|
212
|
+
ctx.nativeDb.close();
|
|
213
|
+
} catch (e) {
|
|
214
|
+
debug(`${label} nativeDb close failed: ${toErrorMessage(e)}`);
|
|
215
|
+
}
|
|
216
|
+
ctx.nativeDb = undefined;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/** Try to reopen the native connection for a given pipeline phase. */
|
|
220
|
+
function reopenNativeDb(ctx: PipelineContext, label: string): void {
|
|
221
|
+
if ((ctx.opts.engine ?? 'auto') === 'wasm') return;
|
|
222
|
+
const native = loadNative();
|
|
223
|
+
if (!native?.NativeDatabase) return;
|
|
224
|
+
try {
|
|
225
|
+
ctx.nativeDb = native.NativeDatabase.openReadWrite(ctx.dbPath);
|
|
226
|
+
} catch (e) {
|
|
227
|
+
debug(`reopen nativeDb for ${label} failed: ${toErrorMessage(e)}`);
|
|
228
|
+
ctx.nativeDb = undefined;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/** Close nativeDb and clear stale references in engineOpts. */
|
|
233
|
+
function suspendNativeDb(ctx: PipelineContext, label: string): void {
|
|
234
|
+
closeNativeDb(ctx, label);
|
|
235
|
+
if (ctx.engineOpts?.nativeDb) {
|
|
236
|
+
ctx.engineOpts.nativeDb = undefined;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* After native writes, reopen the JS db connection to get a fresh page cache.
|
|
242
|
+
* Rusqlite WAL truncation invalidates better-sqlite3's internal WAL index,
|
|
243
|
+
* causing SQLITE_CORRUPT on the next read (#715, #736).
|
|
244
|
+
*/
|
|
245
|
+
function refreshJsDb(ctx: PipelineContext): void {
|
|
246
|
+
try {
|
|
247
|
+
ctx.db.close();
|
|
248
|
+
} catch (e) {
|
|
249
|
+
debug(`refreshJsDb close failed: ${toErrorMessage(e)}`);
|
|
250
|
+
}
|
|
251
|
+
ctx.db = openDb(ctx.dbPath);
|
|
252
|
+
}
|
|
253
|
+
|
|
188
254
|
// ── Pipeline stages execution ───────────────────────────────────────────
|
|
189
255
|
|
|
190
256
|
async function runPipelineStages(ctx: PipelineContext): Promise<void> {
|
|
@@ -195,26 +261,7 @@ async function runPipelineStages(ctx: PipelineContext): Promise<void> {
|
|
|
195
261
|
// that use suspendJsDb/resumeJsDb WAL checkpoint pattern (#696).
|
|
196
262
|
const hadNativeDb = !!ctx.nativeDb;
|
|
197
263
|
if (ctx.db && ctx.nativeDb) {
|
|
198
|
-
|
|
199
|
-
// needs to apply WAL frames written by a different SQLite library (#715, #717).
|
|
200
|
-
// Separate try/catch blocks ensure close() always runs even if checkpoint throws,
|
|
201
|
-
// preventing a live rusqlite connection from lingering until GC.
|
|
202
|
-
try {
|
|
203
|
-
ctx.nativeDb.exec('PRAGMA wal_checkpoint(TRUNCATE)');
|
|
204
|
-
} catch {
|
|
205
|
-
/* ignore checkpoint errors */
|
|
206
|
-
}
|
|
207
|
-
try {
|
|
208
|
-
ctx.nativeDb.close();
|
|
209
|
-
} catch {
|
|
210
|
-
/* ignore close errors */
|
|
211
|
-
}
|
|
212
|
-
ctx.nativeDb = undefined;
|
|
213
|
-
// Also clear stale reference in engineOpts to prevent stages from
|
|
214
|
-
// calling methods on the closed NativeDatabase.
|
|
215
|
-
if (ctx.engineOpts?.nativeDb) {
|
|
216
|
-
ctx.engineOpts.nativeDb = undefined;
|
|
217
|
-
}
|
|
264
|
+
suspendNativeDb(ctx, 'pre-collect');
|
|
218
265
|
}
|
|
219
266
|
|
|
220
267
|
await collectFiles(ctx);
|
|
@@ -228,44 +275,15 @@ async function runPipelineStages(ctx: PipelineContext): Promise<void> {
|
|
|
228
275
|
// guard internally (same pattern as feature modules). Closed again before
|
|
229
276
|
// resolveImports/buildEdges which don't yet have the guard (#709).
|
|
230
277
|
if (hadNativeDb && ctx.engineName === 'native') {
|
|
231
|
-
|
|
232
|
-
if (native?.NativeDatabase) {
|
|
233
|
-
try {
|
|
234
|
-
ctx.nativeDb = native.NativeDatabase.openReadWrite(ctx.dbPath);
|
|
235
|
-
} catch {
|
|
236
|
-
ctx.nativeDb = undefined;
|
|
237
|
-
}
|
|
238
|
-
}
|
|
278
|
+
reopenNativeDb(ctx, 'insertNodes');
|
|
239
279
|
}
|
|
240
280
|
|
|
241
281
|
await insertNodes(ctx);
|
|
242
282
|
|
|
243
283
|
// Close nativeDb after insertNodes — remaining pipeline stages use JS paths.
|
|
244
284
|
if (ctx.nativeDb && ctx.db) {
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
try {
|
|
248
|
-
ctx.nativeDb.exec('PRAGMA wal_checkpoint(TRUNCATE)');
|
|
249
|
-
} catch {
|
|
250
|
-
/* ignore checkpoint errors */
|
|
251
|
-
}
|
|
252
|
-
try {
|
|
253
|
-
ctx.nativeDb.close();
|
|
254
|
-
} catch {
|
|
255
|
-
/* ignore close errors */
|
|
256
|
-
}
|
|
257
|
-
ctx.nativeDb = undefined;
|
|
258
|
-
// Reopen better-sqlite3 connection to get a fresh page cache.
|
|
259
|
-
// After rusqlite truncates the WAL, better-sqlite3's internal WAL index
|
|
260
|
-
// (shared-memory mapping) may reference frames that no longer exist,
|
|
261
|
-
// causing SQLITE_CORRUPT on the next read. Closing and reopening
|
|
262
|
-
// forces a clean slate — the only reliable cross-library handoff (#715, #736).
|
|
263
|
-
try {
|
|
264
|
-
ctx.db.close();
|
|
265
|
-
} catch {
|
|
266
|
-
/* ignore close errors */
|
|
267
|
-
}
|
|
268
|
-
ctx.db = openDb(ctx.dbPath);
|
|
285
|
+
closeNativeDb(ctx, 'post-insertNodes');
|
|
286
|
+
refreshJsDb(ctx);
|
|
269
287
|
}
|
|
270
288
|
|
|
271
289
|
await resolveImports(ctx);
|
|
@@ -275,41 +293,23 @@ async function runPipelineStages(ctx: PipelineContext): Promise<void> {
|
|
|
275
293
|
// Reopen nativeDb for feature modules (ast, cfg, complexity, dataflow)
|
|
276
294
|
// which use suspendJsDb/resumeJsDb WAL checkpoint before native writes.
|
|
277
295
|
if (hadNativeDb) {
|
|
278
|
-
|
|
279
|
-
if (
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
}
|
|
285
|
-
} catch {
|
|
286
|
-
ctx.nativeDb = undefined;
|
|
287
|
-
if (ctx.engineOpts) {
|
|
288
|
-
ctx.engineOpts.nativeDb = undefined;
|
|
289
|
-
}
|
|
290
|
-
}
|
|
296
|
+
reopenNativeDb(ctx, 'analyses');
|
|
297
|
+
if (ctx.nativeDb && ctx.engineOpts) {
|
|
298
|
+
ctx.engineOpts.nativeDb = ctx.nativeDb;
|
|
299
|
+
}
|
|
300
|
+
if (!ctx.nativeDb && ctx.engineOpts) {
|
|
301
|
+
ctx.engineOpts.nativeDb = undefined;
|
|
291
302
|
}
|
|
292
303
|
}
|
|
293
304
|
|
|
294
305
|
await runAnalyses(ctx);
|
|
295
306
|
|
|
296
|
-
//
|
|
297
|
-
// and
|
|
307
|
+
// Keep nativeDb open through finalize so persistBuildMetadata, advisory
|
|
308
|
+
// checks, and count queries use the native path. closeDbPair inside
|
|
309
|
+
// finalize handles both connections. Refresh the JS db so it has a
|
|
310
|
+
// valid page cache in case finalize falls back to JS paths (#751).
|
|
298
311
|
if (ctx.nativeDb) {
|
|
299
|
-
|
|
300
|
-
// needs to apply WAL frames written by a different SQLite library (#715, #717).
|
|
301
|
-
// Separate try/catch blocks ensure close() always runs even if checkpoint throws.
|
|
302
|
-
try {
|
|
303
|
-
ctx.nativeDb.exec('PRAGMA wal_checkpoint(TRUNCATE)');
|
|
304
|
-
} catch {
|
|
305
|
-
/* ignore checkpoint errors */
|
|
306
|
-
}
|
|
307
|
-
try {
|
|
308
|
-
ctx.nativeDb.close();
|
|
309
|
-
} catch {
|
|
310
|
-
/* ignore close errors */
|
|
311
|
-
}
|
|
312
|
-
ctx.nativeDb = undefined;
|
|
312
|
+
refreshJsDb(ctx);
|
|
313
313
|
}
|
|
314
314
|
|
|
315
315
|
await finalize(ctx);
|
|
@@ -338,7 +338,31 @@ export async function buildGraph(
|
|
|
338
338
|
// When available, run the entire build pipeline in Rust with zero
|
|
339
339
|
// napi crossings (eliminates WAL dual-connection dance). Falls back
|
|
340
340
|
// to the JS pipeline on failure or when native is unavailable.
|
|
341
|
-
|
|
341
|
+
//
|
|
342
|
+
// Native addon ≤3.8.0 has a path bug: file_symbols keys are absolute
|
|
343
|
+
// paths but known_files are relative, causing zero import/call edges.
|
|
344
|
+
// Native addon ≤3.8.1 has an incremental barrel bug: the Rust pipeline
|
|
345
|
+
// doesn't re-parse barrel files that are imported by changed files,
|
|
346
|
+
// causing missing barrel import edges and lost analysis data for
|
|
347
|
+
// reverse-dep files during incremental builds.
|
|
348
|
+
// Skip the orchestrator for affected versions (fixed in 3.9.0+).
|
|
349
|
+
const orchestratorBuggy = !!ctx.engineVersion && semverCompare(ctx.engineVersion, '3.8.1') <= 0;
|
|
350
|
+
const forceJs =
|
|
351
|
+
process.env.CODEGRAPH_FORCE_JS_PIPELINE === '1' ||
|
|
352
|
+
ctx.forceFullRebuild ||
|
|
353
|
+
orchestratorBuggy ||
|
|
354
|
+
ctx.engineName !== 'native';
|
|
355
|
+
if (forceJs) {
|
|
356
|
+
const reason =
|
|
357
|
+
process.env.CODEGRAPH_FORCE_JS_PIPELINE === '1'
|
|
358
|
+
? 'CODEGRAPH_FORCE_JS_PIPELINE=1'
|
|
359
|
+
: ctx.forceFullRebuild
|
|
360
|
+
? 'forceFullRebuild'
|
|
361
|
+
: orchestratorBuggy
|
|
362
|
+
? `buggy addon ${ctx.engineVersion}`
|
|
363
|
+
: `engine=${ctx.engineName}`;
|
|
364
|
+
debug(`Skipping native orchestrator: ${reason}`);
|
|
365
|
+
}
|
|
342
366
|
if (!forceJs && ctx.nativeDb?.buildGraph) {
|
|
343
367
|
try {
|
|
344
368
|
const resultJson = ctx.nativeDb.buildGraph(
|
|
@@ -353,21 +377,307 @@ export async function buildGraph(
|
|
|
353
377
|
nodeCount?: number;
|
|
354
378
|
edgeCount?: number;
|
|
355
379
|
fileCount?: number;
|
|
380
|
+
changedFiles?: string[];
|
|
381
|
+
changedCount?: number;
|
|
382
|
+
removedCount?: number;
|
|
383
|
+
isFullBuild?: boolean;
|
|
356
384
|
};
|
|
357
385
|
|
|
358
386
|
if (result.earlyExit) {
|
|
387
|
+
info('No changes detected');
|
|
359
388
|
closeDbPair({ db: ctx.db, nativeDb: ctx.nativeDb });
|
|
360
389
|
return;
|
|
361
390
|
}
|
|
362
391
|
|
|
392
|
+
// Log incremental status to match JS pipeline output
|
|
393
|
+
const changed = result.changedCount ?? 0;
|
|
394
|
+
const removed = result.removedCount ?? 0;
|
|
395
|
+
if (!result.isFullBuild && (changed > 0 || removed > 0)) {
|
|
396
|
+
info(`Incremental: ${changed} changed, ${removed} removed`);
|
|
397
|
+
}
|
|
398
|
+
|
|
363
399
|
// Map Rust timing fields to the JS BuildResult format.
|
|
364
400
|
// Rust handles collect+detect+parse+insert+resolve+edges+structure+roles.
|
|
365
|
-
// AST/complexity/CFG/dataflow analyses are not yet ported to Rust.
|
|
366
401
|
const p = result.phases;
|
|
367
|
-
|
|
402
|
+
|
|
403
|
+
// Sync build_meta so JS-side version/engine checks work on next build.
|
|
404
|
+
// Note: the Rust orchestrator also writes codegraph_version (using
|
|
405
|
+
// CARGO_PKG_VERSION). We intentionally overwrite it here with the npm
|
|
406
|
+
// package version so that the JS-side "version changed → full rebuild"
|
|
407
|
+
// detection (line ~97) compares against the authoritative JS version.
|
|
408
|
+
// The two versions are kept in lockstep by the release process.
|
|
409
|
+
setBuildMeta(ctx.db, {
|
|
410
|
+
engine: ctx.engineName,
|
|
411
|
+
engine_version: ctx.engineVersion || '',
|
|
412
|
+
codegraph_version: CODEGRAPH_VERSION,
|
|
413
|
+
schema_version: String(ctx.schemaVersion),
|
|
414
|
+
built_at: new Date().toISOString(),
|
|
415
|
+
node_count: String(result.nodeCount ?? 0),
|
|
416
|
+
edge_count: String(result.edgeCount ?? 0),
|
|
417
|
+
});
|
|
418
|
+
|
|
368
419
|
info(
|
|
369
420
|
`Native build orchestrator completed: ${result.nodeCount ?? 0} nodes, ${result.edgeCount ?? 0} edges, ${result.fileCount ?? 0} files`,
|
|
370
421
|
);
|
|
422
|
+
|
|
423
|
+
// ── Run structure + analysis phases after native orchestrator ──
|
|
424
|
+
// Structure (directory nodes, contains edges, metrics) is not fully
|
|
425
|
+
// ported to Rust — the native pipeline only handles the small
|
|
426
|
+
// incremental fast path (≤5 changed files). For full builds and
|
|
427
|
+
// larger incremental builds, run JS buildStructure() to fill the gap.
|
|
428
|
+
// Analysis phases (AST, complexity, CFG, dataflow) are also not yet
|
|
429
|
+
// ported; run via JS engine after reconstructing fileSymbols from DB.
|
|
430
|
+
let analysisTiming = { astMs: 0, complexityMs: 0, cfgMs: 0, dataflowMs: 0 };
|
|
431
|
+
let structurePatchMs = 0;
|
|
432
|
+
const needsAnalysis =
|
|
433
|
+
opts.ast !== false ||
|
|
434
|
+
opts.complexity !== false ||
|
|
435
|
+
opts.cfg !== false ||
|
|
436
|
+
opts.dataflow !== false;
|
|
437
|
+
|
|
438
|
+
// The native fast path only runs structure for small incremental
|
|
439
|
+
// builds: !isFullBuild && changedCount <= 5 && existingFileCount > 20.
|
|
440
|
+
// For all other cases (full builds, large incrementals), we must
|
|
441
|
+
// run JS buildStructure() to create directory nodes + contains edges (#804).
|
|
442
|
+
// Always run JS structure — the native fast-path has an additional
|
|
443
|
+
// existingFileCount > 20 guard that isn't reflected in the result JSON,
|
|
444
|
+
// so we can't reliably detect whether native actually ran structure.
|
|
445
|
+
const nativeHandledStructure = false;
|
|
446
|
+
const needsStructure = !nativeHandledStructure;
|
|
447
|
+
|
|
448
|
+
if (needsAnalysis || needsStructure) {
|
|
449
|
+
// WAL handoff: checkpoint through rusqlite, close nativeDb,
|
|
450
|
+
// reopen better-sqlite3 with a fresh page cache (#715, #736).
|
|
451
|
+
try {
|
|
452
|
+
ctx.nativeDb!.exec('PRAGMA wal_checkpoint(TRUNCATE)');
|
|
453
|
+
} catch {
|
|
454
|
+
/* ignore checkpoint errors */
|
|
455
|
+
}
|
|
456
|
+
try {
|
|
457
|
+
ctx.nativeDb!.close();
|
|
458
|
+
} catch {
|
|
459
|
+
/* ignore close errors */
|
|
460
|
+
}
|
|
461
|
+
ctx.nativeDb = undefined;
|
|
462
|
+
try {
|
|
463
|
+
ctx.db.close();
|
|
464
|
+
} catch {
|
|
465
|
+
/* ignore close errors */
|
|
466
|
+
}
|
|
467
|
+
ctx.db = null!; // avoid closeDbPair operating on a stale handle
|
|
468
|
+
try {
|
|
469
|
+
ctx.db = openDb(ctx.dbPath);
|
|
470
|
+
} catch (reopenErr) {
|
|
471
|
+
warn(`Failed to reopen DB after native build: ${(reopenErr as Error).message}`);
|
|
472
|
+
// Native build succeeded but we can't run post-processing — return partial result
|
|
473
|
+
return {
|
|
474
|
+
phases: {
|
|
475
|
+
setupMs: +((p.setupMs ?? 0) + (p.collectMs ?? 0) + (p.detectMs ?? 0)).toFixed(1),
|
|
476
|
+
parseMs: +(p.parseMs ?? 0).toFixed(1),
|
|
477
|
+
insertMs: +(p.insertMs ?? 0).toFixed(1),
|
|
478
|
+
resolveMs: +(p.resolveMs ?? 0).toFixed(1),
|
|
479
|
+
edgesMs: +(p.edgesMs ?? 0).toFixed(1),
|
|
480
|
+
structureMs: +(p.structureMs ?? 0).toFixed(1),
|
|
481
|
+
rolesMs: +(p.rolesMs ?? 0).toFixed(1),
|
|
482
|
+
astMs: 0,
|
|
483
|
+
complexityMs: 0,
|
|
484
|
+
cfgMs: 0,
|
|
485
|
+
dataflowMs: 0,
|
|
486
|
+
finalizeMs: +(p.finalizeMs ?? 0).toFixed(1),
|
|
487
|
+
},
|
|
488
|
+
};
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
// Reconstruct fileSymbols from DB. For structure we need ALL files
|
|
492
|
+
// (to build complete directory tree); for analysis we scope to
|
|
493
|
+
// changed files only. Load all files, then scope analysis later.
|
|
494
|
+
const allFileRows = ctx.db
|
|
495
|
+
.prepare(
|
|
496
|
+
'SELECT file, name, kind, line, end_line as endLine FROM nodes WHERE file IS NOT NULL ORDER BY file, line',
|
|
497
|
+
)
|
|
498
|
+
.all() as {
|
|
499
|
+
file: string;
|
|
500
|
+
name: string;
|
|
501
|
+
kind: string;
|
|
502
|
+
line: number;
|
|
503
|
+
endLine: number | null;
|
|
504
|
+
}[];
|
|
505
|
+
|
|
506
|
+
const allFileSymbols = new Map<string, ExtractorOutput>();
|
|
507
|
+
for (const row of allFileRows) {
|
|
508
|
+
let entry = allFileSymbols.get(row.file);
|
|
509
|
+
if (!entry) {
|
|
510
|
+
entry = {
|
|
511
|
+
definitions: [],
|
|
512
|
+
calls: [],
|
|
513
|
+
imports: [],
|
|
514
|
+
classes: [],
|
|
515
|
+
exports: [],
|
|
516
|
+
typeMap: new Map(),
|
|
517
|
+
};
|
|
518
|
+
allFileSymbols.set(row.file, entry);
|
|
519
|
+
}
|
|
520
|
+
entry.definitions.push({
|
|
521
|
+
name: row.name,
|
|
522
|
+
kind: row.kind as Definition['kind'],
|
|
523
|
+
line: row.line,
|
|
524
|
+
endLine: row.endLine ?? undefined,
|
|
525
|
+
});
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
// Populate import/export counts from DB edges so buildStructure
|
|
529
|
+
// computes correct import_count/export_count in node_metrics.
|
|
530
|
+
// The extractor arrays aren't persisted to the DB, so we derive
|
|
531
|
+
// counts from edge data instead (#804).
|
|
532
|
+
const importCountRows = ctx.db
|
|
533
|
+
.prepare(
|
|
534
|
+
`SELECT n.file, COUNT(*) AS cnt
|
|
535
|
+
FROM edges e JOIN nodes n ON e.source_id = n.id
|
|
536
|
+
WHERE e.kind IN ('imports', 'imports-type', 'dynamic-imports')
|
|
537
|
+
AND n.file IS NOT NULL
|
|
538
|
+
GROUP BY n.file`,
|
|
539
|
+
)
|
|
540
|
+
.all() as { file: string; cnt: number }[];
|
|
541
|
+
for (const row of importCountRows) {
|
|
542
|
+
const entry = allFileSymbols.get(row.file);
|
|
543
|
+
if (entry) entry.imports = new Array(row.cnt) as ExtractorOutput['imports'];
|
|
544
|
+
}
|
|
545
|
+
// Export count: definitions in this file that are imported by other files
|
|
546
|
+
const exportCountRows = ctx.db
|
|
547
|
+
.prepare(
|
|
548
|
+
`SELECT n_tgt.file, COUNT(DISTINCT n_tgt.id) AS cnt
|
|
549
|
+
FROM edges e
|
|
550
|
+
JOIN nodes n_tgt ON e.target_id = n_tgt.id
|
|
551
|
+
JOIN nodes n_src ON e.source_id = n_src.id
|
|
552
|
+
WHERE e.kind IN ('imports', 'imports-type', 'reexports')
|
|
553
|
+
AND n_tgt.file IS NOT NULL
|
|
554
|
+
AND n_src.file != n_tgt.file
|
|
555
|
+
GROUP BY n_tgt.file`,
|
|
556
|
+
)
|
|
557
|
+
.all() as { file: string; cnt: number }[];
|
|
558
|
+
for (const row of exportCountRows) {
|
|
559
|
+
const entry = allFileSymbols.get(row.file);
|
|
560
|
+
if (entry) entry.exports = new Array(row.cnt) as ExtractorOutput['exports'];
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
// ── Structure phase: directory nodes + contains edges (#804) ──
|
|
564
|
+
if (needsStructure) {
|
|
565
|
+
const structureStart = performance.now();
|
|
566
|
+
try {
|
|
567
|
+
// Derive directories from file paths
|
|
568
|
+
const directories = new Set<string>();
|
|
569
|
+
for (const relPath of allFileSymbols.keys()) {
|
|
570
|
+
const parts = relPath.split('/');
|
|
571
|
+
for (let i = 1; i < parts.length; i++) {
|
|
572
|
+
directories.add(parts.slice(0, i).join('/'));
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
// Build line count map from DB metrics or file content
|
|
577
|
+
const lineCountMap = new Map<string, number>();
|
|
578
|
+
const cachedLineCounts = ctx.db
|
|
579
|
+
.prepare(
|
|
580
|
+
`SELECT n.name AS file, m.line_count
|
|
581
|
+
FROM node_metrics m JOIN nodes n ON m.node_id = n.id
|
|
582
|
+
WHERE n.kind = 'file'`,
|
|
583
|
+
)
|
|
584
|
+
.all() as Array<{ file: string; line_count: number }>;
|
|
585
|
+
for (const row of cachedLineCounts) {
|
|
586
|
+
lineCountMap.set(row.file, row.line_count);
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
// Native ran no structure at all — always do a full rebuild so
|
|
590
|
+
// every directory gets nodes + contains edges (#804).
|
|
591
|
+
const changedFilePaths = null;
|
|
592
|
+
|
|
593
|
+
const { buildStructure: buildStructureFn } = (await import(
|
|
594
|
+
'../../../features/structure.js'
|
|
595
|
+
)) as {
|
|
596
|
+
buildStructure: (
|
|
597
|
+
db: typeof ctx.db,
|
|
598
|
+
fileSymbols: Map<string, ExtractorOutput>,
|
|
599
|
+
rootDir: string,
|
|
600
|
+
lineCountMap: Map<string, number>,
|
|
601
|
+
directories: Set<string>,
|
|
602
|
+
changedFiles: string[] | null,
|
|
603
|
+
) => void;
|
|
604
|
+
};
|
|
605
|
+
buildStructureFn(
|
|
606
|
+
ctx.db,
|
|
607
|
+
allFileSymbols,
|
|
608
|
+
ctx.rootDir,
|
|
609
|
+
lineCountMap,
|
|
610
|
+
directories,
|
|
611
|
+
changedFilePaths,
|
|
612
|
+
);
|
|
613
|
+
debug('Structure phase completed after native orchestrator');
|
|
614
|
+
} catch (err) {
|
|
615
|
+
warn(`Structure phase failed after native build: ${toErrorMessage(err)}`);
|
|
616
|
+
}
|
|
617
|
+
structurePatchMs = performance.now() - structureStart;
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
// ── Analysis phase ──
|
|
621
|
+
if (needsAnalysis) {
|
|
622
|
+
// Scope analysis fileSymbols to changed files only
|
|
623
|
+
const changedFiles = result.changedFiles;
|
|
624
|
+
let analysisFileSymbols: Map<string, ExtractorOutput>;
|
|
625
|
+
if (changedFiles && changedFiles.length > 0) {
|
|
626
|
+
analysisFileSymbols = new Map();
|
|
627
|
+
for (const f of changedFiles) {
|
|
628
|
+
const entry = allFileSymbols.get(f);
|
|
629
|
+
if (entry) analysisFileSymbols.set(f, entry);
|
|
630
|
+
}
|
|
631
|
+
} else {
|
|
632
|
+
analysisFileSymbols = allFileSymbols;
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
// Reopen nativeDb for analysis features (suspend/resume WAL pattern).
|
|
636
|
+
const native = loadNative();
|
|
637
|
+
if (native?.NativeDatabase) {
|
|
638
|
+
try {
|
|
639
|
+
ctx.nativeDb = native.NativeDatabase.openReadWrite(ctx.dbPath);
|
|
640
|
+
if (ctx.engineOpts) ctx.engineOpts.nativeDb = ctx.nativeDb;
|
|
641
|
+
} catch {
|
|
642
|
+
ctx.nativeDb = undefined;
|
|
643
|
+
if (ctx.engineOpts) ctx.engineOpts.nativeDb = undefined;
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
try {
|
|
648
|
+
const { runAnalyses: runAnalysesFn } = await import(
|
|
649
|
+
'../../../ast-analysis/engine.js'
|
|
650
|
+
);
|
|
651
|
+
analysisTiming = await runAnalysesFn(
|
|
652
|
+
ctx.db,
|
|
653
|
+
analysisFileSymbols,
|
|
654
|
+
ctx.rootDir,
|
|
655
|
+
opts,
|
|
656
|
+
ctx.engineOpts,
|
|
657
|
+
);
|
|
658
|
+
} catch (err) {
|
|
659
|
+
warn(`Analysis phases failed after native build: ${toErrorMessage(err)}`);
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
// Close nativeDb after analyses
|
|
663
|
+
if (ctx.nativeDb) {
|
|
664
|
+
try {
|
|
665
|
+
ctx.nativeDb.exec('PRAGMA wal_checkpoint(TRUNCATE)');
|
|
666
|
+
} catch {
|
|
667
|
+
/* ignore checkpoint errors */
|
|
668
|
+
}
|
|
669
|
+
try {
|
|
670
|
+
ctx.nativeDb.close();
|
|
671
|
+
} catch {
|
|
672
|
+
/* ignore close errors */
|
|
673
|
+
}
|
|
674
|
+
ctx.nativeDb = undefined;
|
|
675
|
+
if (ctx.engineOpts) ctx.engineOpts.nativeDb = undefined;
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
closeDbPair({ db: ctx.db, nativeDb: ctx.nativeDb });
|
|
371
681
|
return {
|
|
372
682
|
phases: {
|
|
373
683
|
setupMs: +((p.setupMs ?? 0) + (p.collectMs ?? 0) + (p.detectMs ?? 0)).toFixed(1),
|
|
@@ -375,18 +685,18 @@ export async function buildGraph(
|
|
|
375
685
|
insertMs: +(p.insertMs ?? 0).toFixed(1),
|
|
376
686
|
resolveMs: +(p.resolveMs ?? 0).toFixed(1),
|
|
377
687
|
edgesMs: +(p.edgesMs ?? 0).toFixed(1),
|
|
378
|
-
structureMs: +(p.structureMs ?? 0).toFixed(1),
|
|
688
|
+
structureMs: +((p.structureMs ?? 0) + structurePatchMs).toFixed(1),
|
|
379
689
|
rolesMs: +(p.rolesMs ?? 0).toFixed(1),
|
|
380
|
-
astMs: 0,
|
|
381
|
-
complexityMs: 0,
|
|
382
|
-
cfgMs: 0,
|
|
383
|
-
dataflowMs: 0,
|
|
690
|
+
astMs: +(analysisTiming.astMs ?? 0).toFixed(1),
|
|
691
|
+
complexityMs: +(analysisTiming.complexityMs ?? 0).toFixed(1),
|
|
692
|
+
cfgMs: +(analysisTiming.cfgMs ?? 0).toFixed(1),
|
|
693
|
+
dataflowMs: +(analysisTiming.dataflowMs ?? 0).toFixed(1),
|
|
384
694
|
finalizeMs: +(p.finalizeMs ?? 0).toFixed(1),
|
|
385
695
|
},
|
|
386
696
|
};
|
|
387
697
|
} catch (err) {
|
|
388
698
|
warn(
|
|
389
|
-
`Native build orchestrator failed, falling back to JS pipeline: ${(err
|
|
699
|
+
`Native build orchestrator failed, falling back to JS pipeline: ${toErrorMessage(err)}`,
|
|
390
700
|
);
|
|
391
701
|
// Fall through to JS pipeline
|
|
392
702
|
}
|