Analytics package that collects installation statistics for npm packages to help open-source maintainers understand usage patterns.
npx @tessl/cli install tessl/npm-scarf--scarf@1.4.0Scarf is an analytics package that automatically collects installation statistics for npm packages, helping open-source maintainers understand how their packages are used and by which organizations. It operates transparently during package installation via postinstall hooks and provides configurable opt-in/opt-out mechanisms.
npm install @scarf/scarfconst scarf = require('@scarf/scarf');For ES modules:
import * as scarf from '@scarf/scarf';Scarf is primarily designed to work automatically through npm postinstall hooks:
{
"name": "your-package",
"scripts": {
"postinstall": "node ./node_modules/@scarf/scarf"
},
"dependencies": {
"@scarf/scarf": "^1.4.0"
}
}The package automatically reports analytics when installed as a dependency. No additional code is required for basic functionality.
Scarf operates through several key components:
Control Scarf behavior through environment variables:
SCARF_ANALYTICS=false - Disables analytics collectionSCARF_NO_ANALYTICS=true - Legacy disable flagDO_NOT_TRACK=1 - Respects Console Do Not Track standardSCARF_VERBOSE=true - Enables verbose logging outputSCARF_API_TOKEN - API authentication token for custom endpointsConfigure Scarf behavior in your package.json:
{
"scarfSettings": {
"enabled": true,
"defaultOptIn": true,
"allowTopLevel": false,
"skipTraversal": false
}
}Main function that collects and sends installation analytics to Scarf servers.
/**
* Collect and report installation analytics
* @returns {Promise<void>} Promise that resolves when reporting completes
* @throws {Error} When analytics are disabled or reporting fails
*/
function reportPostInstall(): Promise<void>;Analyzes npm dependency tree to understand package relationships and configuration.
/**
* Analyze npm dependency tree to find package relationships
* @param {string} [packageJSONOverride] - Optional path to package.json for testing
* @returns {Promise<DependencyInfo>} Dependency information object
*/
function getDependencyInfo(packageJSONOverride?: string): Promise<DependencyInfo>;
/**
* Find all paths to @scarf/scarf in dependency tree
* @param {Object} tree - npm ls dependency tree object
* @returns {Array<Array<Object>>} Array of dependency chain arrays
*/
function findScarfInFullDependencyTree(tree: Object): Array<Array<Object>>;Functions that handle sensitive data redaction and hashing before transmission.
/**
* Remove sensitive information from dependency data
* @param {DependencyInfo} dependencyInfo - Dependency information object
* @returns {DependencyInfo} Sanitized dependency info object
*/
function redactSensitivePackageInfo(dependencyInfo: DependencyInfo): DependencyInfo;
/**
* Create SHA256 hash of input string with fallback value
* @param {string} toHash - String to hash
* @param {string} defaultReturn - Fallback value if hashing fails
* @returns {string} Hash or default value
*/
function hashWithDefault(toHash: string, defaultReturn: string): string;Retrieves Git commit information for enhanced analytics when available.
/**
* Retrieve Git commit SHA from current repository
* @returns {Promise<string|undefined>} Git SHA or undefined if unavailable
*/
function getGitShaFromRootPath(): Promise<string | undefined>;Prevents spam messaging to users during multiple package installations.
/**
* Log messages to users with rate limiting
* @param {string} rateLimitKey - Unique key for rate limiting
* @param {string} toLog - Message to log
*/
function rateLimitedUserLog(rateLimitKey: string, toLog: string): void;
/**
* Check if rate limit has been exceeded for a given key
* @param {string} rateLimitKey - Rate limit key to check
* @param {Object} history - Rate limit history object
* @returns {boolean} True if rate limit has been hit
*/
function hasHitRateLimit(rateLimitKey: string, history: Object): boolean;
/**
* Retrieve rate limiting history from temporary file
* @returns {Object} Rate limit history data
*/
function getRateLimitedLogHistory(): Object;Utility functions for environment detection and path management.
/**
* Generate temporary file name for rate limiting history
* @returns {string} Temporary file path
*/
function tmpFileName(): string;
/**
* Get current directory name (testable __dirname wrapper)
* @returns {string} Current directory path
*/
function dirName(): string;
/**
* Get npm executable path from environment
* @returns {string} npm executable path
*/
function npmExecPath(): string;Callback factory functions for processing command output (primarily for testing).
/**
* Create callback function for processing npm ls command output
* @param {Function} resolve - Promise resolve callback
* @param {Function} reject - Promise reject callback
* @returns {Function} Callback function for processing npm ls output
*/
function processDependencyTreeOutput(resolve: Function, reject: Function): Function;
/**
* Create callback function for processing git rev-parse command output
* @param {Function} resolve - Promise resolve callback
* @param {Function} reject - Promise reject callback
* @returns {Function} Callback function for processing git output
*/
function processGitRevParseOutput(resolve: Function, reject: Function): Function;Complete dependency relationship information for analytics reporting.
interface DependencyInfo {
scarf: {
name: string;
version: string;
path?: string;
};
parent: {
name: string;
version: string;
scarfSettings?: ScarfSettings;
path?: string;
gitSha?: string;
};
grandparent?: {
name: string;
version: string;
path?: string;
nameHash?: string;
versionHash?: string;
};
rootPackage: {
name: string;
version: string;
packageJsonPath: string;
path?: string;
nameHash?: string;
versionHash?: string;
};
anyInChainDisabled: boolean;
skippedTraversal?: boolean;
}Configuration options for Scarf behavior in package.json.
interface ScarfSettings {
/** Enable or disable Scarf for this package */
enabled?: boolean;
/** Whether users are opted into analytics by default */
defaultOptIn?: boolean;
/** Allow analytics when package is installed at root level */
allowTopLevel?: boolean;
/** Skip dependency tree traversal for large projects */
skipTraversal?: boolean;
}Data structure sent to Scarf analytics endpoint.
interface AnalyticsPayload {
libraryType: 'npm';
rawPlatform: string;
rawArch: string;
nodeVersion: string;
dependencyInfo: DependencyInfo;
}While primarily automatic, functions can be called programmatically for testing or advanced use cases:
const scarf = require('@scarf/scarf');
// Manually trigger analytics reporting
try {
await scarf.reportPostInstall();
console.log('Analytics reported successfully');
} catch (error) {
console.log('Analytics disabled or failed:', error.message);
}
// Analyze dependency information
const depInfo = await scarf.getDependencyInfo();
console.log('Package relationships:', depInfo);
// Check rate limiting status
const history = scarf.getRateLimitedLogHistory();
const hitLimit = scarf.hasHitRateLimit('test-key', history);
console.log('Rate limit hit:', hitLimit);Opt-out by default with manual opt-in:
{
"scarfSettings": {
"defaultOptIn": false
}
}Enable for top-level installations:
{
"scarfSettings": {
"allowTopLevel": true
}
}Skip dependency traversal for large projects:
{
"scarfSettings": {
"skipTraversal": true
}
}Complete configuration:
{
"scarfSettings": {
"enabled": true,
"defaultOptIn": true,
"allowTopLevel": true,
"skipTraversal": false
}
}