gitnexus 1.6.4-rc.79 → 1.6.4-rc.80

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.
@@ -1,6 +1,6 @@
1
1
  import fsp from 'node:fs/promises';
2
2
  import path from 'node:path';
3
- import { createHash } from 'node:crypto';
3
+ import { createHash, randomBytes } from 'node:crypto';
4
4
  import lbug from '@ladybugdb/core';
5
5
  import { BRIDGE_SCHEMA_QUERIES, BRIDGE_SCHEMA_VERSION } from './bridge-schema.js';
6
6
  import { closeLbugConnection, openLbugConnection, } from '../lbug/lbug-config.js';
@@ -17,7 +17,7 @@ import { dedupeContracts, dedupeCrossLinks } from './normalization.js';
17
17
  * - `.shadow` — non-blocking concurrent checkpoint sidecar (added in
18
18
  * LadybugDB 0.15.4); same pairing constraint as `.wal`.
19
19
  *
20
- * `bridge-db` writes to a `bridge.lbug.tmp` file and then atomically renames
20
+ * `bridge-db` writes to a `bridge.lbug.tmp.<random>` file and then atomically renames
21
21
  * it into place. The rename only moves the main file; sidecars must be
22
22
  * cleaned up explicitly or the next writer trips the database-id check.
23
23
  */
@@ -33,6 +33,24 @@ async function removeLbugFile(basePath) {
33
33
  }
34
34
  }
35
35
  }
36
+ /**
37
+ * Remove all stale `bridge.lbug.tmp.*` files (and their sidecars) from a
38
+ * group directory. With randomBytes-based temp names, a crashed writeBridge
39
+ * leaves behind a uniquely-named tmp file that no future run will target by
40
+ * name — so we glob for the prefix and clean up everything matching.
41
+ */
42
+ async function cleanStaleBridgeTmpFiles(groupDir) {
43
+ try {
44
+ const entries = await fsp.readdir(groupDir);
45
+ const staleBases = entries.filter((e) => e.startsWith('bridge.lbug.tmp.') && !LBUG_SIDECAR_SUFFIXES.some((s) => e.endsWith(s)));
46
+ for (const name of staleBases) {
47
+ await removeLbugFile(path.join(groupDir, name));
48
+ }
49
+ }
50
+ catch {
51
+ /* best-effort: directory may not exist yet */
52
+ }
53
+ }
36
54
  export function contractNodeId(repo, contractId, role, filePath) {
37
55
  return createHash('sha256').update(`${repo}\0${contractId}\0${role}\0${filePath}`).digest('hex');
38
56
  }
@@ -211,7 +229,7 @@ export async function retryRename(src, dst, attempts = 3) {
211
229
  /* ------------------------------------------------------------------ */
212
230
  export async function writeBridgeMeta(groupDir, meta) {
213
231
  const target = path.join(groupDir, 'meta.json');
214
- const tmp = `${target}.tmp.${Date.now()}`;
232
+ const tmp = `${target}.tmp.${randomBytes(8).toString('hex')}`;
215
233
  await fsp.writeFile(tmp, JSON.stringify(meta, null, 2), 'utf-8');
216
234
  // Use retryRename for consistency with writeBridge's atomic swap — on
217
235
  // Windows a concurrent reader can cause EBUSY/EPERM even on a tiny
@@ -244,7 +262,7 @@ export async function writeBridge(groupDir, input) {
244
262
  const contracts = dedupeContracts(input.contracts);
245
263
  const crossLinks = dedupeCrossLinks(input.crossLinks);
246
264
  const finalPath = path.join(groupDir, 'bridge.lbug');
247
- const tmpPath = path.join(groupDir, 'bridge.lbug.tmp');
265
+ const tmpPath = path.join(groupDir, `bridge.lbug.tmp.${randomBytes(8).toString('hex')}`);
248
266
  const bakPath = path.join(groupDir, 'bridge.lbug.bak');
249
267
  const report = {
250
268
  contractsInserted: 0,
@@ -261,11 +279,12 @@ export async function writeBridge(groupDir, input) {
261
279
  report.sampleErrors.push({ kind, id, message: errMessage(err) });
262
280
  }
263
281
  };
264
- // Clean up any leftover tmp main file AND its `.wal` / `.shadow` sidecars.
265
- // LadybugDB 0.16.0 rejects opening a database whose sidecars belong to a
266
- // different database instance (database-id check), so any stale sidecar
267
- // from a crashed previous run will fail the next writeBridge.
268
- await removeLbugFile(tmpPath);
282
+ // Clean up stale tmp files left behind by previously crashed writeBridge
283
+ // runs. With randomBytes-based names each run picks a unique path, so
284
+ // the old fixed-name `removeLbugFile(tmpPath)` was a no-op — stale
285
+ // artifacts accumulated. The glob-based helper finds *all* leftover
286
+ // `bridge.lbug.tmp.*` entries and removes them (including sidecars).
287
+ await cleanStaleBridgeTmpFiles(groupDir);
269
288
  // 1. Create temp DB, insert all data.
270
289
  //
271
290
  // Everything after `openBridgeDb` must run inside a try/finally so that
@@ -2,6 +2,7 @@ import * as fs from 'node:fs';
2
2
  import * as fsp from 'node:fs/promises';
3
3
  import * as path from 'node:path';
4
4
  import * as os from 'node:os';
5
+ import { randomBytes } from 'node:crypto';
5
6
  const CONTRACTS_FILE = 'contracts.json';
6
7
  export function getDefaultGitnexusDir() {
7
8
  return process.env.GITNEXUS_HOME || path.join(os.homedir(), '.gitnexus');
@@ -21,7 +22,7 @@ export function getGroupDir(gitnexusDir, groupName) {
21
22
  }
22
23
  export async function writeContractRegistry(groupDir, registry) {
23
24
  const targetPath = path.join(groupDir, CONTRACTS_FILE);
24
- const tmpPath = `${targetPath}.tmp.${Date.now()}`;
25
+ const tmpPath = `${targetPath}.tmp.${randomBytes(8).toString('hex')}`;
25
26
  await fsp.writeFile(tmpPath, JSON.stringify(registry, null, 2), 'utf-8');
26
27
  await fsp.rename(tmpPath, targetPath);
27
28
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gitnexus",
3
- "version": "1.6.4-rc.79",
3
+ "version": "1.6.4-rc.80",
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",