Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion gitnexus-shared/src/graph/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ export type NodeProperties = {
errorKeys?: string[];
middleware?: string[];
// Extensible
[key: string]: any;
[key: string]: unknown;
};

export type RelationshipType =
Expand Down
2 changes: 1 addition & 1 deletion gitnexus/src/core/ingestion/ast-cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export const createASTCache = (maxSize: number = 50): ASTCache => {
try {
// NOTE: web-tree-sitter has tree.delete(); native tree-sitter trees are GC-managed.
// Keep this try/catch so we don't crash on either runtime.
(tree as any).delete?.();
(tree as unknown as { delete?: () => void }).delete?.();
} catch (e) {
console.warn('Failed to delete tree from WASM memory', e);
}
Expand Down
4 changes: 2 additions & 2 deletions gitnexus/src/core/ingestion/call-routing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,8 @@ export function routeRubyCall(calledName: string, callNode: SyntaxNode): RubyCal
// ── require / require_relative → import ─────────────────────────────────
if (calledName === 'require' || calledName === 'require_relative') {
const argList = callNode.childForFieldName?.('arguments');
const stringNode = argList?.children?.find((c: any) => c.type === 'string');
const contentNode = stringNode?.children?.find((c: any) => c.type === 'string_content');
const stringNode = argList?.children?.find((c: SyntaxNode) => c.type === 'string');
const contentNode = stringNode?.children?.find((c: SyntaxNode) => c.type === 'string_content');
if (!contentNode) return SKIP_RESULT;

let importPath: string = contentNode.text;
Expand Down
38 changes: 29 additions & 9 deletions gitnexus/src/core/ingestion/community-processor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
// (src/communities-leiden) because it was never published to npm.
// We use createRequire to load the CommonJS vendored files in ESM context.
import Graph from 'graphology';
import type { AbstractGraph, Attributes } from 'graphology-types';
import { createRequire } from 'node:module';
import { fileURLToPath } from 'node:url';
import { dirname, resolve } from 'node:path';
Expand All @@ -23,7 +24,22 @@ const __dirname = dirname(__filename);
// Navigate to package root (works from both src/ and dist/)
const leidenPath = resolve(__dirname, '..', '..', '..', 'vendor', 'leiden', 'index.cjs');
const _require = createRequire(import.meta.url);
const leiden = _require(leidenPath);
/** Graphology Graph instance type (AbstractGraph from graphology-types avoids CJS/ESM interop namespace issue) */
type GraphInstance = AbstractGraph<Attributes, Attributes, Attributes>;

const leiden: LeidenModule = _require(leidenPath);

/** Vendored Leiden algorithm module shape */
interface LeidenModule {
detailed: (graph: GraphInstance, options: Record<string, unknown>) => LeidenDetailedResult;
}

/** Result returned by leiden.detailed() */
interface LeidenDetailedResult {
communities: Record<string, number>;
count: number;
modularity: number;
}

// ============================================================================
// TYPES
Expand Down Expand Up @@ -127,16 +143,16 @@ export const processCommunities = async (
// The first 2 iterations capture ~95%+ of modularity; additional iterations have diminishing returns.
// Timeout: abort after 60s for pathological graph structures.
const LEIDEN_TIMEOUT_MS = 60_000;
let details: any;
let details: LeidenDetailedResult;
try {
details = await Promise.race([
Promise.resolve(
(leiden as any).detailed(graph, {
leiden.detailed(graph, {
resolution: isLarge ? 2.0 : 1.0,
maxIterations: isLarge ? 3 : 0,
}),
),
new Promise((_, reject) =>
new Promise<never>((_, reject) =>
setTimeout(() => reject(new Error('Leiden timeout')), LEIDEN_TIMEOUT_MS),
),
]);
Expand Down Expand Up @@ -199,8 +215,12 @@ export const processCommunities = async (
*/
const MIN_CONFIDENCE_LARGE = 0.5;

const buildGraphologyGraph = (knowledgeGraph: KnowledgeGraph, isLarge: boolean): any => {
const graph = new (Graph as any)({ type: 'undirected', allowSelfLoops: false });
const buildGraphologyGraph = (knowledgeGraph: KnowledgeGraph, isLarge: boolean): GraphInstance => {
const GraphCtor = Graph as unknown as new (options: {
type: string;
allowSelfLoops: boolean;
}) => GraphInstance;
const graph = new GraphCtor({ type: 'undirected', allowSelfLoops: false });

const symbolTypes = new Set<NodeLabel>(['Function', 'Class', 'Method', 'Interface']);
const clusteringRelTypes = new Set(['CALLS', 'EXTENDS', 'IMPLEMENTS']);
Expand Down Expand Up @@ -257,7 +277,7 @@ const buildGraphologyGraph = (knowledgeGraph: KnowledgeGraph, isLarge: boolean):
const createCommunityNodes = (
communities: Record<string, number>,
communityCount: number,
graph: any,
graph: GraphInstance,
knowledgeGraph: KnowledgeGraph,
): CommunityNode[] => {
// Group node IDs by community
Expand Down Expand Up @@ -312,7 +332,7 @@ const createCommunityNodes = (
const generateHeuristicLabel = (
memberIds: string[],
nodePathMap: Map<string, string>,
graph: any,
graph: GraphInstance,
commNum: number,
): string => {
// Collect folder names from file paths
Expand Down Expand Up @@ -397,7 +417,7 @@ const findCommonPrefix = (strings: string[]): string => {
* Estimate cohesion score (0-1) based on internal edge density.
* Uses sampling for large communities to avoid O(N^2) cost.
*/
const calculateCohesion = (memberIds: string[], graph: any): number => {
const calculateCohesion = (memberIds: string[], graph: GraphInstance): number => {
if (memberIds.length <= 1) return 1.0;

const memberSet = new Set(memberIds);
Expand Down
2 changes: 1 addition & 1 deletion gitnexus/src/core/ingestion/import-processor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -352,7 +352,7 @@ export const processImports = async (
console.groupEnd();
}

if (wasReparsed) (tree as any).delete?.();
if (wasReparsed) (tree as unknown as { delete?: () => void }).delete?.();
continue;
}

Expand Down
27 changes: 14 additions & 13 deletions gitnexus/src/core/ingestion/languages/php.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { phpExportChecker } from '../export-detection.js';
import { resolvePhpImport } from '../import-resolvers/php.js';
import { extractPhpNamedBindings } from '../named-bindings/php.js';
import { PHP_QUERIES } from '../tree-sitter-queries.js';
import { findDescendant, extractStringContent } from '../utils/ast-helpers.js';
import { findDescendant, extractStringContent, type SyntaxNode } from '../utils/ast-helpers.js';
import type { NodeLabel } from 'gitnexus-shared';
import { createFieldExtractor } from '../field-extractors/generic.js';
import { phpConfig as phpFieldConfig } from '../field-extractors/configs/php.js';
Expand Down Expand Up @@ -128,7 +128,7 @@ const ELOQUENT_RELATIONS = new Set([
* For a PHP property_declaration node, extract array values as a description string.
* Returns null if not an Eloquent model property or no array values found.
*/
function extractPhpPropertyDescription(propName: string, propDeclNode: any): string | null {
function extractPhpPropertyDescription(propName: string, propDeclNode: SyntaxNode): string | null {
if (!ELOQUENT_ARRAY_PROPS.has(propName)) return null;

const arrayNode = findDescendant(propDeclNode, 'array_creation_expression');
Expand All @@ -138,7 +138,7 @@ function extractPhpPropertyDescription(propName: string, propDeclNode: any): str
for (const child of arrayNode.children ?? []) {
if (child.type !== 'array_element_initializer') continue;
const children = child.children ?? [];
const arrowIdx = children.findIndex((c: any) => c.type === '=>');
const arrowIdx = children.findIndex((c: SyntaxNode) => c.type === '=>');
if (arrowIdx !== -1) {
const key = extractStringContent(children[arrowIdx - 1]);
const val = extractStringContent(children[arrowIdx + 1]);
Expand All @@ -156,14 +156,14 @@ function extractPhpPropertyDescription(propName: string, propDeclNode: any): str
* For a PHP method_declaration node, detect if it defines an Eloquent relationship.
* Returns description like "hasMany(Post)" or null.
*/
function extractEloquentRelationDescription(methodNode: any): string | null {
function findRelationCall(node: any): any {
function extractEloquentRelationDescription(methodNode: SyntaxNode): string | null {
function findRelationCall(node: SyntaxNode): SyntaxNode | null {
if (node.type === 'member_call_expression') {
const children = node.children ?? [];
const objectNode = children.find(
(c: any) => c.type === 'variable_name' && c.text === '$this',
(c: SyntaxNode) => c.type === 'variable_name' && c.text === '$this',
);
const nameNode = children.find((c: any) => c.type === 'name');
const nameNode = children.find((c: SyntaxNode) => c.type === 'name');
if (objectNode && nameNode && ELOQUENT_RELATIONS.has(nameNode.text)) return node;
}
for (const child of node.children ?? []) {
Expand All @@ -176,17 +176,18 @@ function extractEloquentRelationDescription(methodNode: any): string | null {
const callNode = findRelationCall(methodNode);
if (!callNode) return null;

const relType = callNode.children?.find((c: any) => c.type === 'name')?.text;
const argsNode = callNode.children?.find((c: any) => c.type === 'arguments');
const relType = callNode.children?.find((c: SyntaxNode) => c.type === 'name')?.text;
const argsNode = callNode.children?.find((c: SyntaxNode) => c.type === 'arguments');
let targetModel: string | null = null;
if (argsNode) {
const firstArg = argsNode.children?.find((c: any) => c.type === 'argument');
const firstArg = argsNode.children?.find((c: SyntaxNode) => c.type === 'argument');
if (firstArg) {
const classConstant = firstArg.children?.find(
(c: any) => c.type === 'class_constant_access_expression',
(c: SyntaxNode) => c.type === 'class_constant_access_expression',
);
if (classConstant) {
targetModel = classConstant.children?.find((c: any) => c.type === 'name')?.text ?? null;
targetModel =
classConstant.children?.find((c: SyntaxNode) => c.type === 'name')?.text ?? null;
}
}
}
Expand All @@ -203,7 +204,7 @@ function extractEloquentRelationDescription(methodNode: any): string | null {
function phpDescriptionExtractor(
nodeLabel: NodeLabel,
nodeName: string,
captureMap: Record<string, any>,
captureMap: Record<string, SyntaxNode>,
): string | undefined {
if (nodeLabel === 'Property' && captureMap['definition.property']) {
return extractPhpPropertyDescription(nodeName, captureMap['definition.property']) ?? undefined;
Expand Down
24 changes: 12 additions & 12 deletions gitnexus/src/core/ingestion/parsing-processor.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { GraphNode, GraphRelationship } from 'gitnexus-shared';
import type { GraphNode, GraphRelationship, NodeLabel } from 'gitnexus-shared';
import { KnowledgeGraph } from '../graph/types.js';
import Parser from 'tree-sitter';
import { loadParser, loadLanguage, isLanguageAvailable } from '../tree-sitter/parser-loader.js';
Expand Down Expand Up @@ -114,7 +114,7 @@ const processParsingWithWorkers = async (
for (const node of result.nodes) {
graph.addNode({
id: node.id,
label: node.label as any,
label: node.label as NodeLabel,
properties: node.properties,
});
}
Expand Down Expand Up @@ -184,10 +184,10 @@ const processParsingWithWorkers = async (

// Inline caches to avoid repeated parent-walks per node (same pattern as parse-worker.ts).
// Keyed by tree-sitter node reference — cleared at the start of each file.
const classIdCache = new Map<any, string | null>();
const exportCache = new Map<any, boolean>();
const classIdCache = new Map<SyntaxNode, string | null>();
const exportCache = new Map<SyntaxNode, boolean>();

const cachedFindEnclosingClassId = (node: any, filePath: string): string | null => {
const cachedFindEnclosingClassId = (node: SyntaxNode, filePath: string): string | null => {
const cached = classIdCache.get(node);
if (cached !== undefined) return cached;
const result = findEnclosingClassId(node, filePath);
Expand All @@ -196,8 +196,8 @@ const cachedFindEnclosingClassId = (node: any, filePath: string): string | null
};

const cachedExportCheck = (
checker: (node: any, name: string) => boolean,
node: any,
checker: (node: SyntaxNode, name: string) => boolean,
node: SyntaxNode,
name: string,
): boolean => {
const cached = exportCache.get(node);
Expand All @@ -210,7 +210,7 @@ const cachedExportCheck = (
// FieldExtractor cache for sequential path — same pattern as parse-worker.ts
const seqFieldInfoCache = new Map<number, Map<string, FieldInfo>>();

function seqFindEnclosingClassNode(node: any): any | null {
function seqFindEnclosingClassNode(node: SyntaxNode): SyntaxNode | null {
let current = node.parent;
while (current) {
if (CLASS_CONTAINER_TYPES.has(current.type)) return current;
Expand All @@ -221,11 +221,11 @@ function seqFindEnclosingClassNode(node: any): any | null {

/** Minimal no-op SymbolTable stub for FieldExtractorContext (sequential path has a real
* SymbolTable, but it's incomplete at this stage — use the stub for safety). */
const NOOP_SYMBOL_TABLE_SEQ: any = {
const NOOP_SYMBOL_TABLE_SEQ = {
lookupExactAll: () => [],
lookupExact: () => undefined,
lookupExactFull: () => undefined,
};
} as unknown as SymbolTable;

function seqGetFieldInfo(
classNode: SyntaxNode,
Expand Down Expand Up @@ -321,7 +321,7 @@ const processParsingSequential = async (
: null;

matches.forEach((match) => {
const captureMap: Record<string, any> = {};
const captureMap: Record<string, SyntaxNode> = {};

match.captures.forEach((c) => {
captureMap[c.name] = c.node;
Expand Down Expand Up @@ -372,7 +372,7 @@ const processParsingSequential = async (

const node: GraphNode = {
id: nodeId,
label: nodeLabel as any,
label: nodeLabel as NodeLabel,
properties: {
name: nodeName,
filePath: file.path,
Expand Down
8 changes: 5 additions & 3 deletions gitnexus/src/core/ingestion/type-extractors/c-cpp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,11 @@ const SMART_PTR_WRAPPERS = new Set(['shared_ptr', 'unique_ptr', 'weak_ptr']);
* Unwraps type_descriptor wrappers common in tree-sitter-cpp ASTs.
* Returns undefined if no template arguments or no type found. */
export const extractFirstTemplateTypeArg = (parentNode: SyntaxNode): string | undefined => {
const templateArgs = parentNode.children.find((c: any) => c.type === 'template_argument_list');
const templateArgs = parentNode.children.find(
(c: SyntaxNode) => c.type === 'template_argument_list',
);
if (!templateArgs?.firstNamedChild) return undefined;
let argNode: any = templateArgs.firstNamedChild;
let argNode: SyntaxNode | null = templateArgs.firstNamedChild;
if (argNode.type === 'type_descriptor') {
const inner = argNode.childForFieldName('type');
if (inner) argNode = inner;
Expand Down Expand Up @@ -141,7 +143,7 @@ const extractInitializer: InitializerExtractor = (
func.type === 'template_function'
? func
: func.type === 'qualified_identifier' || func.type === 'scoped_identifier'
? (func.namedChildren.find((c: any) => c.type === 'template_function') ?? null)
? (func.namedChildren.find((c: SyntaxNode) => c.type === 'template_function') ?? null)
: null;
if (templateFunc) {
const nameNode = templateFunc.firstNamedChild;
Expand Down
Loading
Loading