@xhmikosr/bin-check
@xhmikosr/bin-check answers one narrow question: can this exact executable path be run successfully with these arguments? It first checks executable permissions with isexe, then launches the process through execa and resolves to true only for exit code zero. It provides asynchronous and synchronous calls and defaults to `--help` when arguments are omitted. It does not search PATH, install binaries, inspect versions, capture output for callers, or let you configure timeouts and environment variables.
Useful as a focused smoke test for a known binary path, especially in installers that already target modern ESM Node. Use execa directly when you need any process detail or safety control beyond a boolean exit-code check.
Use it if
- You ship or download a helper executable and need a quick post-install smoke test against its exact path
- You want to distinguish a missing or non-executable file from a command that ran and returned a failing exit code
- You need the same tiny boolean check in asynchronous setup code and synchronous startup code
- Your project is ESM-only on Node 20 or newer and already accepts execa in its dependency tree
- You need to find a command by name on PATH; the API requires a path and calls isexe on that string before spawning it
- You need stdout, stderr, exit signals, timeouts, cwd, env, or cancellation; the wrapper returns only a boolean and exposes no execa options
- Your package supports Node 18 or CommonJS; version 8.2.2 requires Node 20 or newer and publishes only an ESM default export
- You need TypeScript declarations; the published package contains index.js but declares no types file
- You can call execa directly; if you already depend on it, this package adds isexe and a narrow wrapper around `reject: false` for little extra value
Setup reality
Install with `npm install @xhmikosr/bin-check`. Version 8.2.2 requires Node 20 or newer, is marked `type: module`, and exposes only an ESM default export, so `require()` is not a supported entry path. There are no peers, native builds, credentials, or config files, but the runtime dependency chain includes execa 9.6.1 and isexe 4.0.0. Pass an actual executable path, not merely a command such as `git`: bin-check runs isexe against the supplied string and does not perform shell or PATH resolution. If the file is missing or lacks executable permission, the promise rejects or the sync call throws with a permissions-oriented error. If the file launches and exits nonzero, the result is false because execa is called with `reject: false`. Those are different failure channels and production setup code should handle both. Omitting the arguments array uses `['--help']`; that is not universally safe because some tools lack `--help`, write it to stderr, open a UI, wait for input, or return nonzero for help. Pick a fast, side-effect-free probe such as `--version` when the target documents one. There is no timeout or AbortSignal option, so a hanging binary can hang the check. The synchronous method blocks Node's event loop and belongs in short startup or install scripts, not request handling. TypeScript users need a local declaration or an untyped import because the package publishes no declaration file.
Patterns
Check an executable asynchronouslycheck-binary-async
import binCheck from '@xhmikosr/bin-check'
const works = await binCheck('/usr/bin/tool', ['--version'])
if (!works) throw new Error('tool returned a failing exit code')Use an exact path. A bare command name is not resolved through PATH before the executability check.
Use the default help probeuse-default-probe
import binCheck from '@xhmikosr/bin-check'
const works = await binCheck('/usr/bin/tool')Omitting arguments runs `--help`; confirm that the target supports it and exits zero without waiting for input.
Distinguish launch errors from bad exit codeshandle-check-errors
try {
const works = await binCheck(binaryPath, ['--version'])
if (!works) console.error('Executable ran but exited nonzero')
} catch (error) {
console.error('Missing or not executable:', error.message)
}A non-executable path rejects; a successfully launched process with a nonzero exit code resolves false.
Run a synchronous startup checkcheck-binary-sync
import binCheck from '@xhmikosr/bin-check'
if (!binCheck.sync('/usr/bin/tool', ['--version'])) {
throw new Error('tool failed its startup probe')
}The sync form blocks the event loop until the child exits and has no timeout, so keep it out of request handlers.
Smoke-test the current Node executablecheck-node-runtime
import process from 'node:process'
import binCheck from '@xhmikosr/bin-check'
const works = await binCheck(process.execPath, ['--version'])process.execPath is an absolute path, which matches bin-check's exact-path requirement.
Select a packaged binary by platformcheck-platform-binary
import path from 'node:path'
import process from 'node:process'
import binCheck from '@xhmikosr/bin-check'
const filename = process.platform === 'win32' ? 'tool.exe' : 'tool'
const binaryPath = path.join(packageRoot, 'bin', process.platform, process.arch, filename)
const works = await binCheck(binaryPath, ['--version'])A real binary package must also map unsupported platform and architecture combinations before calling bin-check.
Set Unix permissions before probing a downloadset-executable-permission
import { chmod } from 'node:fs/promises'
import binCheck from '@xhmikosr/bin-check'
if (process.platform !== 'win32') await chmod(binaryPath, 0o755)
const works = await binCheck(binaryPath, ['--version'])Only change permissions on a file you installed and verified; bin-check does not repair permissions itself.
Probe several known executable pathscheck-several-binaries
const probes = [
['/opt/tools/a', ['--version']],
['/opt/tools/b', ['version']],
]
const results = await Promise.all(probes.map(async ([file, args]) => ({
file,
works: await binCheck(file, args),
})))Any missing or non-executable path rejects Promise.all; use Promise.allSettled when every diagnostic must be retained.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| execa | npm | Choose it when you need output, timeout, cancellation, environment, cwd, or detailed process errors |
| command-exists | npm | Choose it when the real question is whether a command name can be resolved on PATH |
| which | npm | Choose it to resolve command names to executable paths before deciding how to probe them |
| isexe | npm | Choose it when checking file executability is enough and launching the program would be undesirable |