/** * bun-napi-shim — intercept native-module loads that would otherwise crash * Bun's NAPI loader before testcontainers' fallback paths run. * * Background: `dockerode` (which testcontainers uses for the Docker socket) * pulls `ssh2` and `cpu-features`. Both packages load `.node` binaries via * a wrapped `require()` with a `try { require(...) } catch {}` fallback to * pure-JS implementations. Under Bun, however, the underlying NAPI loader * panics on `uv_version_string` (oven-sh/bun#18546) BEFORE the catch block * runs, killing the runner. * * This shim intercepts `Module._resolveFilename` for the two known offending * `.node` paths and throws a plain JS error instead. ssh2/cpu-features then * catch it and fall back to pure-JS code paths. Safe no-op under Node. * * Loaded as a side-effect by `src/setup/global-setup.ts` so every consumer * who wires `createIntegrationSetup`'s returned `globalSetup` path gets the * shim automatically; no per-consumer wiring needed. * * @module */ import { createRequire } from "node:module"; interface NodeModule { _resolveFilename: (request: string, ...rest: unknown[]) => string; } const BLOCKED_NATIVE_SUFFIXES = [ "/sshcrypto.node", "/cpufeatures.node", ] as const; let shimInstalled = false; /** * Install the Module._resolveFilename interceptor when running under Bun. * * Idempotent: subsequent calls are no-ops. Returns `true` if the shim was * installed (either now or previously), `false` if not running under Bun. * * @param options.moduleRef - Optional override for `node:module`. Used by * tests to inject a fake `Module` whose `_resolveFilename` can be inspected. * @param options.processRef - Optional override for the global `process`. * Used by tests to simulate Bun vs Node detection without actually running * under Bun. */ export function installBunNapiShim( options: { moduleRef?: NodeModule; processRef?: { versions?: { bun?: string } }; } = {} ): boolean { const proc = options.processRef ?? /* istanbul ignore next */ (typeof process !== "undefined" ? (process as unknown as { versions?: { bun?: string } }) : undefined); if (proc?.versions?.bun === undefined) return false; if (shimInstalled) return true; const mod = options.moduleRef ?? /* istanbul ignore next */ (createRequire(import.meta.url)( "node:module" ) as NodeModule); const original = mod._resolveFilename; mod._resolveFilename = function patched( this: unknown, request: string, ...rest: unknown[] ): string { if (BLOCKED_NATIVE_SUFFIXES.some((s) => request.endsWith(s))) { throw new Error( `[bun-napi-shim] blocked native module load (${request}); pure-JS fallback engaged` ); } return original.call(this, request, ...rest); }; shimInstalled = true; return true; } /** Test-only: reset the install flag so a second `installBunNapiShim` call patches again. */ export function _resetForTests(): void { shimInstalled = false; }