gitnexus 1.6.5-rc.15 → 1.6.5-rc.16

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.
@@ -167,6 +167,65 @@ const runWithSessionLock = async (operation) => {
167
167
  }
168
168
  };
169
169
  const normalizeCopyPath = (filePath) => filePath.replace(/\\/g, '/');
170
+ const closeQueryResult = async (result) => {
171
+ try {
172
+ await result.close();
173
+ }
174
+ catch {
175
+ // Best-effort cleanup only.
176
+ }
177
+ };
178
+ const drainQueryResult = async (queryResult) => {
179
+ const results = Array.isArray(queryResult) ? queryResult : [queryResult];
180
+ let firstError;
181
+ let hasError = false;
182
+ for (const result of results) {
183
+ try {
184
+ await result.getAll();
185
+ }
186
+ catch (err) {
187
+ if (!hasError) {
188
+ firstError = err;
189
+ hasError = true;
190
+ }
191
+ }
192
+ finally {
193
+ await closeQueryResult(result);
194
+ }
195
+ }
196
+ if (hasError)
197
+ throw firstError;
198
+ };
199
+ const readQueryRows = async (queryResult) => {
200
+ const results = Array.isArray(queryResult) ? queryResult : [queryResult];
201
+ let rows = [];
202
+ let firstError;
203
+ let hasError = false;
204
+ for (let i = 0; i < results.length; i++) {
205
+ const result = results[i];
206
+ try {
207
+ const resultRows = await result.getAll();
208
+ if (i === 0)
209
+ rows = resultRows;
210
+ }
211
+ catch (err) {
212
+ if (!hasError) {
213
+ firstError = err;
214
+ hasError = true;
215
+ }
216
+ }
217
+ finally {
218
+ await closeQueryResult(result);
219
+ }
220
+ }
221
+ if (hasError)
222
+ throw firstError;
223
+ return rows;
224
+ };
225
+ const queryAndDrain = async (targetConn, cypher) => {
226
+ const queryResult = await targetConn.query(cypher);
227
+ await drainQueryResult(queryResult);
228
+ };
170
229
  export const initLbug = async (dbPath) => {
171
230
  return runWithSessionLock(() => ensureLbugInitialized(dbPath));
172
231
  };
