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 clamscan process 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 INSTREAM protocol. 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,
});
See Docker for a ready-to-use 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.

On Linux this spawns 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.ts
declare 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.

If you would like official @types/pompelmi declarations, open an issue on GitHub.