Overview
The public API surface of pompelmi is intentionally small. The main entry point exports a single object with one method:
const pompelmi = require('pompelmi');
// pompelmi.scan — the only public method
The installer and updater are internal utilities exposed as named exports from
their own modules. They are not re-exported from index.js.
| Symbol | Module | Type | Public? |
|---|---|---|---|
pompelmi.scan |
index.js |
Function → Promise | Yes |
ClamAVInstaller |
ClamAVInstaller.js |
Function → Promise | Internal |
updateClamAVDatabase |
ClamAVDatabaseUpdater.js |
Function → Promise | Internal |
SCAN_RESULTS |
config.js |
Frozen object | Internal |
pompelmi.scan(filePath, [options])
Scans a file for malware and returns a Promise that resolves to a result string.
By default it spawns a local clamscan process. Pass host
or port in options to scan via a clamd TCP socket instead
(e.g. a Docker container).
Signature
pompelmi.scan(
filePath: string,
options?: {
host?: string; // default: '127.0.0.1'
port?: number; // default: 3310
timeout?: number; // default: 15000 ms (TCP path only)
}
): Promise<"Clean" | "Malicious" | "ScanError">
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
filePath |
string |
Yes | Absolute or relative path to the file to scan. The file must exist on the local filesystem before the call is made. |
options |
object |
No |
Optional configuration. Omit entirely to use the local clamscan CLI.
See options object below.
|
Return value
A Promise that resolves to one of the following regardless of which backend is used:
| Value | CLI exit code | clamd response | Meaning |
|---|---|---|---|
"Clean" |
0 | stream: OK |
No known threats found in the file. |
"Malicious" |
1 | stream: <name> FOUND |
A virus or malware signature was matched. |
"ScanError" |
2 | anything else | The scan itself failed. The file's safety is unknown — treat as untrusted. |
Rejection reasons
The Promise rejects (throws in async/await) in these cases:
| Message pattern | Backend | Cause |
|---|---|---|
filePath must be a string |
both | The argument passed to scan() is not a string. |
File not found: <path> |
both | fs.existsSync(filePath) returned false before anything was spawned or connected. |
ENOENT |
CLI | clamscan binary not found in PATH. |
Unexpected exit code: <n> |
CLI | ClamAV exited with a code not in the SCAN_RESULTS map. |
Process killed by signal: <SIGNAL> |
CLI | The clamscan process was terminated by a signal (e.g. SIGKILL). |
ECONNREFUSED |
TCP | Nothing is listening on host:port. Check that clamd is running. |
clamd connection timed out after Nms |
TCP | The socket was idle for longer than options.timeout. |
OS I/O error (e.g. EACCES) |
TCP | The local file could not be read while streaming it to clamd. |
Example
const pompelmi = require('pompelmi');
// Local clamscan — no options needed
const result = await pompelmi.scan('/var/uploads/report.pdf');
switch (result) {
case 'Clean': /* proceed */ break;
case 'Malicious': /* reject file */ break;
case 'ScanError': /* treat as untrusted, reject */ break;
}
Notes
-
The CLI path spawns a new
clamscanprocess for every call. There is no process pooling or caching. -
The TCP path opens a new socket per call and streams the file using ClamAV's
INSTREAMprotocol. The socket is destroyed immediately after the result is received. - The file is never modified or copied. Both backends read it in-place.
options object
All properties are optional. Omitting options entirely (or passing
an empty object) selects the local CLI path.
Setting host or port selects the TCP path.
| Property | Type | Default | Description |
|---|---|---|---|
host |
string |
'127.0.0.1' |
Hostname or IP address of the clamd instance. |
port |
number |
3310 |
TCP port clamd is listening on. |
timeout |
number |
15000 |
Milliseconds of socket inactivity before the call rejects with a timeout error. Applies to the TCP path only; has no effect on the CLI path. |
Docker / TCP scanning
If ClamAV runs in a Docker container (or anywhere on the network), pass
host and/or port. Everything else — the result
strings, the rejection behaviour, the file path argument — stays identical.
const pompelmi = require('pompelmi');
// Docker on localhost
const result = await pompelmi.scan('/path/to/upload.zip', {
host: '127.0.0.1',
port: 3310,
});
// Remote clamd with a tight timeout
const result2 = await pompelmi.scan('/path/to/upload.zip', {
host: 'clamd-svc.internal',
port: 3310,
timeout: 5_000,
});
docker-compose.yml and first-boot notes.
ClamAVInstaller()
Internal utility function that installs ClamAV using the platform's package manager.
Not exported from the main index.js.
Import
const { ClamAVInstaller } = require('pompelmi/src/ClamAVInstaller');
Signature
ClamAVInstaller(): Promise<string>
Behaviour
Reads process.platform and spawns the corresponding install command:
Platform (process.platform) |
Command executed |
|---|---|
darwin |
brew install clamav |
linux |
sudo apt-get install -y clamav clamav-daemon |
win32 |
choco install clamav -y |
| other | Logs "Current platform is not supported." and returns. |
Output from the package manager is piped to the terminal (stdio: 'inherit').
The function returns a Promise<string> that resolves with a status
message on success or skip, and rejects with an Error if the install
process exits with a non-zero code or if spawning the package manager fails.
sudo apt-get, which may prompt for a password.
This function is intended for use in setup scripts, not in production application code.
updateClamAVDatabase()
Internal utility that runs freshclam to download or update ClamAV
virus definitions. Not exported from the main index.js.
Import
const { updateClamAVDatabase } = require('pompelmi/src/ClamAVDatabaseUpdater');
Signature
updateClamAVDatabase(): Promise<string>
Behaviour
| Platform | Command executed |
|---|---|
darwin |
freshclam |
linux |
sudo freshclam |
win32 |
freshclam |
| other | Logs "Current platform is not supported." and returns. |
Logs "Downloading virus definitions..." before starting.
Returns a Promise<string> that resolves with
"Database updated successfully!" on exit code 0, and rejects
with an Error on any non-zero exit code or spawn failure.
config
The frozen configuration object in src/config.js. It is the single
source of truth for all subprocess commands and result strings.
Shape
module.exports = Object.freeze({
INSTALLER_COMMANDS: Object.freeze({
win32: ['choco', ['install', 'clamav', '-y']],
darwin: ['brew', ['install', 'clamav']],
linux: ['sudo', ['apt-get', 'install', '-y', 'clamav', 'clamav-daemon']],
}),
UPDATER_COMMANDS: Object.freeze({
win32: ['freshclam', []],
darwin: ['freshclam', []],
linux: ['sudo', ['freshclam']],
}),
DB_PATHS: Object.freeze({
darwin: '/usr/local/share/clamav/main.cvd',
linux: '/var/lib/clamav/main.cvd',
win32: 'C:\\ProgramData\\ClamAV\\main.cvd',
}),
SCAN_RESULTS: Object.freeze({
0: 'Clean',
1: 'Malicious',
2: 'ScanError',
}),
});
DB_PATHS
Maps process.platform to the path where main.cvd is expected.
Used by updateClamAVDatabase() to skip freshclam if the
database is already present.
SCAN_RESULTS
Maps ClamAV exit codes to result strings. The map is frozen to prevent accidental
mutation. If ClamAV returns an exit code that is not a key in this object,
scan() rejects with "Unexpected exit code: N".
| Key (exit code) | Value |
|---|---|
| 0 | "Clean" |
| 1 | "Malicious" |
| 2 | "ScanError" |
constants
src/constants.js exports PLATFORM, which is
simply process.platform.
// src/constants.js
module.exports = {
PLATFORM: process.platform,
};
Exporting the platform constant separately makes it replaceable in tests
without monkey-patching process.
Known platform values
process.platform |
OS | Supported |
|---|---|---|
darwin |
macOS | Yes |
linux |
Linux | Yes |
win32 |
Windows (32 and 64-bit) | Yes |
freebsd, openbsd, etc. |
Other Unix | No (installer/updater only; scan works if clamscan is in PATH) |
Error types
All errors thrown by pompelmi are plain Error instances.
There are no custom error classes. You can distinguish them by
inspecting err.message.
| Message | Source | Recovery |
|---|---|---|
filePath must be a string |
ClamAVScanner.js input guard |
Pass a string path to scan(). |
File not found: <path> |
ClamAVScanner.js pre-flight check |
Verify the file path before calling scan(). |
ENOENT |
OS / cross-spawn | Install ClamAV and ensure clamscan is in PATH. |
Unexpected exit code: <n> |
ClamAVScanner.js close handler |
Inspect the exit code. ClamAV exit codes above 2 indicate internal
ClamAV errors. Check ClamAV logs (/var/log/clamav/).
|
Process killed by signal: <SIGNAL> |
ClamAVScanner.js close handler |
The clamscan process was killed externally (e.g. OOM killer).
Retry the scan or investigate system resources.
|
TypeScript
pompelmi does not ship TypeScript type declarations. If you are using TypeScript, add a local declaration file to your project:
pompelmi.d.tsdeclare module 'pompelmi' {
type ScanResult = 'Clean' | 'Malicious' | 'ScanError';
interface ScanOptions {
host?: string;
port?: number;
timeout?: number;
}
const pompelmi: {
scan(filePath: string, options?: ScanOptions): Promise<ScanResult>;
};
export = pompelmi;
}
Place this file anywhere TypeScript can find it (e.g. src/types/pompelmi.d.ts)
and ensure your tsconfig.json includes it.
@types/pompelmi declarations,
open an issue on GitHub.