@@ -262,7 +321,7 @@ const doInitLbug = async (dbPath) => {
262
321
  conn = opened.conn;
263
322
  for (const schemaQuery of SCHEMA_QUERIES) {
264
323
  try {
265
- await conn.query(schemaQuery);
324
+ await queryAndDrain(conn, schemaQuery);
266
325
  }
267
326
  catch (err) {
268
327
  const msg = err instanceof Error ? err.message : String(err);
@@ -312,12 +371,12 @@ export const loadGraphToLbug = async (graph, repoPath, storagePath, onProgress)
312
371
  const normalizedPath = normalizeCopyPath(csvPath);
313
372
  const copyQuery = getCopyQuery(table, normalizedPath);
314
373
  try {
315
- await conn.query(copyQuery);
374
+ await queryAndDrain(conn, copyQuery);
316
375
  }
317
376
  catch (err) {
318
377
  try {
319
378
  const retryQuery = copyQuery.replace('auto_detect=false)', 'auto_detect=false, IGNORE_ERRORS=true)');
320
- await conn.query(retryQuery);
379
+ await queryAndDrain(conn, retryQuery);
321
380
  }
322
381
  catch (retryErr) {
323
382
  const retryMsg = retryErr instanceof Error ? retryErr.message : String(retryErr);
@@ -350,12 +409,12 @@ export const loadGraphToLbug = async (graph, repoPath, storagePath, onProgress)
350
409
  log(`Loading edges: ${pairIdx}/${relsByPairMeta.size} types (${fromLabel} -> ${toLabel})`);
351
410
  }
352
411
  try {
353
- await conn.query(copyQuery);
412
+ await queryAndDrain(conn, copyQuery);
354
413
  }
355
414
  catch (err) {
356
415
  try {
357
416
  const retryQuery = copyQuery.replace('auto_detect=false)', 'auto_detect=false, IGNORE_ERRORS=true)');
358
- await conn.query(retryQuery);
417
+ await queryAndDrain(conn, retryQuery);
359
418
  }
360
419
  catch (retryErr) {
361
420
  const retryMsg = retryErr instanceof Error ? retryErr.message : String(retryErr);
@@ -475,7 +534,7 @@ const fallbackRelationshipInserts = async (validRelLines, validTables, getNodeLa
475
534
  const confidence = parseFloat(confidenceStr) || 1.0;
476
535
  const step = parseInt(stepStr) || 0;
477
536
  const esc = (s) => s.replace(/'/g, "''").replace(/\\/g, '\\\\').replace(/\n/g, '\\n').replace(/\r/g, '\\r');
478
- await conn.query(`
537
+ await queryAndDrain(conn, `
479
538
  MATCH (a:${escapeLabel(fromLabel)} {id: '${esc(fromId)}' }),
480
539
  (b:${escapeLabel(toLabel)} {id: '${esc(toId)}' })
481
540
  CREATE (a)-[:${REL_TABLE_NAME} {type: '${esc(relType)}', confidence: ${confidence}, reason: '${esc(reason)}', step: ${step}}]->(b)
@@ -589,7 +648,7 @@ export const insertNodeToLbug = async (label, properties, dbPath) => {
589
648
  if (targetDbPath) {
590
649
  const tempHandle = await openLbugConnection(lbug, targetDbPath);
591
650
  try {
592
- await tempHandle.conn.query(query);
651
+ await queryAndDrain(tempHandle.conn, query);
593
652
  return true;
594
653
  }
595
654
  finally {
@@ -598,7 +657,7 @@ export const insertNodeToLbug = async (label, properties, dbPath) => {
598
657
  }
599
658
  else if (conn) {
600
659
  // Use existing persistent connection (when called from analyze)
601
- await conn.query(query);
660
+ await queryAndDrain(conn, query);
602
661
  return true;
603
662
  }
604
663
  return false;
@@ -667,7 +726,7 @@ export const batchInsertNodesToLbug = async (nodes, dbPath) => {
667
726
  : '';
668
727
  query = `MERGE (n:${t} {id: ${escapeValue(properties.id)}}) SET n.name = ${escapeValue(properties.name)}, n.filePath = ${escapeValue(properties.filePath)}, n.startLine = ${properties.startLine || 0}, n.endLine = ${properties.endLine || 0}, n.content = ${escapeValue(properties.content || '')}${descPart}`;
669
728
  }
670
- await tempConn.query(query);
729
+ await queryAndDrain(tempConn, query);
671
730
  inserted++;
672
731
  }
673
732
  catch (e) {
@@ -686,19 +745,17 @@ export const executeQuery = async (cypher) => {
686
745
  throw new Error('LadybugDB not initialized. Call initLbug first.');
687
746
  }
688
747
  const queryResult = await conn.query(cypher);
689
- // LadybugDB uses getAll() instead of hasNext()/getNext()
690
- // Query returns QueryResult for single queries, QueryResult[] for multi-statement
691
- const result = Array.isArray(queryResult) ? queryResult[0] : queryResult;
692
- const rows = await result.getAll();
693
- return rows;
748
+ return await readQueryRows(queryResult);
694
749
  };
695
750
  export const streamQuery = async (cypher, onRow) => {
696
751
  if (!conn) {
697
752
  throw new Error('LadybugDB not initialized. Call initLbug first.');
698
753
  }
699
754
  const queryResult = await conn.query(cypher);
700
- const result = Array.isArray(queryResult) ? queryResult[0] : queryResult;
755
+ const results = Array.isArray(queryResult) ? queryResult : [queryResult];
756
+ const result = results[0];
701
757
  let rowCount = 0;
758
+ let streamError;
702
759
  try {
703
760
  while (await result.hasNext()) {
704
761
  const row = await result.getNext();
@@ -707,12 +764,17 @@ export const streamQuery = async (cypher, onRow) => {
707
764
  }
708
765
  return rowCount;
709
766
  }
767
+ catch (err) {
768
+ streamError = err;
769
+ throw err;
770
+ }
710
771
  finally {
711
772
  try {
712
- await result.close();
773
+ await drainQueryResult(results);
713
774
  }
714
- catch {
715
- // Best-effort cleanup only.
775
+ catch (err) {
776
+ if (streamError === undefined)
777
+ throw err;
716
778
  }
717
779
  }
718
780
  };
@@ -730,8 +792,7 @@ export const executePrepared = async (cypher, params) => {
730
792
  throw new Error(`Prepare failed: ${errMsg}`);
731
793
  }
732
794
  const queryResult = await conn.execute(stmt, params);
733
- const result = Array.isArray(queryResult) ? queryResult[0] : queryResult;
734
- return await result.getAll();
795
+ return await readQueryRows(queryResult);
735
796
  };
736
797
  export const executeWithReusedStatement = async (cypher, paramsList) => {
737
798
  if (!conn) {
@@ -749,7 +810,7 @@ export const executeWithReusedStatement = async (cypher, paramsList) => {
749
810
  }
750
811
  try {
751
812
  for (const params of subBatch) {
752
- await conn.execute(stmt, params);
813
+ await drainQueryResult(await conn.execute(stmt, params));
753
814
  }
754
815
  }
755
816
  catch (e) {
@@ -767,8 +828,7 @@ export const getLbugStats = async () => {
767
828
  for (const tableName of NODE_TABLES) {
768
829
  try {
769
830
  const queryResult = await conn.query(`MATCH (n:${escapeTableName(tableName)}) RETURN count(n) AS cnt`);
770
- const nodeResult = Array.isArray(queryResult) ? queryResult[0] : queryResult;
771
- const nodeRows = await nodeResult.getAll();
831
+ const nodeRows = await readQueryRows(queryResult);
772
832
  if (nodeRows.length > 0) {
773
833
  totalNodes += Number(nodeRows[0]?.cnt ?? nodeRows[0]?.[0] ?? 0);
774
834
  }
@@ -780,8 +840,7 @@ export const getLbugStats = async () => {
780
840
  let totalEdges = 0;
781
841
  try {
782
842
  const queryResult = await conn.query(`MATCH ()-[r:${REL_TABLE_NAME}]->() RETURN count(r) AS cnt`);
783
- const edgeResult = Array.isArray(queryResult) ? queryResult[0] : queryResult;
784
- const edgeRows = await edgeResult.getAll();
843
+ const edgeRows = await readQueryRows(queryResult);
785
844
  if (edgeRows.length > 0) {
786
845
  totalEdges = Number(edgeRows[0]?.cnt ?? edgeRows[0]?.[0] ?? 0);
787
846
  }
@@ -810,8 +869,7 @@ export const loadCachedEmbeddings = async () => {
810
869
  // If the query fails (column missing), we return empty cache to force a full rebuild.
811
870
  try {
812
871
  const check = await conn.query(`MATCH (e:${EMBEDDING_TABLE_NAME}) RETURN e.nodeId AS nodeId, e.chunkIndex AS chunkIndex LIMIT 1`);
813
- const checkResult = Array.isArray(check) ? check[0] : check;
814
- await checkResult.getAll();
872
+ await readQueryRows(check);
815
873
  }
816
874
  catch {
817
875
  return { embeddingNodeIds: new Set(), embeddings: [] };
@@ -833,8 +891,7 @@ export const loadCachedEmbeddings = async () => {
833
891
  throw err;
834
892
  }
835
893
  }
836
- const result = Array.isArray(rows) ? rows[0] : rows;
837
- for (const row of await result.getAll()) {
894
+ for (const row of await readQueryRows(rows)) {
838
895
  const nodeId = String(row.nodeId ?? row[0] ?? '');
839
896
  if (!nodeId)
840
897
  continue;
@@ -938,7 +995,8 @@ export const flushWAL = async () => {
938
995
  if (!conn)
939
996
  return;
940
997
  try {
941
- await conn.query('CHECKPOINT');
998
+ const checkpointResult = await conn.query('CHECKPOINT');
999
+ await drainQueryResult(checkpointResult);
942
1000
  }
943
1001
  catch {
944
1002
  /* ignore — older LadybugDB or schemaless DB may not accept it */
@@ -1036,12 +1094,11 @@ export const deleteNodesForFile = async (filePath, dbPath) => {
1036
1094
  // First count how many we'll delete
1037
1095
  const tn = escapeTableName(tableName);
1038
1096
  const countResult = await targetConn.query(`MATCH (n:${tn}) WHERE n.filePath = '${escapedPath}' RETURN count(n) AS cnt`);
1039
- const result = Array.isArray(countResult) ? countResult[0] : countResult;
1040
- const rows = await result.getAll();
1097
+ const rows = await readQueryRows(countResult);
1041
1098
  const count = Number(rows[0]?.cnt ?? rows[0]?.[0] ?? 0);
1042
1099
  if (count > 0) {
1043
1100
  // Delete nodes (and implicitly their relationships via DETACH)
1044
- await targetConn.query(`MATCH (n:${tn}) WHERE n.filePath = '${escapedPath}' DETACH DELETE n`);
1101
+ await queryAndDrain(targetConn, `MATCH (n:${tn}) WHERE n.filePath = '${escapedPath}' DETACH DELETE n`);
1045
1102
  deletedNodes += count;
1046
1103
  }
1047
1104
  }
@@ -1051,7 +1108,7 @@ export const deleteNodesForFile = async (filePath, dbPath) => {
1051
1108
  }
1052
1109
  // Also delete any embeddings for nodes in this file
1053
1110
  try {
1054
- await targetConn.query(`MATCH (e:${EMBEDDING_TABLE_NAME}) WHERE e.nodeId STARTS WITH '${escapedPath}' DELETE e`);
1111
+ await queryAndDrain(targetConn, `MATCH (e:${EMBEDDING_TABLE_NAME}) WHERE e.nodeId STARTS WITH '${escapedPath}' DELETE e`);
1055
1112
  }
1056
1113
  catch {
1057
1114
  // Embedding table may not exist or nodeId format may differ
@@ -1158,7 +1215,7 @@ export const loadFTSExtension = async (targetConn, opts = {}) => {
1158
1215
  if (!c) {
1159
1216
  throw new Error('LadybugDB not initialized. Call initLbug first.');
1160
1217
  }
1161
- const loaded = await extensionManager.ensure((sql) => c.query(sql), 'fts', 'FTS', opts);
1218
+ const loaded = await extensionManager.ensure((sql) => queryAndDrain(c, sql), 'fts', 'FTS', opts);
1162
1219
  if (loaded && useModuleState)
1163
1220
  ftsLoaded = true;
1164
1221
  return loaded;
@@ -1186,7 +1243,7 @@ export const loadVectorExtension = async (targetConn, opts = {}) => {
1186
1243
  if (!c) {
1187
1244
  throw new Error('LadybugDB not initialized. Call initLbug first.');
1188
1245
  }
1189
- const loaded = await extensionManager.ensure((sql) => c.query(sql), 'VECTOR', 'VECTOR', opts);
1246
+ const loaded = await extensionManager.ensure((sql) => queryAndDrain(c, sql), 'VECTOR', 'VECTOR', opts);
1190
1247
  if (loaded && useModuleState)
1191
1248
  vectorExtensionLoaded = true;
1192
1249
  return loaded;
@@ -1211,7 +1268,7 @@ export const createFTSIndex = async (tableName, indexName, properties, stemmer =
1211
1268
  const propList = properties.map((p) => `'${p}'`).join(', ');
1212
1269
  const query = `CALL CREATE_FTS_INDEX('${tableName}', '${indexName}', [${propList}], stemmer := '${stemmer}')`;
1213
1270
  try {
1214
- await conn.query(query);
1271
+ await queryAndDrain(conn, query);
1215
1272
  ensuredFTSIndexes.add(key);
1216
1273
  }
1217
1274
  catch (e) {
@@ -1280,8 +1337,7 @@ export const queryFTS = async (tableName, indexName, query, limit = 20, conjunct
1280
1337
  `;
1281
1338
  try {
1282
1339
  const queryResult = await conn.query(cypher);
1283
- const result = Array.isArray(queryResult) ? queryResult[0] : queryResult;
1284
- const rows = await result.getAll();
1340
+ const rows = await readQueryRows(queryResult);
1285
1341
  return rows.map((row) => {
1286
1342
  const node = row.node || row[0] || {};
1287
1343
  const score = row.score ?? row[1] ?? 0;
@@ -1310,7 +1366,7 @@ export const dropFTSIndex = async (tableName, indexName) => {
1310
1366
  throw new Error('LadybugDB not initialized. Call initLbug first.');
1311
1367
  }
1312
1368
  try {
1313
- await conn.query(`CALL DROP_FTS_INDEX('${tableName}', '${indexName}')`);
1369
+ await queryAndDrain(conn, `CALL DROP_FTS_INDEX('${tableName}', '${indexName}')`);
1314
1370
  }
1315
1371
  catch {
1316
1372
  // Index may not exist
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gitnexus",
3
- "version": "1.6.5-rc.15",
3
+ "version": "1.6.5-rc.16",
4
4
  "description": "Graph-powered code intelligence for AI agents. Index any codebase, query via MCP or CLI.",
5
5
  "author": "Abhigyan Patwari",
6
6
  "license": "PolyForm-Noncommercial-1.0.0",