@optave/codegraph 3.9.0 → 3.9.2
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 +12 -13
- package/dist/ast-analysis/engine.d.ts.map +1 -1
- package/dist/ast-analysis/engine.js +78 -48
- 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 +15 -18
- package/dist/ast-analysis/visitors/ast-store-visitor.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 +3 -0
- package/dist/db/connection.d.ts.map +1 -1
- package/dist/db/connection.js +24 -6
- 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/repository/base.d.ts +35 -0
- package/dist/db/repository/base.d.ts.map +1 -1
- package/dist/db/repository/base.js +8 -0
- package/dist/db/repository/base.js.map +1 -1
- package/dist/db/repository/index.d.ts +1 -0
- package/dist/db/repository/index.d.ts.map +1 -1
- package/dist/db/repository/index.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 +46 -1
- package/dist/db/repository/native-repository.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 +6 -33
- package/dist/domain/analysis/dependencies.d.ts.map +1 -1
- package/dist/domain/analysis/dependencies.js +18 -16
- package/dist/domain/analysis/dependencies.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/incremental.d.ts.map +1 -1
- package/dist/domain/graph/builder/incremental.js +18 -0
- package/dist/domain/graph/builder/incremental.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 +87 -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 +410 -349
- 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 +44 -4
- 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 +21 -26
- package/dist/domain/graph/builder/stages/resolve-imports.js.map +1 -1
- package/dist/domain/graph/watcher.d.ts.map +1 -1
- package/dist/domain/graph/watcher.js +99 -95
- package/dist/domain/graph/watcher.js.map +1 -1
- package/dist/domain/parser.d.ts.map +1 -1
- package/dist/domain/parser.js +7 -2
- 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/extractors/go.js +53 -35
- package/dist/extractors/go.js.map +1 -1
- package/dist/extractors/javascript.js +66 -27
- package/dist/extractors/javascript.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/complexity.d.ts.map +1 -1
- package/dist/features/complexity.js +78 -58
- package/dist/features/complexity.js.map +1 -1
- package/dist/features/dataflow.d.ts.map +1 -1
- package/dist/features/dataflow.js +109 -118
- package/dist/features/dataflow.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/features/structure.d.ts.map +1 -1
- package/dist/features/structure.js +147 -97
- package/dist/features/structure.js.map +1 -1
- package/dist/graph/algorithms/louvain.d.ts.map +1 -1
- package/dist/graph/algorithms/louvain.js +4 -2
- package/dist/graph/algorithms/louvain.js.map +1 -1
- package/dist/graph/classifiers/roles.d.ts +2 -0
- package/dist/graph/classifiers/roles.d.ts.map +1 -1
- package/dist/graph/classifiers/roles.js +13 -5
- package/dist/graph/classifiers/roles.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/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/communities.d.ts.map +1 -1
- package/dist/presentation/communities.js +38 -34
- package/dist/presentation/communities.js.map +1 -1
- package/dist/presentation/manifesto.d.ts.map +1 -1
- package/dist/presentation/manifesto.js +31 -33
- package/dist/presentation/manifesto.js.map +1 -1
- package/dist/presentation/queries-cli/inspect.d.ts.map +1 -1
- package/dist/presentation/queries-cli/inspect.js +47 -46
- package/dist/presentation/queries-cli/inspect.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/file-utils.d.ts.map +1 -1
- package/dist/shared/file-utils.js +94 -72
- package/dist/shared/file-utils.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 +82 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +7 -7
- package/src/ast-analysis/engine.ts +99 -55
- package/src/ast-analysis/visitors/ast-store-visitor.ts +19 -21
- package/src/cli/commands/batch.ts +5 -26
- package/src/cli/commands/structure.ts +21 -1
- package/src/db/connection.ts +26 -7
- package/src/db/index.ts +2 -0
- package/src/db/repository/base.ts +43 -0
- package/src/db/repository/index.ts +1 -0
- package/src/db/repository/native-repository.ts +67 -1
- package/src/domain/analysis/context.ts +5 -15
- package/src/domain/analysis/dependencies.ts +19 -16
- 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/incremental.ts +21 -0
- package/src/domain/graph/builder/native-db-proxy.ts +98 -0
- package/src/domain/graph/builder/pipeline.ts +514 -416
- package/src/domain/graph/builder/stages/build-edges.ts +45 -3
- 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 +22 -23
- package/src/domain/graph/watcher.ts +118 -98
- package/src/domain/parser.ts +8 -2
- package/src/domain/queries.ts +1 -1
- package/src/extractors/go.ts +57 -32
- package/src/extractors/javascript.ts +67 -27
- 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/complexity.ts +94 -58
- package/src/features/dataflow.ts +153 -132
- package/src/features/flow.ts +2 -1
- package/src/features/manifesto.ts +15 -1
- package/src/features/structure.ts +167 -95
- package/src/graph/algorithms/louvain.ts +5 -2
- package/src/graph/classifiers/roles.ts +14 -5
- package/src/infrastructure/config.ts +1 -0
- package/src/presentation/batch.ts +1 -0
- package/src/presentation/communities.ts +44 -39
- package/src/presentation/manifesto.ts +35 -38
- package/src/presentation/queries-cli/inspect.ts +48 -46
- package/src/presentation/structure.ts +2 -2
- package/src/shared/file-utils.ts +116 -77
- package/src/shared/normalize.ts +10 -0
- package/src/types.ts +86 -0
package/src/extractors/go.ts
CHANGED
|
@@ -266,44 +266,69 @@ function handleTypedIdentifiers(
|
|
|
266
266
|
}
|
|
267
267
|
|
|
268
268
|
/** Infer type from a single RHS expression in a short var declaration. */
|
|
269
|
-
|
|
269
|
+
/** x := Struct{...} — composite literal (confidence 1.0). */
|
|
270
|
+
function inferCompositeLiteral(
|
|
270
271
|
varNode: TreeSitterNode,
|
|
271
272
|
rhs: TreeSitterNode,
|
|
272
273
|
typeMap: Map<string, { type: string; confidence: number }>,
|
|
273
|
-
):
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
if (
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
274
|
+
): boolean {
|
|
275
|
+
if (rhs.type !== 'composite_literal') return false;
|
|
276
|
+
const typeNode = rhs.childForFieldName('type');
|
|
277
|
+
if (!typeNode) return false;
|
|
278
|
+
const typeName = extractGoTypeName(typeNode);
|
|
279
|
+
if (typeName) setTypeMapEntry(typeMap, varNode.text, typeName, 1.0);
|
|
280
|
+
return true;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/** x := &Struct{...} — address-of composite literal (confidence 1.0). */
|
|
284
|
+
function inferAddressOfComposite(
|
|
285
|
+
varNode: TreeSitterNode,
|
|
286
|
+
rhs: TreeSitterNode,
|
|
287
|
+
typeMap: Map<string, { type: string; confidence: number }>,
|
|
288
|
+
): boolean {
|
|
289
|
+
if (rhs.type !== 'unary_expression') return false;
|
|
290
|
+
const operand = rhs.childForFieldName('operand');
|
|
291
|
+
if (!operand || operand.type !== 'composite_literal') return false;
|
|
292
|
+
const typeNode = operand.childForFieldName('type');
|
|
293
|
+
if (!typeNode) return false;
|
|
294
|
+
const typeName = extractGoTypeName(typeNode);
|
|
295
|
+
if (typeName) setTypeMapEntry(typeMap, varNode.text, typeName, 1.0);
|
|
296
|
+
return true;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/** x := NewFoo() or x := pkg.NewFoo() — factory function (confidence 0.7). */
|
|
300
|
+
function inferFactoryCall(
|
|
301
|
+
varNode: TreeSitterNode,
|
|
302
|
+
rhs: TreeSitterNode,
|
|
303
|
+
typeMap: Map<string, { type: string; confidence: number }>,
|
|
304
|
+
): boolean {
|
|
305
|
+
if (rhs.type !== 'call_expression') return false;
|
|
306
|
+
const fn = rhs.childForFieldName('function');
|
|
307
|
+
if (!fn) return false;
|
|
308
|
+
|
|
309
|
+
if (fn.type === 'selector_expression') {
|
|
310
|
+
const field = fn.childForFieldName('field');
|
|
311
|
+
if (field?.text.startsWith('New')) {
|
|
312
|
+
const typeName = field.text.slice(3);
|
|
304
313
|
if (typeName) setTypeMapEntry(typeMap, varNode.text, typeName, 0.7);
|
|
314
|
+
return true;
|
|
305
315
|
}
|
|
316
|
+
} else if (fn.type === 'identifier' && fn.text.startsWith('New')) {
|
|
317
|
+
const typeName = fn.text.slice(3);
|
|
318
|
+
if (typeName) setTypeMapEntry(typeMap, varNode.text, typeName, 0.7);
|
|
319
|
+
return true;
|
|
306
320
|
}
|
|
321
|
+
return false;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
function inferShortVarType(
|
|
325
|
+
varNode: TreeSitterNode,
|
|
326
|
+
rhs: TreeSitterNode,
|
|
327
|
+
typeMap: Map<string, { type: string; confidence: number }>,
|
|
328
|
+
): void {
|
|
329
|
+
if (inferCompositeLiteral(varNode, rhs, typeMap)) return;
|
|
330
|
+
if (inferAddressOfComposite(varNode, rhs, typeMap)) return;
|
|
331
|
+
inferFactoryCall(varNode, rhs, typeMap);
|
|
307
332
|
}
|
|
308
333
|
|
|
309
334
|
/** Handle short_var_declaration: x := Struct{}, x := &Struct{}, x := NewFoo(). */
|
|
@@ -202,6 +202,48 @@ function handleExportCapture(
|
|
|
202
202
|
}
|
|
203
203
|
}
|
|
204
204
|
|
|
205
|
+
function handleInterfaceCapture(
|
|
206
|
+
c: Record<string, TreeSitterNode>,
|
|
207
|
+
definitions: Definition[],
|
|
208
|
+
): void {
|
|
209
|
+
const ifaceNode = c.iface_node!;
|
|
210
|
+
const ifaceName = c.iface_name!.text;
|
|
211
|
+
definitions.push({
|
|
212
|
+
name: ifaceName,
|
|
213
|
+
kind: 'interface',
|
|
214
|
+
line: ifaceNode.startPosition.row + 1,
|
|
215
|
+
endLine: nodeEndLine(ifaceNode),
|
|
216
|
+
});
|
|
217
|
+
const body =
|
|
218
|
+
ifaceNode.childForFieldName('body') ||
|
|
219
|
+
findChild(ifaceNode, 'interface_body') ||
|
|
220
|
+
findChild(ifaceNode, 'object_type');
|
|
221
|
+
if (body) extractInterfaceMethods(body, ifaceName, definitions);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function handleTypeCapture(c: Record<string, TreeSitterNode>, definitions: Definition[]): void {
|
|
225
|
+
const typeNode = c.type_node!;
|
|
226
|
+
definitions.push({
|
|
227
|
+
name: c.type_name!.text,
|
|
228
|
+
kind: 'type',
|
|
229
|
+
line: typeNode.startPosition.row + 1,
|
|
230
|
+
endLine: nodeEndLine(typeNode),
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function handleImportCapture(c: Record<string, TreeSitterNode>, imports: Import[]): void {
|
|
235
|
+
const impNode = c.imp_node!;
|
|
236
|
+
const isTypeOnly = impNode.text.startsWith('import type');
|
|
237
|
+
const modPath = c.imp_source!.text.replace(/['"]/g, '');
|
|
238
|
+
const names = extractImportNames(impNode);
|
|
239
|
+
imports.push({
|
|
240
|
+
source: modPath,
|
|
241
|
+
names,
|
|
242
|
+
line: impNode.startPosition.row + 1,
|
|
243
|
+
typeOnly: isTypeOnly,
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
|
|
205
247
|
/** Dispatch a single query match to the appropriate handler. */
|
|
206
248
|
function dispatchQueryMatch(
|
|
207
249
|
c: Record<string, TreeSitterNode>,
|
|
@@ -220,35 +262,11 @@ function dispatchQueryMatch(
|
|
|
220
262
|
} else if (c.meth_node) {
|
|
221
263
|
handleMethodCapture(c, definitions);
|
|
222
264
|
} else if (c.iface_node) {
|
|
223
|
-
|
|
224
|
-
definitions.push({
|
|
225
|
-
name: ifaceName,
|
|
226
|
-
kind: 'interface',
|
|
227
|
-
line: c.iface_node.startPosition.row + 1,
|
|
228
|
-
endLine: nodeEndLine(c.iface_node),
|
|
229
|
-
});
|
|
230
|
-
const body =
|
|
231
|
-
c.iface_node.childForFieldName('body') ||
|
|
232
|
-
findChild(c.iface_node, 'interface_body') ||
|
|
233
|
-
findChild(c.iface_node, 'object_type');
|
|
234
|
-
if (body) extractInterfaceMethods(body, ifaceName, definitions);
|
|
265
|
+
handleInterfaceCapture(c, definitions);
|
|
235
266
|
} else if (c.type_node) {
|
|
236
|
-
definitions
|
|
237
|
-
name: c.type_name!.text,
|
|
238
|
-
kind: 'type',
|
|
239
|
-
line: c.type_node.startPosition.row + 1,
|
|
240
|
-
endLine: nodeEndLine(c.type_node),
|
|
241
|
-
});
|
|
267
|
+
handleTypeCapture(c, definitions);
|
|
242
268
|
} else if (c.imp_node) {
|
|
243
|
-
|
|
244
|
-
const modPath = c.imp_source!.text.replace(/['"]/g, '');
|
|
245
|
-
const names = extractImportNames(c.imp_node);
|
|
246
|
-
imports.push({
|
|
247
|
-
source: modPath,
|
|
248
|
-
names,
|
|
249
|
-
line: c.imp_node.startPosition.row + 1,
|
|
250
|
-
typeOnly: isTypeOnly,
|
|
251
|
-
});
|
|
269
|
+
handleImportCapture(c, imports);
|
|
252
270
|
} else if (c.exp_node) {
|
|
253
271
|
handleExportCapture(c, exps, imports);
|
|
254
272
|
} else if (c.callfn_node) {
|
|
@@ -264,6 +282,14 @@ function dispatchQueryMatch(
|
|
|
264
282
|
} else if (c.callsub_node) {
|
|
265
283
|
const callInfo = extractCallInfo(c.callsub_fn!, c.callsub_node);
|
|
266
284
|
if (callInfo) calls.push(callInfo);
|
|
285
|
+
} else if (c.newfn_node) {
|
|
286
|
+
calls.push({
|
|
287
|
+
name: c.newfn_name!.text,
|
|
288
|
+
line: c.newfn_node.startPosition.row + 1,
|
|
289
|
+
});
|
|
290
|
+
} else if (c.newmem_node) {
|
|
291
|
+
const callInfo = extractCallInfo(c.newmem_fn!, c.newmem_node);
|
|
292
|
+
if (callInfo) calls.push(callInfo);
|
|
267
293
|
} else if (c.assign_node) {
|
|
268
294
|
handleCommonJSAssignment(c.assign_left!, c.assign_right!, c.assign_node, imports);
|
|
269
295
|
}
|
|
@@ -502,6 +528,9 @@ function walkJavaScriptNode(node: TreeSitterNode, ctx: ExtractorOutput): void {
|
|
|
502
528
|
case 'call_expression':
|
|
503
529
|
handleCallExpr(node, ctx);
|
|
504
530
|
break;
|
|
531
|
+
case 'new_expression':
|
|
532
|
+
handleNewExpr(node, ctx);
|
|
533
|
+
break;
|
|
505
534
|
case 'import_statement':
|
|
506
535
|
handleImportStmt(node, ctx);
|
|
507
536
|
break;
|
|
@@ -689,6 +718,17 @@ function handleCallExpr(node: TreeSitterNode, ctx: ExtractorOutput): void {
|
|
|
689
718
|
}
|
|
690
719
|
}
|
|
691
720
|
|
|
721
|
+
function handleNewExpr(node: TreeSitterNode, ctx: ExtractorOutput): void {
|
|
722
|
+
const ctor = node.childForFieldName('constructor') || node.child(1);
|
|
723
|
+
if (!ctor) return;
|
|
724
|
+
if (ctor.type === 'identifier') {
|
|
725
|
+
ctx.calls.push({ name: ctor.text, line: node.startPosition.row + 1 });
|
|
726
|
+
} else if (ctor.type === 'member_expression') {
|
|
727
|
+
const callInfo = extractCallInfo(ctor, node);
|
|
728
|
+
if (callInfo) ctx.calls.push(callInfo);
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
|
|
692
732
|
/** Handle a dynamic import() call expression and add to imports if static. */
|
|
693
733
|
function handleDynamicImportCall(node: TreeSitterNode, imports: Import[]): void {
|
|
694
734
|
const args = node.childForFieldName('arguments') || findChild(node, 'arguments');
|
package/src/features/audit.ts
CHANGED
|
@@ -7,6 +7,7 @@ import { loadConfig } from '../infrastructure/config.js';
|
|
|
7
7
|
import { debug } from '../infrastructure/logger.js';
|
|
8
8
|
import { isTestFile } from '../infrastructure/test-filter.js';
|
|
9
9
|
import { toErrorMessage } from '../shared/errors.js';
|
|
10
|
+
import { toSymbolRef } from '../shared/normalize.js';
|
|
10
11
|
import type { BetterSqlite3Database, CodegraphConfig } from '../types.js';
|
|
11
12
|
import { RULE_DEFS } from './manifesto.js';
|
|
12
13
|
|
|
@@ -317,7 +318,7 @@ function enrichSymbol(
|
|
|
317
318
|
WHERE e.source_id = ? AND e.kind = 'calls'`,
|
|
318
319
|
)
|
|
319
320
|
.all(nodeId) as SymbolRef[]
|
|
320
|
-
).map(
|
|
321
|
+
).map(toSymbolRef);
|
|
321
322
|
|
|
322
323
|
callers = (
|
|
323
324
|
db
|
|
@@ -327,7 +328,7 @@ function enrichSymbol(
|
|
|
327
328
|
WHERE e.target_id = ? AND e.kind = 'calls'`,
|
|
328
329
|
)
|
|
329
330
|
.all(nodeId) as SymbolRef[]
|
|
330
|
-
).map(
|
|
331
|
+
).map(toSymbolRef);
|
|
331
332
|
if (noTests) callers = callers.filter((c) => !isTestFile(c.file));
|
|
332
333
|
|
|
333
334
|
const testCallerRows = db
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { debug } from '../infrastructure/logger.js';
|
|
2
1
|
import { isTestFile } from '../infrastructure/test-filter.js';
|
|
2
|
+
import { BoundaryError } from '../shared/errors.js';
|
|
3
3
|
import type { BetterSqlite3Database } from '../types.js';
|
|
4
4
|
|
|
5
5
|
// ─── Glob-to-Regex ───────────────────────────────────────────────────
|
|
@@ -269,8 +269,7 @@ export function evaluateBoundaries(
|
|
|
269
269
|
|
|
270
270
|
const { valid, errors } = validateBoundaryConfig(boundaryConfig);
|
|
271
271
|
if (!valid) {
|
|
272
|
-
|
|
273
|
-
return { violations: [], violationCount: 0 };
|
|
272
|
+
throw new BoundaryError(`Invalid boundary configuration: ${errors.join('; ')}`);
|
|
274
273
|
}
|
|
275
274
|
|
|
276
275
|
const modules = resolveModules(boundaryConfig);
|
|
@@ -297,8 +296,7 @@ export function evaluateBoundaries(
|
|
|
297
296
|
)
|
|
298
297
|
.all() as Array<{ source: string; target: string }>;
|
|
299
298
|
} catch (err) {
|
|
300
|
-
|
|
301
|
-
return { violations: [], violationCount: 0 };
|
|
299
|
+
throw new BoundaryError('Boundary evaluation failed', { cause: err as Error });
|
|
302
300
|
}
|
|
303
301
|
|
|
304
302
|
if (opts.noTests) {
|
|
@@ -9,6 +9,7 @@ import { debug } from '../infrastructure/logger.js';
|
|
|
9
9
|
import { getNative, isNativeAvailable } from '../infrastructure/native.js';
|
|
10
10
|
import { isTestFile } from '../infrastructure/test-filter.js';
|
|
11
11
|
import { toErrorMessage } from '../shared/errors.js';
|
|
12
|
+
import { toSymbolRef } from '../shared/normalize.js';
|
|
12
13
|
import type { EngineMode, NativeDatabase } from '../types.js';
|
|
13
14
|
|
|
14
15
|
// ─── Git Helpers ────────────────────────────────────────────────────────
|
|
@@ -255,9 +256,7 @@ function loadCallersFromDb(
|
|
|
255
256
|
if (!visited.has(c.id) && (!noTests || !isTestFile(c.file))) {
|
|
256
257
|
visited.add(c.id);
|
|
257
258
|
nextFrontier.push(c.id);
|
|
258
|
-
allCallers.add(
|
|
259
|
-
JSON.stringify({ name: c.name, kind: c.kind, file: c.file, line: c.line }),
|
|
260
|
-
);
|
|
259
|
+
allCallers.add(JSON.stringify(toSymbolRef(c)));
|
|
261
260
|
}
|
|
262
261
|
}
|
|
263
262
|
}
|
|
@@ -535,75 +535,97 @@ function upsertAstComplexity(
|
|
|
535
535
|
return 1;
|
|
536
536
|
}
|
|
537
537
|
|
|
538
|
-
|
|
538
|
+
/** Collect native bulk-insert rows from precomputed complexity data.
|
|
539
|
+
* Returns the rows array, or null if any definition is missing complexity
|
|
540
|
+
* (signalling that JS fallback is needed). */
|
|
541
|
+
function collectNativeBulkRows(
|
|
542
|
+
db: BetterSqlite3Database,
|
|
543
|
+
fileSymbols: Map<string, FileSymbols>,
|
|
544
|
+
): Array<Record<string, unknown>> | null {
|
|
545
|
+
const rows: Array<Record<string, unknown>> = [];
|
|
546
|
+
|
|
547
|
+
for (const [relPath, symbols] of fileSymbols) {
|
|
548
|
+
for (const def of symbols.definitions) {
|
|
549
|
+
if (def.kind !== 'function' && def.kind !== 'method') continue;
|
|
550
|
+
if (!def.line) continue;
|
|
551
|
+
// Interface/type property signatures and single-line stubs are extracted
|
|
552
|
+
// as methods but the native engine correctly never assigns complexity.
|
|
553
|
+
// Mirror the leniency in initWasmParsersIfNeeded to avoid bailing out
|
|
554
|
+
// of the native bulk-insert path for every TypeScript codebase (#846).
|
|
555
|
+
if (!def.complexity) {
|
|
556
|
+
if (def.name.includes('.') || !def.endLine || def.endLine <= def.line) continue;
|
|
557
|
+
return null; // genuine function body missing complexity — needs JS fallback
|
|
558
|
+
}
|
|
559
|
+
const nodeId = getFunctionNodeId(db, def.name, relPath, def.line);
|
|
560
|
+
if (!nodeId) continue;
|
|
561
|
+
const ch = def.complexity.halstead;
|
|
562
|
+
const cl = def.complexity.loc;
|
|
563
|
+
rows.push({
|
|
564
|
+
nodeId,
|
|
565
|
+
cognitive: def.complexity.cognitive ?? 0,
|
|
566
|
+
cyclomatic: def.complexity.cyclomatic ?? 0,
|
|
567
|
+
maxNesting: def.complexity.maxNesting ?? 0,
|
|
568
|
+
loc: cl ? cl.loc : 0,
|
|
569
|
+
sloc: cl ? cl.sloc : 0,
|
|
570
|
+
commentLines: cl ? cl.commentLines : 0,
|
|
571
|
+
halsteadN1: ch ? ch.n1 : 0,
|
|
572
|
+
halsteadN2: ch ? ch.n2 : 0,
|
|
573
|
+
halsteadBigN1: ch ? ch.bigN1 : 0,
|
|
574
|
+
halsteadBigN2: ch ? ch.bigN2 : 0,
|
|
575
|
+
halsteadVocabulary: ch ? ch.vocabulary : 0,
|
|
576
|
+
halsteadLength: ch ? ch.length : 0,
|
|
577
|
+
halsteadVolume: ch ? ch.volume : 0,
|
|
578
|
+
halsteadDifficulty: ch ? ch.difficulty : 0,
|
|
579
|
+
halsteadEffort: ch ? ch.effort : 0,
|
|
580
|
+
halsteadBugs: ch ? ch.bugs : 0,
|
|
581
|
+
maintainabilityIndex: def.complexity.maintainabilityIndex ?? 0,
|
|
582
|
+
});
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
return rows;
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
/** Try the native bulk-insert fast path. Returns true if all rows were
|
|
590
|
+
* inserted successfully (caller can return early). */
|
|
591
|
+
function tryNativeBulkInsert(
|
|
539
592
|
db: BetterSqlite3Database,
|
|
540
593
|
fileSymbols: Map<string, FileSymbols>,
|
|
541
|
-
rootDir: string,
|
|
542
594
|
engineOpts?: {
|
|
543
595
|
nativeDb?: { bulkInsertComplexity?(rows: Array<Record<string, unknown>>): number };
|
|
544
596
|
suspendJsDb?: () => void;
|
|
545
597
|
resumeJsDb?: () => void;
|
|
546
598
|
},
|
|
547
|
-
):
|
|
548
|
-
// ── Native bulk-insert fast path ──────────────────────────────────────
|
|
599
|
+
): boolean {
|
|
549
600
|
const nativeDb = engineOpts?.nativeDb;
|
|
550
|
-
if (nativeDb?.bulkInsertComplexity)
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
if (!nodeId) continue;
|
|
564
|
-
const ch = def.complexity.halstead;
|
|
565
|
-
const cl = def.complexity.loc;
|
|
566
|
-
rows.push({
|
|
567
|
-
nodeId,
|
|
568
|
-
cognitive: def.complexity.cognitive ?? 0,
|
|
569
|
-
cyclomatic: def.complexity.cyclomatic ?? 0,
|
|
570
|
-
maxNesting: def.complexity.maxNesting ?? 0,
|
|
571
|
-
loc: cl ? cl.loc : 0,
|
|
572
|
-
sloc: cl ? cl.sloc : 0,
|
|
573
|
-
commentLines: cl ? cl.commentLines : 0,
|
|
574
|
-
halsteadN1: ch ? ch.n1 : 0,
|
|
575
|
-
halsteadN2: ch ? ch.n2 : 0,
|
|
576
|
-
halsteadBigN1: ch ? ch.bigN1 : 0,
|
|
577
|
-
halsteadBigN2: ch ? ch.bigN2 : 0,
|
|
578
|
-
halsteadVocabulary: ch ? ch.vocabulary : 0,
|
|
579
|
-
halsteadLength: ch ? ch.length : 0,
|
|
580
|
-
halsteadVolume: ch ? ch.volume : 0,
|
|
581
|
-
halsteadDifficulty: ch ? ch.difficulty : 0,
|
|
582
|
-
halsteadEffort: ch ? ch.effort : 0,
|
|
583
|
-
halsteadBugs: ch ? ch.bugs : 0,
|
|
584
|
-
maintainabilityIndex: def.complexity.maintainabilityIndex ?? 0,
|
|
585
|
-
});
|
|
586
|
-
}
|
|
587
|
-
if (needsJsFallback) break;
|
|
588
|
-
}
|
|
601
|
+
if (!nativeDb?.bulkInsertComplexity) return false;
|
|
602
|
+
|
|
603
|
+
const rows = collectNativeBulkRows(db, fileSymbols);
|
|
604
|
+
if (rows === null) return false; // missing complexity — needs JS fallback
|
|
605
|
+
if (rows.length === 0) return true; // nothing to insert — native path done
|
|
606
|
+
|
|
607
|
+
let inserted: number;
|
|
608
|
+
try {
|
|
609
|
+
engineOpts?.suspendJsDb?.();
|
|
610
|
+
inserted = nativeDb.bulkInsertComplexity(rows);
|
|
611
|
+
} finally {
|
|
612
|
+
engineOpts?.resumeJsDb?.();
|
|
613
|
+
}
|
|
589
614
|
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
engineOpts?.suspendJsDb?.();
|
|
594
|
-
inserted = nativeDb.bulkInsertComplexity(rows);
|
|
595
|
-
} finally {
|
|
596
|
-
engineOpts?.resumeJsDb?.();
|
|
597
|
-
}
|
|
598
|
-
if (inserted === rows.length) {
|
|
599
|
-
info(`Complexity (native bulk): ${inserted} functions analyzed`);
|
|
600
|
-
return;
|
|
601
|
-
}
|
|
602
|
-
debug(`Native bulkInsertComplexity partial: ${inserted}/${rows.length} — falling back to JS`);
|
|
603
|
-
}
|
|
615
|
+
if (inserted === rows.length) {
|
|
616
|
+
info(`Complexity (native bulk): ${inserted} functions analyzed`);
|
|
617
|
+
return true;
|
|
604
618
|
}
|
|
619
|
+
debug(`Native bulkInsertComplexity partial: ${inserted}/${rows.length} — falling back to JS`);
|
|
620
|
+
return false;
|
|
621
|
+
}
|
|
605
622
|
|
|
606
|
-
|
|
623
|
+
/** JS/WASM fallback: parse files and compute metrics via AST traversal. */
|
|
624
|
+
async function computeJsFallbackMetrics(
|
|
625
|
+
db: BetterSqlite3Database,
|
|
626
|
+
fileSymbols: Map<string, FileSymbols>,
|
|
627
|
+
rootDir: string,
|
|
628
|
+
): Promise<void> {
|
|
607
629
|
const { parsers, extToLang } = await initWasmParsersIfNeeded(fileSymbols);
|
|
608
630
|
const { getParser } = await import('../domain/parser.js');
|
|
609
631
|
|
|
@@ -649,6 +671,20 @@ export async function buildComplexityMetrics(
|
|
|
649
671
|
}
|
|
650
672
|
}
|
|
651
673
|
|
|
674
|
+
export async function buildComplexityMetrics(
|
|
675
|
+
db: BetterSqlite3Database,
|
|
676
|
+
fileSymbols: Map<string, FileSymbols>,
|
|
677
|
+
rootDir: string,
|
|
678
|
+
engineOpts?: {
|
|
679
|
+
nativeDb?: { bulkInsertComplexity?(rows: Array<Record<string, unknown>>): number };
|
|
680
|
+
suspendJsDb?: () => void;
|
|
681
|
+
resumeJsDb?: () => void;
|
|
682
|
+
},
|
|
683
|
+
): Promise<void> {
|
|
684
|
+
if (tryNativeBulkInsert(db, fileSymbols, engineOpts)) return;
|
|
685
|
+
await computeJsFallbackMetrics(db, fileSymbols, rootDir);
|
|
686
|
+
}
|
|
687
|
+
|
|
652
688
|
// ─── Query-Time Functions (re-exported from complexity-query.ts) ──────────
|
|
653
689
|
// Split to separate query-time concerns (DB reads, filtering, pagination)
|
|
654
690
|
// from compute-time concerns (AST traversal, metric algorithms).
|