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
23 changes: 14 additions & 9 deletions src/lib/natives.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
// `security defend` needs (dropped from the 3.28 tree — ruvnet/ruflo#2670).
import fs from 'node:fs';
import path from 'node:path';
import { createRequire } from 'node:module';
import { rufloNodeModules, aqeRoot } from './paths.mjs';

/** agentdb locations under the global ruflo tree (mirrors ruflo-patch-native). */
Expand All @@ -16,15 +15,21 @@ export function agentdbLocations() {
.filter((p) => fs.existsSync(p));
}

/** Package root of better-sqlite3 as resolved from `fromDir` (real Node
* resolution), or null if not resolvable. */
/** Package root of better-sqlite3 as resolved from `fromDir`, or null if not
* found. Walks up the node_modules chain reading disk fresh on every call —
* the node resolution equivalent, but WITHOUT createRequire().resolve(), whose
* process-wide cache (Module._pathCache/_realpathCache) goes stale after an
* in-process `npm install` reshapes the tree. That staleness made `sync`'s
* final convergence proof report a false WASM fallback on a location the
* earlier heal (and a fresh process) both saw as native. */
export function bsq3Root(fromDir) {
try {
const req = createRequire(path.join(fromDir, 'noop.js'));
const entry = req.resolve('better-sqlite3'); // …/better-sqlite3/lib/index.js
return path.join(entry.slice(0, entry.lastIndexOf(`${path.sep}better-sqlite3${path.sep}`)), 'better-sqlite3');
} catch {
return null;
let dir = path.resolve(fromDir);
for (;;) {
const cand = path.join(dir, 'node_modules', 'better-sqlite3');
if (fs.existsSync(path.join(cand, 'package.json'))) return cand;
const parent = path.dirname(dir);
if (parent === dir) return null; // reached filesystem root
dir = parent;
}
}

Expand Down
32 changes: 32 additions & 0 deletions tests/kit/natives.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,35 @@ test('bsq3IsNative is false for a WASM-fallback install (no binding file)', () =
assert.equal(bsq3IsNative(dir), false);
fs.rmSync(dir, { recursive: true, force: true });
});

test('bsq3Root finds better-sqlite3 hoisted up the node_modules chain', () => {
// Real layout: ruflo/node_modules/agentdb resolves better-sqlite3 from the
// hoisted ruflo/node_modules/better-sqlite3 — walk up, don't only look nested.
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'ak-natives-hoist-'));
const fromDir = path.join(root, 'node_modules', 'agentdb');
const hoisted = path.join(root, 'node_modules', 'better-sqlite3');
fs.mkdirSync(fromDir, { recursive: true });
fs.mkdirSync(hoisted, { recursive: true });
fs.writeFileSync(path.join(hoisted, 'package.json'), JSON.stringify({ name: 'better-sqlite3' }));
assert.equal(bsq3Root(fromDir), hoisted);
fs.rmSync(root, { recursive: true, force: true });
});

test('bsq3Root follows a nested→hoisted move in-process (sync convergence bug)', () => {
// The false-negative: healNatives resolves better-sqlite3 to a NESTED copy,
// then an in-process `npm install` (aidefence) dedupes it away, leaving only a
// HOISTED copy. The final proof must resolve to the hoisted copy — reading disk
// fresh — not a stale cached root pointing at the removed nested path.
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'ak-natives-move-'));
const fromDir = path.join(root, 'node_modules', 'agentdb');
const nested = path.join(fromDir, 'node_modules', 'better-sqlite3');
const hoisted = path.join(root, 'node_modules', 'better-sqlite3');
for (const p of [nested, hoisted]) {
fs.mkdirSync(p, { recursive: true });
fs.writeFileSync(path.join(p, 'package.json'), JSON.stringify({ name: 'better-sqlite3' }));
}
assert.equal(bsq3Root(fromDir), nested, 'prefers the nested copy while it exists');
fs.rmSync(nested, { recursive: true, force: true }); // simulate npm dedupe mid-sync
assert.equal(bsq3Root(fromDir), hoisted, 'resolves to the hoisted copy after the move');
fs.rmSync(root, { recursive: true, force: true });
});
Loading