@optave/codegraph 3.9.1 → 3.9.3
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 +95 -14
- package/dist/ast-analysis/engine.d.ts.map +1 -1
- package/dist/ast-analysis/engine.js +64 -0
- package/dist/ast-analysis/engine.js.map +1 -1
- package/dist/cli/commands/batch.d.ts.map +1 -1
- package/dist/cli/commands/batch.js +5 -17
- package/dist/cli/commands/batch.js.map +1 -1
- package/dist/cli/commands/structure.d.ts.map +1 -1
- package/dist/cli/commands/structure.js +18 -1
- package/dist/cli/commands/structure.js.map +1 -1
- package/dist/db/connection.d.ts +2 -0
- package/dist/db/connection.d.ts.map +1 -1
- package/dist/db/connection.js +2 -2
- 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/domain/analysis/context.d.ts.map +1 -1
- package/dist/domain/analysis/context.js +5 -15
- package/dist/domain/analysis/context.js.map +1 -1
- package/dist/domain/analysis/dependencies.d.ts +5 -5
- package/dist/domain/analysis/dependencies.d.ts.map +1 -1
- package/dist/domain/analysis/dependencies.js +6 -16
- package/dist/domain/analysis/dependencies.js.map +1 -1
- package/dist/domain/analysis/diff-impact.d.ts +12 -0
- package/dist/domain/analysis/diff-impact.d.ts.map +1 -1
- package/dist/domain/analysis/diff-impact.js +20 -1
- package/dist/domain/analysis/diff-impact.js.map +1 -1
- package/dist/domain/analysis/fn-impact.js +2 -2
- 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 +3 -13
- package/dist/domain/analysis/implementations.js.map +1 -1
- package/dist/domain/graph/builder/context.d.ts +4 -0
- package/dist/domain/graph/builder/context.d.ts.map +1 -1
- package/dist/domain/graph/builder/context.js +4 -0
- package/dist/domain/graph/builder/context.js.map +1 -1
- package/dist/domain/graph/builder/native-db-proxy.d.ts +24 -0
- package/dist/domain/graph/builder/native-db-proxy.d.ts.map +1 -0
- package/dist/domain/graph/builder/native-db-proxy.js +91 -0
- package/dist/domain/graph/builder/native-db-proxy.js.map +1 -0
- package/dist/domain/graph/builder/pipeline.d.ts.map +1 -1
- package/dist/domain/graph/builder/pipeline.js +148 -79
- 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 +15 -2
- package/dist/domain/graph/builder/stages/build-edges.js.map +1 -1
- package/dist/domain/graph/builder/stages/build-structure.js +2 -2
- 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 +6 -28
- package/dist/domain/graph/builder/stages/detect-changes.js.map +1 -1
- package/dist/domain/graph/builder/stages/finalize.js +1 -1
- 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 +16 -12
- 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 +2 -3
- package/dist/domain/graph/builder/stages/resolve-imports.js.map +1 -1
- package/dist/domain/parser.d.ts.map +1 -1
- package/dist/domain/parser.js +11 -4
- package/dist/domain/parser.js.map +1 -1
- package/dist/domain/queries.d.ts +1 -1
- package/dist/domain/queries.d.ts.map +1 -1
- package/dist/domain/queries.js +1 -1
- package/dist/domain/queries.js.map +1 -1
- package/dist/features/ast.js +2 -2
- package/dist/features/ast.js.map +1 -1
- package/dist/features/audit.d.ts.map +1 -1
- package/dist/features/audit.js +3 -2
- package/dist/features/audit.js.map +1 -1
- package/dist/features/boundaries.d.ts.map +1 -1
- package/dist/features/boundaries.js +3 -5
- package/dist/features/boundaries.js.map +1 -1
- package/dist/features/branch-compare.d.ts.map +1 -1
- package/dist/features/branch-compare.js +2 -1
- package/dist/features/branch-compare.js.map +1 -1
- package/dist/features/cfg.d.ts +1 -1
- package/dist/features/cfg.d.ts.map +1 -1
- package/dist/features/cfg.js +52 -6
- package/dist/features/cfg.js.map +1 -1
- package/dist/features/complexity.d.ts.map +1 -1
- package/dist/features/complexity.js +7 -0
- package/dist/features/complexity.js.map +1 -1
- package/dist/features/flow.d.ts.map +1 -1
- package/dist/features/flow.js +2 -1
- package/dist/features/flow.js.map +1 -1
- package/dist/features/manifesto.d.ts.map +1 -1
- package/dist/features/manifesto.js +15 -1
- package/dist/features/manifesto.js.map +1 -1
- package/dist/infrastructure/config.d.ts +1 -0
- package/dist/infrastructure/config.d.ts.map +1 -1
- package/dist/infrastructure/config.js +1 -0
- package/dist/infrastructure/config.js.map +1 -1
- package/dist/infrastructure/update-check.d.ts +1 -1
- package/dist/infrastructure/update-check.js +3 -3
- package/dist/infrastructure/update-check.js.map +1 -1
- package/dist/presentation/batch.d.ts.map +1 -1
- package/dist/presentation/batch.js +1 -0
- package/dist/presentation/batch.js.map +1 -1
- package/dist/presentation/structure.d.ts +1 -1
- package/dist/presentation/structure.d.ts.map +1 -1
- package/dist/presentation/structure.js +1 -1
- package/dist/presentation/structure.js.map +1 -1
- package/dist/shared/normalize.d.ts +12 -0
- package/dist/shared/normalize.d.ts.map +1 -1
- package/dist/shared/normalize.js +4 -0
- package/dist/shared/normalize.js.map +1 -1
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +7 -7
- package/src/ast-analysis/engine.ts +83 -0
- package/src/cli/commands/batch.ts +5 -26
- package/src/cli/commands/structure.ts +21 -1
- package/src/db/connection.ts +2 -2
- package/src/db/index.ts +2 -0
- package/src/domain/analysis/context.ts +5 -15
- package/src/domain/analysis/dependencies.ts +6 -16
- package/src/domain/analysis/diff-impact.ts +28 -1
- package/src/domain/analysis/fn-impact.ts +2 -2
- package/src/domain/analysis/implementations.ts +3 -13
- package/src/domain/graph/builder/context.ts +4 -0
- package/src/domain/graph/builder/native-db-proxy.ts +104 -0
- package/src/domain/graph/builder/pipeline.ts +171 -84
- package/src/domain/graph/builder/stages/build-edges.ts +15 -2
- package/src/domain/graph/builder/stages/build-structure.ts +2 -2
- package/src/domain/graph/builder/stages/detect-changes.ts +11 -33
- package/src/domain/graph/builder/stages/finalize.ts +1 -1
- package/src/domain/graph/builder/stages/insert-nodes.ts +17 -14
- package/src/domain/graph/builder/stages/resolve-imports.ts +2 -3
- package/src/domain/parser.ts +12 -4
- package/src/domain/queries.ts +1 -1
- package/src/features/ast.ts +2 -2
- package/src/features/audit.ts +3 -2
- package/src/features/boundaries.ts +3 -5
- package/src/features/branch-compare.ts +2 -3
- package/src/features/cfg.ts +51 -6
- package/src/features/complexity.ts +7 -0
- package/src/features/flow.ts +2 -1
- package/src/features/manifesto.ts +15 -1
- package/src/infrastructure/config.ts +1 -0
- package/src/infrastructure/update-check.ts +3 -3
- package/src/presentation/batch.ts +1 -0
- package/src/presentation/structure.ts +2 -2
- package/src/shared/normalize.ts +10 -0
- package/src/types.ts +2 -0
|
@@ -2,7 +2,7 @@ import { findFileNodes, type Repository } from '../../db/index.js';
|
|
|
2
2
|
import { cachedStmt } from '../../db/repository/cached-stmt.js';
|
|
3
3
|
import { isTestFile } from '../../infrastructure/test-filter.js';
|
|
4
4
|
import { resolveMethodViaHierarchy } from '../../shared/hierarchy.js';
|
|
5
|
-
import { normalizeSymbol } from '../../shared/normalize.js';
|
|
5
|
+
import { normalizeSymbol, toSymbolRef } from '../../shared/normalize.js';
|
|
6
6
|
import { paginateResult } from '../../shared/paginate.js';
|
|
7
7
|
import type {
|
|
8
8
|
BetterSqlite3Database,
|
|
@@ -143,12 +143,8 @@ function buildNodeDepsResult(
|
|
|
143
143
|
|
|
144
144
|
return {
|
|
145
145
|
...normalizeSymbol(node, repo, hc),
|
|
146
|
-
callees: filteredCallees.map(
|
|
147
|
-
|
|
148
|
-
kind: c.kind,
|
|
149
|
-
file: c.file,
|
|
150
|
-
line: c.line,
|
|
151
|
-
})),
|
|
146
|
+
callees: filteredCallees.map(toSymbolRef),
|
|
147
|
+
// Not using toSymbolRef — callers include the extra viaHierarchy field
|
|
152
148
|
callers: callers.map((c) => ({
|
|
153
149
|
name: c.name,
|
|
154
150
|
kind: c.kind,
|
|
@@ -245,20 +241,14 @@ function resolveEndpoints(
|
|
|
245
241
|
to,
|
|
246
242
|
found: false,
|
|
247
243
|
error: `No symbol matching "${to}"`,
|
|
248
|
-
fromCandidates: fromNodes
|
|
249
|
-
.slice(0, 5)
|
|
250
|
-
.map((n) => ({ name: n.name, kind: n.kind, file: n.file, line: n.line })),
|
|
244
|
+
fromCandidates: fromNodes.slice(0, 5).map(toSymbolRef),
|
|
251
245
|
toCandidates: [],
|
|
252
246
|
},
|
|
253
247
|
};
|
|
254
248
|
}
|
|
255
249
|
|
|
256
|
-
const fromCandidates = fromNodes
|
|
257
|
-
|
|
258
|
-
.map((n) => ({ name: n.name, kind: n.kind, file: n.file, line: n.line }));
|
|
259
|
-
const toCandidates = toNodes
|
|
260
|
-
.slice(0, 5)
|
|
261
|
-
.map((n) => ({ name: n.name, kind: n.kind, file: n.file, line: n.line }));
|
|
250
|
+
const fromCandidates = fromNodes.slice(0, 5).map(toSymbolRef);
|
|
251
|
+
const toCandidates = toNodes.slice(0, 5).map(toSymbolRef);
|
|
262
252
|
|
|
263
253
|
return {
|
|
264
254
|
sourceNode: fromNodes[0],
|
|
@@ -307,6 +307,33 @@ export function diffImpactData(
|
|
|
307
307
|
}
|
|
308
308
|
|
|
309
309
|
const affectedFunctions = findAffectedFunctions(db, changedRanges, noTests);
|
|
310
|
+
|
|
311
|
+
// Short-circuit: when no function-level changes detected, skip expensive
|
|
312
|
+
// lookups (BFS, co-change, ownership). Boundary checks are preserved
|
|
313
|
+
// because they are file-scoped and can surface real violations even when
|
|
314
|
+
// no function bodies were touched (e.g. import or type-alias changes).
|
|
315
|
+
if (affectedFunctions.length === 0) {
|
|
316
|
+
const { boundaryViolations, boundaryViolationCount } = checkBoundaryViolations(
|
|
317
|
+
db,
|
|
318
|
+
changedRanges,
|
|
319
|
+
noTests,
|
|
320
|
+
{ ...opts, config },
|
|
321
|
+
repoRoot,
|
|
322
|
+
);
|
|
323
|
+
const base = {
|
|
324
|
+
changedFiles: changedRanges.size,
|
|
325
|
+
newFiles: [...newFiles],
|
|
326
|
+
affectedFunctions: [] as unknown[],
|
|
327
|
+
affectedFiles: [] as string[],
|
|
328
|
+
historicallyCoupled: [] as unknown[],
|
|
329
|
+
ownership: null,
|
|
330
|
+
boundaryViolations,
|
|
331
|
+
boundaryViolationCount,
|
|
332
|
+
summary: null as null,
|
|
333
|
+
};
|
|
334
|
+
return paginateResult(base, 'affectedFunctions', { limit: opts.limit, offset: opts.offset });
|
|
335
|
+
}
|
|
336
|
+
|
|
310
337
|
const includeImplementors = opts.includeImplementors !== false;
|
|
311
338
|
const { functionResults, allAffected } = buildFunctionImpactResults(
|
|
312
339
|
db,
|
|
@@ -325,7 +352,7 @@ export function diffImpactData(
|
|
|
325
352
|
db,
|
|
326
353
|
changedRanges,
|
|
327
354
|
noTests,
|
|
328
|
-
opts,
|
|
355
|
+
{ ...opts, config },
|
|
329
356
|
repoRoot,
|
|
330
357
|
);
|
|
331
358
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Repository, SqliteRepository } from '../../db/index.js';
|
|
2
2
|
import { isTestFile } from '../../infrastructure/test-filter.js';
|
|
3
|
-
import { normalizeSymbol } from '../../shared/normalize.js';
|
|
3
|
+
import { normalizeSymbol, toSymbolRef } from '../../shared/normalize.js';
|
|
4
4
|
import { paginateResult } from '../../shared/paginate.js';
|
|
5
5
|
import type { BetterSqlite3Database, NodeRow, RelatedNodeRow } from '../../types.js';
|
|
6
6
|
import { resolveAnalysisOpts, withRepo } from './query-helpers.js';
|
|
@@ -125,7 +125,7 @@ export function bfsTransitiveCallers(
|
|
|
125
125
|
visited.add(c.id);
|
|
126
126
|
nextFrontier.push(c.id);
|
|
127
127
|
if (!levels[d]) levels[d] = [];
|
|
128
|
-
levels[d]!.push(
|
|
128
|
+
levels[d]!.push(toSymbolRef(c));
|
|
129
129
|
if (onVisit) onVisit(c, fid, d);
|
|
130
130
|
}
|
|
131
131
|
if (resolveImplementors && INTERFACE_LIKE_KINDS.has(c.kind)) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { isTestFile } from '../../infrastructure/test-filter.js';
|
|
2
2
|
import { CORE_SYMBOL_KINDS } from '../../shared/kinds.js';
|
|
3
|
-
import { normalizeSymbol } from '../../shared/normalize.js';
|
|
3
|
+
import { normalizeSymbol, toSymbolRef } from '../../shared/normalize.js';
|
|
4
4
|
import { paginateResult } from '../../shared/paginate.js';
|
|
5
5
|
import type { RelatedNodeRow } from '../../types.js';
|
|
6
6
|
import { withRepo } from './query-helpers.js';
|
|
@@ -34,12 +34,7 @@ export function implementationsData(
|
|
|
34
34
|
|
|
35
35
|
return {
|
|
36
36
|
...normalizeSymbol(node, repo, hc),
|
|
37
|
-
implementors: implementors.map(
|
|
38
|
-
name: impl.name,
|
|
39
|
-
kind: impl.kind,
|
|
40
|
-
file: impl.file,
|
|
41
|
-
line: impl.line,
|
|
42
|
-
})),
|
|
37
|
+
implementors: implementors.map(toSymbolRef),
|
|
43
38
|
};
|
|
44
39
|
});
|
|
45
40
|
|
|
@@ -76,12 +71,7 @@ export function interfacesData(
|
|
|
76
71
|
|
|
77
72
|
return {
|
|
78
73
|
...normalizeSymbol(node, repo, hc),
|
|
79
|
-
interfaces: interfaces.map(
|
|
80
|
-
name: iface.name,
|
|
81
|
-
kind: iface.kind,
|
|
82
|
-
file: iface.file,
|
|
83
|
-
line: iface.line,
|
|
84
|
-
})),
|
|
74
|
+
interfaces: interfaces.map(toSymbolRef),
|
|
85
75
|
};
|
|
86
76
|
});
|
|
87
77
|
|
|
@@ -33,6 +33,10 @@ export class PipelineContext {
|
|
|
33
33
|
forceFullRebuild: boolean = false;
|
|
34
34
|
schemaVersion!: number;
|
|
35
35
|
nativeDb?: NativeDatabase;
|
|
36
|
+
/** Whether native engine is available (deferred — DB opened only when needed). */
|
|
37
|
+
nativeAvailable: boolean = false;
|
|
38
|
+
/** True when ctx.db is a NativeDbProxy — single rusqlite connection for the entire pipeline. */
|
|
39
|
+
nativeFirstProxy: boolean = false;
|
|
36
40
|
|
|
37
41
|
// ── File collection (set by collectFiles stage) ────────────────────
|
|
38
42
|
allFiles!: string[];
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NativeDbProxy — wraps a NativeDatabase (rusqlite via napi-rs) to satisfy the
|
|
3
|
+
* BetterSqlite3Database interface. When the native addon is available, the
|
|
4
|
+
* build pipeline uses this proxy as `ctx.db` so that every stage operates on a
|
|
5
|
+
* single rusqlite connection — no dual-connection WAL corruption, no
|
|
6
|
+
* open/close/reopen dance.
|
|
7
|
+
*
|
|
8
|
+
* When native is unavailable, the pipeline falls back to real better-sqlite3.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type { BetterSqlite3Database, NativeDatabase, SqliteStatement } from '../../../types.js';
|
|
12
|
+
|
|
13
|
+
/** Sanitize params for napi-rs: better-sqlite3 treats `undefined` as NULL,
|
|
14
|
+
* but serde_json cannot represent `undefined`. Replace with `null`. */
|
|
15
|
+
function sanitize(params: unknown[]): Array<string | number | null> {
|
|
16
|
+
return params.map((p) => (p === undefined ? null : p)) as Array<string | number | null>;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export class NativeDbProxy implements BetterSqlite3Database {
|
|
20
|
+
readonly #ndb: NativeDatabase;
|
|
21
|
+
/** Advisory lock path — set by the pipeline so closeDb() can release it. */
|
|
22
|
+
__lockPath?: string;
|
|
23
|
+
|
|
24
|
+
constructor(nativeDb: NativeDatabase) {
|
|
25
|
+
this.#ndb = nativeDb;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
prepare<TRow = unknown>(sql: string): SqliteStatement<TRow> {
|
|
29
|
+
const ndb = this.#ndb;
|
|
30
|
+
// Only INSERT statements need last_insert_rowid — skip the extra napi
|
|
31
|
+
// call for UPDATE/DELETE/other DML to halve per-statement overhead.
|
|
32
|
+
const isInsert = sql.trimStart().substring(0, 6).toUpperCase() === 'INSERT';
|
|
33
|
+
const stmt: SqliteStatement<TRow> = {
|
|
34
|
+
all(...params: unknown[]): TRow[] {
|
|
35
|
+
return ndb.queryAll(sql, sanitize(params)) as TRow[];
|
|
36
|
+
},
|
|
37
|
+
get(...params: unknown[]): TRow | undefined {
|
|
38
|
+
return (ndb.queryGet(sql, sanitize(params)) ?? undefined) as TRow | undefined;
|
|
39
|
+
},
|
|
40
|
+
run(...params: unknown[]): { changes: number; lastInsertRowid: number | bigint } {
|
|
41
|
+
ndb.queryAll(sql, sanitize(params));
|
|
42
|
+
if (isInsert) {
|
|
43
|
+
const row = ndb.queryGet('SELECT last_insert_rowid() AS rid', []) as {
|
|
44
|
+
rid: number;
|
|
45
|
+
} | null;
|
|
46
|
+
return { changes: 0, lastInsertRowid: row?.rid ?? 0 };
|
|
47
|
+
}
|
|
48
|
+
return { changes: 0, lastInsertRowid: 0 };
|
|
49
|
+
},
|
|
50
|
+
iterate(): IterableIterator<TRow> {
|
|
51
|
+
throw new Error('iterate() is not supported via NativeDbProxy');
|
|
52
|
+
},
|
|
53
|
+
raw(): SqliteStatement<TRow> {
|
|
54
|
+
return stmt; // no-op — .raw() is not used in the build pipeline
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
return stmt;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
exec(sql: string): this {
|
|
61
|
+
this.#ndb.exec(sql);
|
|
62
|
+
return this;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
pragma(sql: string): unknown {
|
|
66
|
+
return this.#ndb.pragma(sql);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
close(): void {
|
|
70
|
+
// No-op: the pipeline manages the NativeDatabase lifecycle directly.
|
|
71
|
+
// closeDbPair() calls nativeDb.close() separately.
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
get open(): boolean {
|
|
75
|
+
return this.#ndb.isOpen;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
get name(): string {
|
|
79
|
+
return this.#ndb.dbPath;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
transaction<F extends (...args: any[]) => any>(
|
|
83
|
+
fn: F,
|
|
84
|
+
): (...args: F extends (...a: infer A) => unknown ? A : never) => ReturnType<F> {
|
|
85
|
+
const ndb = this.#ndb;
|
|
86
|
+
return ((...args: unknown[]) => {
|
|
87
|
+
// NOTE: nested transactions (savepoints) are not supported — ensure callers
|
|
88
|
+
// do not invoke a transaction() wrapper from within an existing transaction.
|
|
89
|
+
ndb.exec('BEGIN');
|
|
90
|
+
try {
|
|
91
|
+
const result = fn(...args);
|
|
92
|
+
ndb.exec('COMMIT');
|
|
93
|
+
return result;
|
|
94
|
+
} catch (e) {
|
|
95
|
+
try {
|
|
96
|
+
ndb.exec('ROLLBACK');
|
|
97
|
+
} catch {
|
|
98
|
+
// Ignore rollback errors — the original error is more important
|
|
99
|
+
}
|
|
100
|
+
throw e;
|
|
101
|
+
}
|
|
102
|
+
}) as any;
|
|
103
|
+
}
|
|
104
|
+
}
|