@optave/codegraph 3.5.0 → 3.7.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 +47 -21
- package/dist/ast-analysis/engine.d.ts.map +1 -1
- package/dist/ast-analysis/engine.js +119 -127
- package/dist/ast-analysis/engine.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 +14 -1
- 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 +11 -13
- package/dist/ast-analysis/visitors/complexity-visitor.js.map +1 -1
- package/dist/db/connection.d.ts +12 -2
- package/dist/db/connection.d.ts.map +1 -1
- package/dist/db/connection.js +81 -53
- package/dist/db/connection.js.map +1 -1
- package/dist/db/index.d.ts +1 -1
- package/dist/db/index.d.ts.map +1 -1
- package/dist/db/index.js +1 -1
- package/dist/db/index.js.map +1 -1
- package/dist/db/migrations.d.ts.map +1 -1
- package/dist/db/migrations.js +38 -32
- package/dist/db/migrations.js.map +1 -1
- package/dist/domain/analysis/context.d.ts.map +1 -1
- package/dist/domain/analysis/context.js +51 -66
- 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 +62 -70
- package/dist/domain/analysis/dependencies.js.map +1 -1
- package/dist/domain/analysis/diff-impact.d.ts +9 -7
- package/dist/domain/analysis/diff-impact.d.ts.map +1 -1
- package/dist/domain/analysis/exports.d.ts.map +1 -1
- package/dist/domain/analysis/exports.js +29 -33
- package/dist/domain/analysis/exports.js.map +1 -1
- package/dist/domain/analysis/fn-impact.d.ts +15 -17
- package/dist/domain/analysis/fn-impact.d.ts.map +1 -1
- package/dist/domain/analysis/fn-impact.js +35 -65
- 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 +91 -6
- package/dist/domain/analysis/module-map.js.map +1 -1
- package/dist/domain/analysis/query-helpers.d.ts +20 -0
- package/dist/domain/analysis/query-helpers.d.ts.map +1 -0
- package/dist/domain/analysis/query-helpers.js +27 -0
- package/dist/domain/analysis/query-helpers.js.map +1 -0
- package/dist/domain/graph/builder/helpers.d.ts.map +1 -1
- package/dist/domain/graph/builder/helpers.js +15 -9
- package/dist/domain/graph/builder/helpers.js.map +1 -1
- package/dist/domain/graph/builder/incremental.d.ts.map +1 -1
- package/dist/domain/graph/builder/incremental.js +3 -2
- package/dist/domain/graph/builder/incremental.js.map +1 -1
- package/dist/domain/graph/builder/pipeline.d.ts.map +1 -1
- package/dist/domain/graph/builder/pipeline.js +69 -3
- 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 +7 -51
- 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 +7 -5
- package/dist/domain/graph/builder/stages/build-structure.js.map +1 -1
- package/dist/domain/graph/builder/stages/collect-files.js +2 -2
- package/dist/domain/graph/builder/stages/collect-files.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 +2 -2
- 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 +124 -105
- 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 +28 -15
- 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 +3 -2
- package/dist/domain/graph/builder/stages/resolve-imports.js.map +1 -1
- package/dist/domain/graph/resolve.d.ts +0 -4
- package/dist/domain/graph/resolve.d.ts.map +1 -1
- package/dist/domain/graph/resolve.js +32 -48
- package/dist/domain/graph/resolve.js.map +1 -1
- package/dist/domain/graph/watcher.d.ts.map +1 -1
- package/dist/domain/graph/watcher.js +12 -12
- package/dist/domain/graph/watcher.js.map +1 -1
- package/dist/domain/parser.d.ts +1 -1
- package/dist/domain/parser.d.ts.map +1 -1
- package/dist/domain/parser.js +206 -101
- package/dist/domain/parser.js.map +1 -1
- package/dist/domain/search/search/cli-formatter.d.ts.map +1 -1
- package/dist/domain/search/search/cli-formatter.js +88 -83
- package/dist/domain/search/search/cli-formatter.js.map +1 -1
- package/dist/extractors/bash.d.ts +6 -0
- package/dist/extractors/bash.d.ts.map +1 -0
- package/dist/extractors/bash.js +91 -0
- package/dist/extractors/bash.js.map +1 -0
- package/dist/extractors/c.d.ts +6 -0
- package/dist/extractors/c.d.ts.map +1 -0
- package/dist/extractors/c.js +204 -0
- package/dist/extractors/c.js.map +1 -0
- package/dist/extractors/cpp.d.ts +6 -0
- package/dist/extractors/cpp.d.ts.map +1 -0
- package/dist/extractors/cpp.js +283 -0
- package/dist/extractors/cpp.js.map +1 -0
- package/dist/extractors/csharp.d.ts.map +1 -1
- package/dist/extractors/csharp.js +42 -54
- package/dist/extractors/csharp.js.map +1 -1
- package/dist/extractors/dart.d.ts +6 -0
- package/dist/extractors/dart.d.ts.map +1 -0
- package/dist/extractors/dart.js +277 -0
- package/dist/extractors/dart.js.map +1 -0
- package/dist/extractors/elixir.d.ts +9 -0
- package/dist/extractors/elixir.d.ts.map +1 -0
- package/dist/extractors/elixir.js +223 -0
- package/dist/extractors/elixir.js.map +1 -0
- package/dist/extractors/go.d.ts.map +1 -1
- package/dist/extractors/go.js +126 -130
- package/dist/extractors/go.js.map +1 -1
- package/dist/extractors/haskell.d.ts +8 -0
- package/dist/extractors/haskell.d.ts.map +1 -0
- package/dist/extractors/haskell.js +217 -0
- package/dist/extractors/haskell.js.map +1 -0
- package/dist/extractors/hcl.js +6 -6
- package/dist/extractors/hcl.js.map +1 -1
- package/dist/extractors/helpers.d.ts +32 -1
- package/dist/extractors/helpers.d.ts.map +1 -1
- package/dist/extractors/helpers.js +74 -0
- package/dist/extractors/helpers.js.map +1 -1
- package/dist/extractors/index.d.ts +12 -0
- package/dist/extractors/index.d.ts.map +1 -1
- package/dist/extractors/index.js +12 -0
- package/dist/extractors/index.js.map +1 -1
- package/dist/extractors/java.d.ts.map +1 -1
- package/dist/extractors/java.js +32 -47
- package/dist/extractors/java.js.map +1 -1
- package/dist/extractors/javascript.d.ts.map +1 -1
- package/dist/extractors/javascript.js +306 -292
- package/dist/extractors/javascript.js.map +1 -1
- package/dist/extractors/kotlin.d.ts +6 -0
- package/dist/extractors/kotlin.d.ts.map +1 -0
- package/dist/extractors/kotlin.js +275 -0
- package/dist/extractors/kotlin.js.map +1 -0
- package/dist/extractors/lua.d.ts +6 -0
- package/dist/extractors/lua.d.ts.map +1 -0
- package/dist/extractors/lua.js +162 -0
- package/dist/extractors/lua.js.map +1 -0
- package/dist/extractors/ocaml.d.ts +6 -0
- package/dist/extractors/ocaml.d.ts.map +1 -0
- package/dist/extractors/ocaml.js +236 -0
- package/dist/extractors/ocaml.js.map +1 -0
- package/dist/extractors/php.d.ts.map +1 -1
- package/dist/extractors/php.js +39 -44
- package/dist/extractors/php.js.map +1 -1
- package/dist/extractors/python.d.ts.map +1 -1
- package/dist/extractors/python.js +75 -93
- package/dist/extractors/python.js.map +1 -1
- package/dist/extractors/ruby.js +6 -13
- package/dist/extractors/ruby.js.map +1 -1
- package/dist/extractors/rust.d.ts.map +1 -1
- package/dist/extractors/rust.js +58 -83
- package/dist/extractors/rust.js.map +1 -1
- package/dist/extractors/scala.d.ts +6 -0
- package/dist/extractors/scala.d.ts.map +1 -0
- package/dist/extractors/scala.js +269 -0
- package/dist/extractors/scala.js.map +1 -0
- package/dist/extractors/swift.d.ts +6 -0
- package/dist/extractors/swift.d.ts.map +1 -0
- package/dist/extractors/swift.js +275 -0
- package/dist/extractors/swift.js.map +1 -0
- package/dist/extractors/zig.d.ts +9 -0
- package/dist/extractors/zig.d.ts.map +1 -0
- package/dist/extractors/zig.js +276 -0
- package/dist/extractors/zig.js.map +1 -0
- package/dist/features/ast.d.ts +2 -0
- package/dist/features/ast.d.ts.map +1 -1
- package/dist/features/ast.js +9 -24
- package/dist/features/ast.js.map +1 -1
- package/dist/features/audit.d.ts.map +1 -1
- package/dist/features/audit.js +17 -21
- package/dist/features/audit.js.map +1 -1
- package/dist/features/branch-compare.d.ts.map +1 -1
- package/dist/features/branch-compare.js +47 -3
- package/dist/features/branch-compare.js.map +1 -1
- package/dist/features/cfg.d.ts +7 -1
- package/dist/features/cfg.d.ts.map +1 -1
- package/dist/features/cfg.js +72 -61
- package/dist/features/cfg.js.map +1 -1
- package/dist/features/check.d.ts.map +1 -1
- package/dist/features/check.js +79 -62
- package/dist/features/check.js.map +1 -1
- package/dist/features/complexity-query.d.ts.map +1 -1
- package/dist/features/complexity-query.js +142 -137
- package/dist/features/complexity-query.js.map +1 -1
- package/dist/features/complexity.d.ts +7 -1
- package/dist/features/complexity.d.ts.map +1 -1
- package/dist/features/complexity.js +62 -1
- package/dist/features/complexity.js.map +1 -1
- package/dist/features/dataflow.d.ts +7 -1
- package/dist/features/dataflow.d.ts.map +1 -1
- package/dist/features/dataflow.js +356 -188
- package/dist/features/dataflow.js.map +1 -1
- package/dist/features/graph-enrichment.d.ts.map +1 -1
- package/dist/features/graph-enrichment.js +117 -104
- package/dist/features/graph-enrichment.js.map +1 -1
- package/dist/features/sequence.d.ts.map +1 -1
- package/dist/features/sequence.js +25 -4
- package/dist/features/sequence.js.map +1 -1
- package/dist/features/structure-query.d.ts.map +1 -1
- package/dist/features/structure-query.js +29 -4
- package/dist/features/structure-query.js.map +1 -1
- package/dist/features/structure.d.ts.map +1 -1
- package/dist/features/structure.js +35 -15
- package/dist/features/structure.js.map +1 -1
- package/dist/graph/algorithms/leiden/adapter.d.ts.map +1 -1
- package/dist/graph/algorithms/leiden/adapter.js +88 -73
- package/dist/graph/algorithms/leiden/adapter.js.map +1 -1
- package/dist/graph/algorithms/leiden/index.js +43 -28
- package/dist/graph/algorithms/leiden/index.js.map +1 -1
- package/dist/graph/algorithms/leiden/optimiser.d.ts.map +1 -1
- package/dist/graph/algorithms/leiden/optimiser.js +90 -104
- 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 +89 -106
- package/dist/graph/algorithms/leiden/partition.js.map +1 -1
- package/dist/graph/model.d.ts +2 -0
- package/dist/graph/model.d.ts.map +1 -1
- package/dist/graph/model.js +20 -8
- package/dist/graph/model.js.map +1 -1
- package/dist/infrastructure/config.d.ts +0 -8
- package/dist/infrastructure/config.d.ts.map +1 -1
- package/dist/infrastructure/config.js +73 -62
- package/dist/infrastructure/config.js.map +1 -1
- package/dist/infrastructure/registry.d.ts +0 -8
- package/dist/infrastructure/registry.d.ts.map +1 -1
- package/dist/infrastructure/registry.js +12 -14
- package/dist/infrastructure/registry.js.map +1 -1
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +45 -36
- package/dist/mcp/server.js.map +1 -1
- package/dist/presentation/audit.d.ts.map +1 -1
- package/dist/presentation/audit.js +61 -57
- package/dist/presentation/audit.js.map +1 -1
- package/dist/presentation/branch-compare.d.ts.map +1 -1
- package/dist/presentation/branch-compare.js +56 -38
- package/dist/presentation/branch-compare.js.map +1 -1
- package/dist/presentation/check.d.ts.map +1 -1
- package/dist/presentation/check.js +30 -32
- package/dist/presentation/check.js.map +1 -1
- package/dist/presentation/colors.d.ts.map +1 -1
- package/dist/presentation/colors.js +2 -0
- package/dist/presentation/colors.js.map +1 -1
- package/dist/presentation/complexity.d.ts.map +1 -1
- package/dist/presentation/complexity.js +25 -19
- package/dist/presentation/complexity.js.map +1 -1
- package/dist/presentation/queries-cli/exports.d.ts.map +1 -1
- package/dist/presentation/queries-cli/exports.js +15 -15
- 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 +29 -19
- package/dist/presentation/queries-cli/impact.js.map +1 -1
- package/dist/types.d.ts +182 -7
- package/dist/types.d.ts.map +1 -1
- package/grammars/tree-sitter-bash.wasm +0 -0
- package/grammars/tree-sitter-c.wasm +0 -0
- package/grammars/tree-sitter-cpp.wasm +0 -0
- package/grammars/tree-sitter-dart.wasm +0 -0
- package/grammars/tree-sitter-elixir.wasm +0 -0
- package/grammars/tree-sitter-haskell.wasm +0 -0
- package/grammars/tree-sitter-kotlin.wasm +0 -0
- package/grammars/tree-sitter-lua.wasm +0 -0
- package/grammars/tree-sitter-ocaml.wasm +0 -0
- package/grammars/tree-sitter-scala.wasm +0 -0
- package/grammars/tree-sitter-swift.wasm +0 -0
- package/grammars/tree-sitter-zig.wasm +0 -0
- package/package.json +19 -7
- package/src/ast-analysis/engine.ts +147 -138
- package/src/ast-analysis/visitors/ast-store-visitor.ts +15 -2
- package/src/ast-analysis/visitors/complexity-visitor.ts +11 -11
- package/src/db/connection.ts +90 -59
- package/src/db/index.ts +1 -0
- package/src/db/migrations.ts +36 -32
- package/src/domain/analysis/context.ts +73 -75
- package/src/domain/analysis/dependencies.ts +78 -68
- package/src/domain/analysis/exports.ts +45 -34
- package/src/domain/analysis/fn-impact.ts +67 -64
- package/src/domain/analysis/module-map.ts +103 -8
- package/src/domain/analysis/query-helpers.ts +35 -0
- package/src/domain/graph/builder/helpers.ts +12 -6
- package/src/domain/graph/builder/incremental.ts +3 -2
- package/src/domain/graph/builder/pipeline.ts +71 -3
- package/src/domain/graph/builder/stages/build-edges.ts +10 -75
- package/src/domain/graph/builder/stages/build-structure.ts +9 -7
- package/src/domain/graph/builder/stages/collect-files.ts +2 -2
- package/src/domain/graph/builder/stages/detect-changes.ts +7 -2
- package/src/domain/graph/builder/stages/finalize.ts +159 -125
- package/src/domain/graph/builder/stages/insert-nodes.ts +32 -21
- package/src/domain/graph/builder/stages/resolve-imports.ts +3 -2
- package/src/domain/graph/resolve.ts +34 -46
- package/src/domain/graph/watcher.ts +12 -14
- package/src/domain/parser.ts +222 -97
- package/src/domain/search/search/cli-formatter.ts +121 -94
- package/src/extractors/bash.ts +97 -0
- package/src/extractors/c.ts +212 -0
- package/src/extractors/cpp.ts +298 -0
- package/src/extractors/csharp.ts +53 -56
- package/src/extractors/dart.ts +304 -0
- package/src/extractors/elixir.ts +251 -0
- package/src/extractors/go.ts +152 -134
- package/src/extractors/haskell.ts +235 -0
- package/src/extractors/hcl.ts +6 -6
- package/src/extractors/helpers.ts +93 -1
- package/src/extractors/index.ts +12 -0
- package/src/extractors/java.ts +43 -48
- package/src/extractors/javascript.ts +328 -281
- package/src/extractors/kotlin.ts +293 -0
- package/src/extractors/lua.ts +169 -0
- package/src/extractors/ocaml.ts +259 -0
- package/src/extractors/php.ts +46 -40
- package/src/extractors/python.ts +81 -104
- package/src/extractors/ruby.ts +6 -13
- package/src/extractors/rust.ts +65 -85
- package/src/extractors/scala.ts +285 -0
- package/src/extractors/swift.ts +293 -0
- package/src/extractors/zig.ts +294 -0
- package/src/features/ast.ts +10 -25
- package/src/features/audit.ts +24 -20
- package/src/features/branch-compare.ts +51 -4
- package/src/features/cfg.ts +113 -65
- package/src/features/check.ts +90 -74
- package/src/features/complexity-query.ts +181 -163
- package/src/features/complexity.ts +64 -1
- package/src/features/dataflow.ts +462 -217
- package/src/features/graph-enrichment.ts +161 -117
- package/src/features/sequence.ts +27 -4
- package/src/features/structure-query.ts +43 -4
- package/src/features/structure.ts +50 -22
- package/src/graph/algorithms/leiden/adapter.ts +126 -71
- package/src/graph/algorithms/leiden/index.ts +67 -28
- package/src/graph/algorithms/leiden/optimiser.ts +114 -105
- package/src/graph/algorithms/leiden/partition.ts +131 -98
- package/src/graph/model.ts +19 -7
- package/src/infrastructure/config.ts +60 -58
- package/src/infrastructure/registry.ts +17 -14
- package/src/mcp/server.ts +46 -37
- package/src/presentation/audit.ts +72 -67
- package/src/presentation/branch-compare.ts +54 -50
- package/src/presentation/check.ts +34 -34
- package/src/presentation/colors.ts +2 -0
- package/src/presentation/complexity.ts +39 -33
- package/src/presentation/queries-cli/exports.ts +17 -17
- package/src/presentation/queries-cli/impact.ts +30 -22
- package/src/types.ts +195 -7
|
@@ -17,12 +17,8 @@ import { CODEGRAPH_VERSION } from '../../../../shared/version.js';
|
|
|
17
17
|
import { writeJournalHeader } from '../../journal.js';
|
|
18
18
|
import type { PipelineContext } from '../context.js';
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
const t0 = performance.now();
|
|
24
|
-
|
|
25
|
-
// Release cached WASM trees
|
|
20
|
+
/** Release cached WASM parse trees to free memory. */
|
|
21
|
+
function releaseWasmTrees(allSymbols: PipelineContext['allSymbols']): void {
|
|
26
22
|
for (const [, symbols] of allSymbols) {
|
|
27
23
|
const tree = symbols._tree as { delete?: () => void } | undefined;
|
|
28
24
|
if (tree && typeof tree.delete === 'function') {
|
|
@@ -35,133 +31,141 @@ export async function finalize(ctx: PipelineContext): Promise<void> {
|
|
|
35
31
|
symbols._tree = undefined;
|
|
36
32
|
symbols._langId = undefined;
|
|
37
33
|
}
|
|
34
|
+
}
|
|
38
35
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
36
|
+
/**
|
|
37
|
+
* Detect significant drift between current and previous node/edge counts.
|
|
38
|
+
* Skipped for small incremental changes where count fluctuation is expected.
|
|
39
|
+
*/
|
|
40
|
+
function detectIncrementalDrift(
|
|
41
|
+
ctx: PipelineContext,
|
|
42
|
+
nodeCount: number,
|
|
43
|
+
actualEdgeCount: number,
|
|
44
|
+
): void {
|
|
45
|
+
const { db, allSymbols, config } = ctx;
|
|
46
|
+
const useNativeDb = ctx.engineName === 'native' && !!ctx.nativeDb;
|
|
47
|
+
if (ctx.isFullBuild || allSymbols.size <= 3) return;
|
|
42
48
|
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
49
|
+
const prevNodes = useNativeDb
|
|
50
|
+
? ctx.nativeDb!.getBuildMeta('node_count')
|
|
51
|
+
: getBuildMeta(db, 'node_count');
|
|
52
|
+
const prevEdges = useNativeDb
|
|
53
|
+
? ctx.nativeDb!.getBuildMeta('edge_count')
|
|
54
|
+
: getBuildMeta(db, 'edge_count');
|
|
55
|
+
if (!prevNodes || !prevEdges) return;
|
|
47
56
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
if (
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
if (prevN > 0) {
|
|
61
|
-
const nodeDrift = Math.abs(nodeCount - prevN) / prevN;
|
|
62
|
-
const edgeDrift = prevE > 0 ? Math.abs(actualEdgeCount - prevE) / prevE : 0;
|
|
63
|
-
const driftThreshold =
|
|
64
|
-
(config as { build?: { driftThreshold?: number } }).build?.driftThreshold ?? 0.2;
|
|
65
|
-
if (nodeDrift > driftThreshold || edgeDrift > driftThreshold) {
|
|
66
|
-
warn(
|
|
67
|
-
`Incremental build diverged significantly from previous counts (nodes: ${prevN}\u2192${nodeCount} [${(nodeDrift * 100).toFixed(1)}%], edges: ${prevE}\u2192${actualEdgeCount} [${(edgeDrift * 100).toFixed(1)}%], threshold: ${(driftThreshold * 100).toFixed(0)}%). Consider rebuilding with --no-incremental.`,
|
|
68
|
-
);
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
}
|
|
57
|
+
const prevN = Number(prevNodes);
|
|
58
|
+
const prevE = Number(prevEdges);
|
|
59
|
+
if (prevN <= 0) return;
|
|
60
|
+
|
|
61
|
+
const nodeDrift = Math.abs(nodeCount - prevN) / prevN;
|
|
62
|
+
const edgeDrift = prevE > 0 ? Math.abs(actualEdgeCount - prevE) / prevE : 0;
|
|
63
|
+
const driftThreshold =
|
|
64
|
+
(config as { build?: { driftThreshold?: number } }).build?.driftThreshold ?? 0.2;
|
|
65
|
+
if (nodeDrift > driftThreshold || edgeDrift > driftThreshold) {
|
|
66
|
+
warn(
|
|
67
|
+
`Incremental build diverged significantly from previous counts (nodes: ${prevN}\u2192${nodeCount} [${(nodeDrift * 100).toFixed(1)}%], edges: ${prevE}\u2192${actualEdgeCount} [${(edgeDrift * 100).toFixed(1)}%], threshold: ${(driftThreshold * 100).toFixed(0)}%). Consider rebuilding with --no-incremental.`,
|
|
68
|
+
);
|
|
72
69
|
}
|
|
70
|
+
}
|
|
73
71
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
built_at: buildNow.toISOString(),
|
|
91
|
-
node_count: String(nodeCount),
|
|
92
|
-
edge_count: String(actualEdgeCount),
|
|
93
|
-
}).map(([key, value]) => ({ key, value: String(value) })),
|
|
94
|
-
);
|
|
95
|
-
} else {
|
|
96
|
-
setBuildMeta(db, {
|
|
72
|
+
/**
|
|
73
|
+
* Persist build metadata (engine, version, counts, timestamp).
|
|
74
|
+
* Skipped for small incremental builds to avoid WAL fsync cost.
|
|
75
|
+
*/
|
|
76
|
+
function persistBuildMetadata(
|
|
77
|
+
ctx: PipelineContext,
|
|
78
|
+
nodeCount: number,
|
|
79
|
+
actualEdgeCount: number,
|
|
80
|
+
buildNow: Date,
|
|
81
|
+
): void {
|
|
82
|
+
const useNativeDb = ctx.engineName === 'native' && !!ctx.nativeDb;
|
|
83
|
+
if (!ctx.isFullBuild && ctx.allSymbols.size <= 3) return;
|
|
84
|
+
try {
|
|
85
|
+
if (useNativeDb) {
|
|
86
|
+
ctx.nativeDb!.setBuildMeta(
|
|
87
|
+
Object.entries({
|
|
97
88
|
engine: ctx.engineName,
|
|
98
89
|
engine_version: ctx.engineVersion || '',
|
|
99
90
|
codegraph_version: CODEGRAPH_VERSION,
|
|
100
|
-
schema_version: String(schemaVersion),
|
|
91
|
+
schema_version: String(ctx.schemaVersion),
|
|
101
92
|
built_at: buildNow.toISOString(),
|
|
102
|
-
node_count: nodeCount,
|
|
103
|
-
edge_count: actualEdgeCount,
|
|
104
|
-
})
|
|
105
|
-
|
|
106
|
-
}
|
|
107
|
-
|
|
93
|
+
node_count: String(nodeCount),
|
|
94
|
+
edge_count: String(actualEdgeCount),
|
|
95
|
+
}).map(([key, value]) => ({ key, value: String(value) })),
|
|
96
|
+
);
|
|
97
|
+
} else {
|
|
98
|
+
setBuildMeta(ctx.db, {
|
|
99
|
+
engine: ctx.engineName,
|
|
100
|
+
engine_version: ctx.engineVersion || '',
|
|
101
|
+
codegraph_version: CODEGRAPH_VERSION,
|
|
102
|
+
schema_version: String(ctx.schemaVersion),
|
|
103
|
+
built_at: buildNow.toISOString(),
|
|
104
|
+
node_count: nodeCount,
|
|
105
|
+
edge_count: actualEdgeCount,
|
|
106
|
+
});
|
|
108
107
|
}
|
|
108
|
+
} catch (err) {
|
|
109
|
+
warn(`Failed to write build metadata: ${(err as Error).message}`);
|
|
109
110
|
}
|
|
111
|
+
}
|
|
110
112
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
/* ignore - embeddings table may have been dropped */
|
|
113
|
+
/**
|
|
114
|
+
* Run advisory checks on full builds: orphaned embeddings, stale embeddings,
|
|
115
|
+
* and unused exports. Informational only — does not affect correctness.
|
|
116
|
+
*/
|
|
117
|
+
function runAdvisoryChecks(
|
|
118
|
+
db: PipelineContext['db'],
|
|
119
|
+
hasEmbeddings: boolean,
|
|
120
|
+
buildNow: Date,
|
|
121
|
+
): void {
|
|
122
|
+
// Orphaned embeddings warning
|
|
123
|
+
if (hasEmbeddings) {
|
|
124
|
+
try {
|
|
125
|
+
const orphaned = (
|
|
126
|
+
db
|
|
127
|
+
.prepare(
|
|
128
|
+
'SELECT COUNT(*) as c FROM embeddings WHERE node_id NOT IN (SELECT id FROM nodes)',
|
|
129
|
+
)
|
|
130
|
+
.get() as { c: number }
|
|
131
|
+
).c;
|
|
132
|
+
if (orphaned > 0) {
|
|
133
|
+
warn(
|
|
134
|
+
`${orphaned} embeddings are orphaned (nodes changed). Run "codegraph embed" to refresh.`,
|
|
135
|
+
);
|
|
135
136
|
}
|
|
137
|
+
} catch {
|
|
138
|
+
/* ignore - embeddings table may have been dropped */
|
|
136
139
|
}
|
|
140
|
+
}
|
|
137
141
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
}
|
|
142
|
+
// Stale embeddings warning (built before current graph rebuild)
|
|
143
|
+
if (hasEmbeddings) {
|
|
144
|
+
try {
|
|
145
|
+
const embedBuiltAt = (
|
|
146
|
+
db.prepare("SELECT value FROM embedding_meta WHERE key = 'built_at'").get() as
|
|
147
|
+
| { value: string }
|
|
148
|
+
| undefined
|
|
149
|
+
)?.value;
|
|
150
|
+
if (embedBuiltAt) {
|
|
151
|
+
const embedTime = new Date(embedBuiltAt).getTime();
|
|
152
|
+
if (!Number.isNaN(embedTime) && embedTime < buildNow.getTime()) {
|
|
153
|
+
warn(
|
|
154
|
+
'Embeddings were built before the last graph rebuild. Run "codegraph embed" to update.',
|
|
155
|
+
);
|
|
153
156
|
}
|
|
154
|
-
} catch {
|
|
155
|
-
/* ignore - embedding_meta table may not exist */
|
|
156
157
|
}
|
|
158
|
+
} catch {
|
|
159
|
+
/* ignore - embedding_meta table may not exist */
|
|
157
160
|
}
|
|
161
|
+
}
|
|
158
162
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
163
|
+
// Unused exports warning
|
|
164
|
+
try {
|
|
165
|
+
const unusedCount = (
|
|
166
|
+
db
|
|
167
|
+
.prepare(
|
|
168
|
+
`SELECT COUNT(*) as c FROM nodes
|
|
165
169
|
WHERE exported = 1 AND kind != 'file'
|
|
166
170
|
AND id NOT IN (
|
|
167
171
|
SELECT DISTINCT e.target_id FROM edges e
|
|
@@ -169,17 +173,47 @@ export async function finalize(ctx: PipelineContext): Promise<void> {
|
|
|
169
173
|
JOIN nodes target ON e.target_id = target.id
|
|
170
174
|
WHERE e.kind = 'calls' AND caller.file != target.file
|
|
171
175
|
)`,
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
}
|
|
180
|
-
} catch {
|
|
181
|
-
/* exported column may not exist on older DBs */
|
|
176
|
+
)
|
|
177
|
+
.get() as { c: number }
|
|
178
|
+
).c;
|
|
179
|
+
if (unusedCount > 0) {
|
|
180
|
+
warn(
|
|
181
|
+
`${unusedCount} exported symbol${unusedCount > 1 ? 's have' : ' has'} zero cross-file consumers. Run "codegraph exports <file> --unused" to inspect.`,
|
|
182
|
+
);
|
|
182
183
|
}
|
|
184
|
+
} catch {
|
|
185
|
+
/* exported column may not exist on older DBs */
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
export async function finalize(ctx: PipelineContext): Promise<void> {
|
|
190
|
+
const { allSymbols, rootDir, isFullBuild, hasEmbeddings, opts } = ctx;
|
|
191
|
+
|
|
192
|
+
const t0 = performance.now();
|
|
193
|
+
|
|
194
|
+
releaseWasmTrees(allSymbols);
|
|
195
|
+
|
|
196
|
+
// Capture a single wall-clock timestamp for the current build — used for
|
|
197
|
+
// both the stale-embeddings comparison and the persisted built_at metadata.
|
|
198
|
+
const buildNow = new Date();
|
|
199
|
+
|
|
200
|
+
const nodeCount = (ctx.db.prepare('SELECT COUNT(*) as c FROM nodes').get() as { c: number }).c;
|
|
201
|
+
const actualEdgeCount = (ctx.db.prepare('SELECT COUNT(*) as c FROM edges').get() as { c: number })
|
|
202
|
+
.c;
|
|
203
|
+
info(`Graph built: ${nodeCount} nodes, ${actualEdgeCount} edges`);
|
|
204
|
+
info(`Stored in ${ctx.dbPath}`);
|
|
205
|
+
|
|
206
|
+
detectIncrementalDrift(ctx, nodeCount, actualEdgeCount);
|
|
207
|
+
persistBuildMetadata(ctx, nodeCount, actualEdgeCount, buildNow);
|
|
208
|
+
|
|
209
|
+
// Skip expensive advisory queries for incremental builds — these are
|
|
210
|
+
// informational warnings that don't affect correctness and cost ~40-60ms.
|
|
211
|
+
if (!isFullBuild) {
|
|
212
|
+
debug(
|
|
213
|
+
'Finalize: skipping advisory queries (orphaned/stale embeddings, unused exports) for incremental build',
|
|
214
|
+
);
|
|
215
|
+
} else {
|
|
216
|
+
runAdvisoryChecks(ctx.db, hasEmbeddings, buildNow);
|
|
183
217
|
}
|
|
184
218
|
|
|
185
219
|
// Intentionally measured before closeDb / writeJournalHeader / auto-registration:
|
|
@@ -192,7 +226,7 @@ export async function finalize(ctx: PipelineContext): Promise<void> {
|
|
|
192
226
|
// For small incremental builds, defer the expensive WAL checkpoint to the
|
|
193
227
|
// next event loop tick. Skip for temp directories (tests) — they rmSync
|
|
194
228
|
// immediately after build.
|
|
195
|
-
const pair = { db, nativeDb: ctx.nativeDb };
|
|
229
|
+
const pair = { db: ctx.db, nativeDb: ctx.nativeDb };
|
|
196
230
|
const isTempDir = path.resolve(rootDir).startsWith(path.resolve(tmpdir()));
|
|
197
231
|
if (!isFullBuild && allSymbols.size <= 5 && !isTempDir) {
|
|
198
232
|
closeDbPairDeferred(pair);
|
|
@@ -39,6 +39,12 @@ interface PrecomputedFileData {
|
|
|
39
39
|
// ── Native fast-path ─────────────────────────────────────────────────
|
|
40
40
|
|
|
41
41
|
function tryNativeInsert(ctx: PipelineContext): boolean {
|
|
42
|
+
// Disabled: bulkInsertNodes corrupts the DB when both the JS (better-sqlite3)
|
|
43
|
+
// and Rust (rusqlite) connections are open to the same WAL-mode file.
|
|
44
|
+
// The native path was never operational before — it always crashed on null
|
|
45
|
+
// visibility serialisation. See #696 for the dual-connection fix.
|
|
46
|
+
if (ctx.db) return false;
|
|
47
|
+
|
|
42
48
|
// Use NativeDatabase persistent connection (Phase 6.15+).
|
|
43
49
|
// Standalone napi functions were removed in 6.17 — falls through to JS if nativeDb unavailable.
|
|
44
50
|
if (!ctx.nativeDb?.bulkInsertNodes) return false;
|
|
@@ -52,14 +58,14 @@ function tryNativeInsert(ctx: PipelineContext): boolean {
|
|
|
52
58
|
name: string;
|
|
53
59
|
kind: string;
|
|
54
60
|
line: number;
|
|
55
|
-
endLine?: number
|
|
56
|
-
visibility?: string
|
|
61
|
+
endLine?: number;
|
|
62
|
+
visibility?: string;
|
|
57
63
|
children: Array<{
|
|
58
64
|
name: string;
|
|
59
65
|
kind: string;
|
|
60
66
|
line: number;
|
|
61
|
-
endLine?: number
|
|
62
|
-
visibility?: string
|
|
67
|
+
endLine?: number;
|
|
68
|
+
visibility?: string;
|
|
63
69
|
}>;
|
|
64
70
|
}>;
|
|
65
71
|
exports: Array<{ name: string; kind: string; line: number }>;
|
|
@@ -72,14 +78,14 @@ function tryNativeInsert(ctx: PipelineContext): boolean {
|
|
|
72
78
|
name: def.name,
|
|
73
79
|
kind: def.kind,
|
|
74
80
|
line: def.line,
|
|
75
|
-
endLine: def.endLine ??
|
|
76
|
-
visibility: def.visibility ??
|
|
81
|
+
endLine: def.endLine ?? undefined,
|
|
82
|
+
visibility: def.visibility ?? undefined,
|
|
77
83
|
children: (def.children ?? []).map((c) => ({
|
|
78
84
|
name: c.name,
|
|
79
85
|
kind: c.kind,
|
|
80
86
|
line: c.line,
|
|
81
|
-
endLine: c.endLine ??
|
|
82
|
-
visibility: c.visibility ??
|
|
87
|
+
endLine: c.endLine ?? undefined,
|
|
88
|
+
visibility: c.visibility ?? undefined,
|
|
83
89
|
})),
|
|
84
90
|
})),
|
|
85
91
|
exports: symbols.exports.map((exp) => ({
|
|
@@ -138,7 +144,7 @@ function tryNativeInsert(ctx: PipelineContext): boolean {
|
|
|
138
144
|
fileHashes.push({ file: item.relPath, hash: item.hash, mtime, size });
|
|
139
145
|
}
|
|
140
146
|
|
|
141
|
-
return ctx.nativeDb
|
|
147
|
+
return ctx.nativeDb!.bulkInsertNodes(batches, fileHashes, removed);
|
|
142
148
|
}
|
|
143
149
|
|
|
144
150
|
// ── JS fallback: Phase 1 ────────────────────────────────────────────
|
|
@@ -330,7 +336,7 @@ function updateFileHashes(
|
|
|
330
336
|
// ── Main entry point ────────────────────────────────────────────────
|
|
331
337
|
|
|
332
338
|
export async function insertNodes(ctx: PipelineContext): Promise<void> {
|
|
333
|
-
const {
|
|
339
|
+
const { allSymbols, filesToParse, metadataUpdates, rootDir, removed } = ctx;
|
|
334
340
|
|
|
335
341
|
// Populate fileSymbols before any DB writes (used by later stages)
|
|
336
342
|
for (const [relPath, symbols] of allSymbols) {
|
|
@@ -340,11 +346,16 @@ export async function insertNodes(ctx: PipelineContext): Promise<void> {
|
|
|
340
346
|
const t0 = performance.now();
|
|
341
347
|
|
|
342
348
|
// Try native Rust path first — single transaction, no JS↔C overhead
|
|
343
|
-
if (ctx.engineName === 'native'
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
349
|
+
if (ctx.engineName === 'native') {
|
|
350
|
+
try {
|
|
351
|
+
if (tryNativeInsert(ctx)) {
|
|
352
|
+
ctx.timing.insertMs = performance.now() - t0;
|
|
353
|
+
// Removed-file hash cleanup is handled inside the native call
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
356
|
+
} catch {
|
|
357
|
+
// Native insert failed — fall through to JS implementation
|
|
358
|
+
}
|
|
348
359
|
}
|
|
349
360
|
|
|
350
361
|
// JS fallback
|
|
@@ -355,17 +366,17 @@ export async function insertNodes(ctx: PipelineContext): Promise<void> {
|
|
|
355
366
|
|
|
356
367
|
let upsertHash: SqliteStatement | null;
|
|
357
368
|
try {
|
|
358
|
-
upsertHash = db.prepare(
|
|
369
|
+
upsertHash = ctx.db.prepare(
|
|
359
370
|
'INSERT OR REPLACE INTO file_hashes (file, hash, mtime, size) VALUES (?, ?, ?, ?)',
|
|
360
371
|
);
|
|
361
372
|
} catch {
|
|
362
373
|
upsertHash = null;
|
|
363
374
|
}
|
|
364
375
|
|
|
365
|
-
const insertAll = db.transaction(() => {
|
|
366
|
-
insertDefinitionsAndExports(db, allSymbols);
|
|
367
|
-
insertChildrenAndEdges(db, allSymbols);
|
|
368
|
-
updateFileHashes(db, allSymbols, precomputedData, metadataUpdates, rootDir, upsertHash);
|
|
376
|
+
const insertAll = ctx.db.transaction(() => {
|
|
377
|
+
insertDefinitionsAndExports(ctx.db, allSymbols);
|
|
378
|
+
insertChildrenAndEdges(ctx.db, allSymbols);
|
|
379
|
+
updateFileHashes(ctx.db, allSymbols, precomputedData, metadataUpdates, rootDir, upsertHash);
|
|
369
380
|
});
|
|
370
381
|
|
|
371
382
|
insertAll();
|
|
@@ -373,7 +384,7 @@ export async function insertNodes(ctx: PipelineContext): Promise<void> {
|
|
|
373
384
|
|
|
374
385
|
// Clean up removed file hashes
|
|
375
386
|
if (upsertHash && removed.length > 0) {
|
|
376
|
-
const deleteHash = db.prepare('DELETE FROM file_hashes WHERE file = ?');
|
|
387
|
+
const deleteHash = ctx.db.prepare('DELETE FROM file_hashes WHERE file = ?');
|
|
377
388
|
for (const relPath of removed) {
|
|
378
389
|
deleteHash.run(relPath);
|
|
379
390
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import { performance } from 'node:perf_hooks';
|
|
3
|
+
import { debug } from '../../../../infrastructure/logger.js';
|
|
3
4
|
import type { Import } from '../../../../types.js';
|
|
4
5
|
import { parseFilesAuto } from '../../../parser.js';
|
|
5
6
|
import { resolveImportPath, resolveImportsBatch } from '../../resolve.js';
|
|
@@ -132,8 +133,8 @@ export async function resolveImports(ctx: PipelineContext): Promise<void> {
|
|
|
132
133
|
);
|
|
133
134
|
}
|
|
134
135
|
}
|
|
135
|
-
} catch {
|
|
136
|
-
|
|
136
|
+
} catch (e: unknown) {
|
|
137
|
+
debug(`Barrel re-parse failed (non-fatal): ${(e as Error).message}`);
|
|
137
138
|
}
|
|
138
139
|
}
|
|
139
140
|
}
|
|
@@ -117,6 +117,35 @@ function matchSubpathPattern(pattern: string, subpath: string): string | null {
|
|
|
117
117
|
* Resolve a bare specifier through the package.json exports field.
|
|
118
118
|
* Returns an absolute path or null.
|
|
119
119
|
*/
|
|
120
|
+
/** Try to resolve a condition target to a file path in packageDir. */
|
|
121
|
+
function tryResolveTarget(target: string | null, packageDir: string): string | null {
|
|
122
|
+
if (!target) return null;
|
|
123
|
+
const resolved = path.resolve(packageDir, target);
|
|
124
|
+
return fs.existsSync(resolved) ? resolved : null;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/** Resolve subpath against a subpath map (object with "." keys). */
|
|
128
|
+
function resolveSubpathMap(
|
|
129
|
+
exports: Record<string, unknown>,
|
|
130
|
+
subpath: string,
|
|
131
|
+
packageDir: string,
|
|
132
|
+
): string | null {
|
|
133
|
+
// Exact match first
|
|
134
|
+
if (subpath in exports) {
|
|
135
|
+
return tryResolveTarget(resolveCondition(exports[subpath]), packageDir);
|
|
136
|
+
}
|
|
137
|
+
// Pattern matching (keys with *)
|
|
138
|
+
for (const [pattern, value] of Object.entries(exports)) {
|
|
139
|
+
if (!pattern.includes('*')) continue;
|
|
140
|
+
const matched = matchSubpathPattern(pattern, subpath);
|
|
141
|
+
if (matched == null) continue;
|
|
142
|
+
const rawTarget = resolveCondition(value);
|
|
143
|
+
if (!rawTarget) continue;
|
|
144
|
+
return tryResolveTarget(rawTarget.replace(/\*/g, matched), packageDir);
|
|
145
|
+
}
|
|
146
|
+
return null;
|
|
147
|
+
}
|
|
148
|
+
|
|
120
149
|
export function resolveViaExports(specifier: string, rootDir: string): string | null {
|
|
121
150
|
const parsed = parseBareSpecifier(specifier);
|
|
122
151
|
if (!parsed) return null;
|
|
@@ -131,66 +160,25 @@ export function resolveViaExports(specifier: string, rootDir: string): string |
|
|
|
131
160
|
|
|
132
161
|
// Simple string exports: "exports": "./index.js"
|
|
133
162
|
if (typeof exports === 'string') {
|
|
134
|
-
|
|
135
|
-
const resolved = path.resolve(packageDir, exports);
|
|
136
|
-
return fs.existsSync(resolved) ? resolved : null;
|
|
137
|
-
}
|
|
138
|
-
return null;
|
|
163
|
+
return subpath === '.' ? tryResolveTarget(exports, packageDir) : null;
|
|
139
164
|
}
|
|
140
165
|
|
|
141
166
|
// Array form at top level
|
|
142
167
|
if (Array.isArray(exports)) {
|
|
143
|
-
|
|
144
|
-
const target = resolveCondition(exports);
|
|
145
|
-
if (target) {
|
|
146
|
-
const resolved = path.resolve(packageDir, target);
|
|
147
|
-
return fs.existsSync(resolved) ? resolved : null;
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
return null;
|
|
168
|
+
return subpath === '.' ? tryResolveTarget(resolveCondition(exports), packageDir) : null;
|
|
151
169
|
}
|
|
152
170
|
|
|
153
171
|
if (typeof exports !== 'object') return null;
|
|
154
172
|
|
|
155
|
-
// Determine if exports is a conditions object
|
|
156
|
-
// or a subpath map (keys start with ".")
|
|
173
|
+
// Determine if exports is a conditions object or a subpath map
|
|
157
174
|
const keys = Object.keys(exports);
|
|
158
175
|
const isSubpathMap = keys.length > 0 && keys.some((k) => k.startsWith('.'));
|
|
159
176
|
|
|
160
177
|
if (!isSubpathMap) {
|
|
161
|
-
|
|
162
|
-
if (subpath === '.') {
|
|
163
|
-
const target = resolveCondition(exports);
|
|
164
|
-
if (target) {
|
|
165
|
-
const resolved = path.resolve(packageDir, target);
|
|
166
|
-
return fs.existsSync(resolved) ? resolved : null;
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
return null;
|
|
178
|
+
return subpath === '.' ? tryResolveTarget(resolveCondition(exports), packageDir) : null;
|
|
170
179
|
}
|
|
171
180
|
|
|
172
|
-
|
|
173
|
-
if (subpath in exports) {
|
|
174
|
-
const target = resolveCondition(exports[subpath]);
|
|
175
|
-
if (target) {
|
|
176
|
-
const resolved = path.resolve(packageDir, target);
|
|
177
|
-
return fs.existsSync(resolved) ? resolved : null;
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
// Pattern matching (keys with *)
|
|
182
|
-
for (const [pattern, value] of Object.entries(exports)) {
|
|
183
|
-
if (!pattern.includes('*')) continue;
|
|
184
|
-
const matched = matchSubpathPattern(pattern, subpath);
|
|
185
|
-
if (matched == null) continue;
|
|
186
|
-
const rawTarget = resolveCondition(value);
|
|
187
|
-
if (!rawTarget) continue;
|
|
188
|
-
const target = rawTarget.replace(/\*/g, matched);
|
|
189
|
-
const resolved = path.resolve(packageDir, target);
|
|
190
|
-
if (fs.existsSync(resolved)) return resolved;
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
return null;
|
|
181
|
+
return resolveSubpathMap(exports as Record<string, unknown>, subpath, packageDir);
|
|
194
182
|
}
|
|
195
183
|
|
|
196
184
|
/** Clear the exports cache (for testing). */
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { closeDb, getNodeId as getNodeIdQuery, initSchema, openDb } from '../../db/index.js';
|
|
4
|
-
import { info } from '../../infrastructure/logger.js';
|
|
4
|
+
import { debug, info } from '../../infrastructure/logger.js';
|
|
5
5
|
import { EXTENSIONS, IGNORE_DIRS, normalizePath } from '../../shared/constants.js';
|
|
6
6
|
import { DbError } from '../../shared/errors.js';
|
|
7
7
|
import { createParseTreeCache, getActiveEngine } from '../parser.js';
|
|
@@ -32,12 +32,10 @@ export async function watchProject(rootDir: string, opts: { engine?: string } =
|
|
|
32
32
|
ast: false,
|
|
33
33
|
};
|
|
34
34
|
const { name: engineName, version: engineVersion } = getActiveEngine(engineOpts);
|
|
35
|
-
|
|
36
|
-
`Watch mode using ${engineName} engine${engineVersion ? ` (v${engineVersion})` : ''}`,
|
|
37
|
-
);
|
|
35
|
+
info(`Watch mode using ${engineName} engine${engineVersion ? ` (v${engineVersion})` : ''}`);
|
|
38
36
|
|
|
39
37
|
const cache = createParseTreeCache();
|
|
40
|
-
|
|
38
|
+
info(
|
|
41
39
|
cache
|
|
42
40
|
? 'Incremental parsing enabled (native tree cache)'
|
|
43
41
|
: 'Incremental parsing unavailable (full re-parse)',
|
|
@@ -124,8 +122,8 @@ export async function watchProject(rootDir: string, opts: { engine?: string } =
|
|
|
124
122
|
}));
|
|
125
123
|
try {
|
|
126
124
|
appendJournalEntries(rootDir, entries);
|
|
127
|
-
} catch {
|
|
128
|
-
|
|
125
|
+
} catch (e: unknown) {
|
|
126
|
+
debug(`Journal write failed (non-fatal): ${(e as Error).message}`);
|
|
129
127
|
}
|
|
130
128
|
|
|
131
129
|
const changeEvents = updates.map((r) =>
|
|
@@ -137,8 +135,8 @@ export async function watchProject(rootDir: string, opts: { engine?: string } =
|
|
|
137
135
|
);
|
|
138
136
|
try {
|
|
139
137
|
appendChangeEvents(rootDir, changeEvents);
|
|
140
|
-
} catch {
|
|
141
|
-
|
|
138
|
+
} catch (e: unknown) {
|
|
139
|
+
debug(`Change event write failed (non-fatal): ${(e as Error).message}`);
|
|
142
140
|
}
|
|
143
141
|
}
|
|
144
142
|
|
|
@@ -153,8 +151,8 @@ export async function watchProject(rootDir: string, opts: { engine?: string } =
|
|
|
153
151
|
}
|
|
154
152
|
}
|
|
155
153
|
|
|
156
|
-
|
|
157
|
-
|
|
154
|
+
info(`Watching ${rootDir} for changes...`);
|
|
155
|
+
info('Press Ctrl+C to stop.');
|
|
158
156
|
|
|
159
157
|
const watcher = fs.watch(rootDir, { recursive: true }, (_eventType, filename) => {
|
|
160
158
|
if (!filename) return;
|
|
@@ -169,7 +167,7 @@ export async function watchProject(rootDir: string, opts: { engine?: string } =
|
|
|
169
167
|
});
|
|
170
168
|
|
|
171
169
|
process.on('SIGINT', () => {
|
|
172
|
-
|
|
170
|
+
info('Stopping watcher...');
|
|
173
171
|
watcher.close();
|
|
174
172
|
// Flush any pending file paths to journal before exit
|
|
175
173
|
if (pending.size > 0) {
|
|
@@ -178,8 +176,8 @@ export async function watchProject(rootDir: string, opts: { engine?: string } =
|
|
|
178
176
|
}));
|
|
179
177
|
try {
|
|
180
178
|
appendJournalEntries(rootDir, entries);
|
|
181
|
-
} catch {
|
|
182
|
-
|
|
179
|
+
} catch (e: unknown) {
|
|
180
|
+
debug(`Journal flush on exit failed (non-fatal): ${(e as Error).message}`);
|
|
183
181
|
}
|
|
184
182
|
}
|
|
185
183
|
if (cache) cache.clear();
|