scarf-js

Zero-dependency npm postinstall analytics that tells maintainers who's installing their packages.

Tool
npm
v1.4.0
178stars
Apache License 2.0

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
50/100Fair
Development Activity56
Maintenance8
Community56
Maturity60
Momentum20

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
54/100Fair
Architecture58
Code Quality62
Innovation60
Learning Curve35

@scarf/scarf is a lightweight postinstall analytics library that open-source npm package maintainers add as a dependency to learn who installs their packages, which companies rely on them, and how usage trends over time. Once installed, it hooks into the npm install lifecycle, traverses the local dependency tree to find its own position beneath the consuming package, and sends a best-effort, non-blocking report to the Scarf analytics service, all without collecting personally identifying information.

The library is intentionally minimal: zero runtime dependencies, transparent opt-in/opt-out behavior communicated directly in the terminal, and automatic disabling when installs are driven by Yarn (which it cannot instrument) or when a SCARF_ANALYTICS=false / DO_NOT_TRACK=1 environment variable is set. Maintainers configure defaults, opt-in or opt-out, per package via a scarfSettings block in their own package.json, giving them control over how aggressively they collect adoption data while staying compliant with user privacy expectations.

What You Get

  • Automatic postinstall reporting that fires the moment your package is installed as a dependency, with no manual instrumentation required
  • Dependency-tree traversal via npm ls that locates exactly which parent package pulled in Scarf, so reports are attributed correctly even in deep trees
  • Configurable opt-in/opt-out defaults per package through a scarfSettings block in package.json, plus environment-variable overrides (SCARF_ANALYTICS, DO_NOT_TRACK)
  • Privacy-conscious payloads that hash package names and versions and never transmit file paths or raw IP addresses in stored form
  • Built-in rate limiting so repeated installs across a dependency tree don’t spam the same opt-in/opt-out message to users

Common Use Cases

  • Open-source maintainers instrumenting a library to measure real-world adoption beyond GitHub stars and npm download counts
  • Commercial open-source companies identifying which enterprises are using their free tier so sales teams can prioritize outreach
  • Package authors tracking version-adoption curves after a major release to know when it’s safe to drop support for older versions
  • Maintainers who want lightweight install telemetry without shipping a full analytics SDK into their package

Under The Hood

Architecture report.js is a single-file, functional CommonJS module exporting a set of near-pure functions plus a main entry that only runs when invoked directly (require.main === module). The flow starts in reportPostInstall(), calls getDependencyInfo() which shells out to npm ls @scarf/scarf --json --long, and hands the resulting JSON tree to processDependencyTreeOutput(), which recursively walks it (findScarfInFullDependencyTree/findScarfInSubDepTree) to find every path down to the Scarf package, then picks the chain matching its own directory (dirName()) before applying opt-in/opt-out and Yarn-detection guards and POSTing a redacted, hashed payload over HTTPS. Small seams like tmpFileName(), dirName(), and npmExecPath() exist purely so tests can override them via module.exports. The whole reporting pipeline is fragile to one thing: if npm ever changes the shape of npm ls --json --long output, the tree-walking functions break silently.

Tech Stack Plain Node.js CommonJS with no build step or transpilation, tested in CI against Node 10.x through 20.x on Linux, macOS, and Windows. It deliberately ships zero runtime dependencies, using only Node core modules: path, os, child_process.exec, https/http (swappable via SCARF_LOCAL_PORT for local development), fs/fs.promises, util, and a lazily-required crypto guarded by try/catch. Dev dependencies are limited to jest for testing and standard for zero-config linting, run via npx standard in CI rather than a custom ESLint config. A nix/ directory in the repo suggests Nix is used for reproducible local dev environments.

Code Quality test/report.test.js and test/skip-traversal.test.js use Jest against realistic fixture files (captured npm ls output) rather than hitting the real filesystem or network, and internal functions are swapped out via module.exports overrides for deterministic tests. Error handling favors descriptive Promise rejections (e.g. “No parent found, nothing to report”) alongside deliberate top-level swallowing in reportPostInstall().catch(...), since analytics must never break npm install. There is no TypeScript or static typing, and the interactive stdin opt-in prompt path in reportPostInstall appears to have no direct test coverage, though the cross-platform, multi-Node-version CI matrix is unusually thorough for a package this size.

What Makes It Unique npm’s dependency graph doesn’t expose “who required me” at install time, so scarf-js shells out to npm ls and walks the resulting tree to reconstruct that provenance itself, a clever workaround for a real ecosystem gap. It pairs that with SHA-256 hashing of package names/versions and an explicit, rate-limited, console-based consent flow (including an offer to persist the user’s opt-in/opt-out choice back into package.json), which is a notably more transparent design than the “phone home silently on install” pattern that gave npm postinstall scripts a bad reputation.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search