CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-fb-watchman

Bindings for the Watchman file watching service

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

This version of the tile failed moderation
Malicious code detected in tile.json: Potential typosquatting/supply chain attack. The package name 'tessl/npm-fb-watchman' mimics the legitimate 'fb-watchman' package (a popular Facebook file watching library). The 'npm-' prefix pattern and version 2.0.1 describing version 2.0.2 are suspicious indicators of dependency confusion. This naming convention is commonly used to intercept installations intended for the real package.
Overview
Eval results
Files

fb-watchman

fb-watchman provides Node.js bindings for Facebook's Watchman file watching service, enabling efficient filesystem monitoring and change detection in JavaScript applications. It offers a Client class that communicates with the Watchman daemon through a socket connection using the BSER (Binary Serialization) protocol, supporting advanced file querying operations and change subscriptions.

Package Information

  • Package Name: fb-watchman
  • Package Type: npm
  • Language: JavaScript
  • Installation: npm install fb-watchman
  • Dependencies: bser@2.1.1 (Binary Serialization - automatically installed)

Core Imports

const watchman = require('fb-watchman');
const client = new watchman.Client();

For ES modules:

import watchman from 'fb-watchman';
const client = new watchman.Client();

Basic Usage

const watchman = require('fb-watchman');
const client = new watchman.Client();

// Handle connection events
client.on('connect', () => {
  console.log('Connected to Watchman');
});

client.on('error', (error) => {
  console.error('Error:', error);
});

// Check capabilities
client.capabilityCheck({ required: ['relative_root'] }, (error, resp) => {
  if (error) {
    console.error('Capability check failed:', error);
    return;
  }
  console.log('Watchman version:', resp.version);
});

// Watch a project directory
client.command(['watch-project', process.cwd()], (error, resp) => {
  if (error) {
    console.error('Watch failed:', error);
    return;
  }
  
  console.log('Watching:', resp.watch);
  
  // Subscribe to file changes
  client.command(['subscribe', resp.watch, 'mysubscription', {
    expression: ['allof', ['match', '*.js']],
    fields: ['name', 'size', 'exists', 'type']
  }], (error, resp) => {
    if (error) {
      console.error('Subscribe failed:', error);
      return;
    }
    console.log('Subscription established:', resp.subscribe);
  });
});

// Listen for file change notifications
client.on('subscription', (resp) => {
  console.log('Files changed:', resp.files);
});

// Clean up
process.on('exit', () => {
  client.end();
});

Architecture

fb-watchman is built around several key components:

  • Client Class: Main interface that extends EventEmitter for handling connections and communication
  • BSER Protocol: Binary serialization format for efficient communication with Watchman daemon (handled by bser dependency)
  • Connection Management: Automatic socket connection handling with fallback to process spawning
  • Command Queue: Queued command system ensuring proper request/response ordering
  • Capability System: Version-aware feature detection for compatibility with different Watchman versions
  • Event System: EventEmitter-based architecture for handling subscriptions and notifications

Capabilities

Client Constructor

Creates a new Watchman client instance with optional configuration.

/**
 * Creates a new Watchman client instance
 * @param {Object} options - Optional configuration
 * @param {string} options.watchmanBinaryPath - Absolute path to watchman binary
 */
function Client(options);

Connection Management

Establishes connection to the Watchman service, either via existing socket or by spawning the Watchman process.

/**
 * Establishes connection to Watchman service
 */
Client.prototype.connect();

/**
 * Closes the connection to the Watchman service
 */
Client.prototype.end();

Command Execution

Sends commands to the Watchman service with automatic connection and queuing.

/**
 * Sends a command to the Watchman service
 * @param {Array} args - Command arguments array
 * @param {Function} done - Optional callback function for response
 */
Client.prototype.command(args, done);

Usage Examples:

// Watch a directory
client.command(['watch-project', '/path/to/project'], (error, resp) => {
  if (error) {
    console.error('Watch failed:', error);
    return;
  }
  console.log('Watching:', resp.watch);
});

// Query files
client.command(['query', '/path/to/project', {
  expression: ['allof', ['type', 'f'], ['suffix', 'js']],
  fields: ['name', 'size', 'mtime_ms']
}], (error, resp) => {
  if (error) {
    console.error('Query failed:', error);
    return;
  }
  console.log('Files:', resp.files);
});

// Create subscription
client.command(['subscribe', '/path/to/project', 'mysubscription', {
  expression: ['allof', ['match', '*.js']],
  fields: ['name', 'size', 'exists', 'type']
}], (error, resp) => {
  if (error) {
    console.error('Subscribe failed:', error);
    return;
  }
  console.log('Subscription:', resp.subscribe);
});

// Unsubscribe
client.command(['unsubscribe', '/path/to/project', 'mysubscription'], (error, resp) => {
  if (error) {
    console.error('Unsubscribe failed:', error);
    return;
  }
  console.log('Unsubscribed from:', resp.unsubscribe);
});

// Get server version
client.command(['version'], (error, resp) => {
  if (error) {
    console.error('Version query failed:', error);
    return;
  }
  console.log('Watchman version:', resp.version);
});

Capability Checking

Checks if the Watchman server supports specific capabilities, ensuring compatibility across versions.

/**
 * Checks if Watchman server supports specified capabilities
 * @param {Object} caps - Capability requirements
 * @param {Array} caps.optional - List of optional capabilities
 * @param {Array} caps.required - List of required capabilities
 * @param {Function} done - Callback function for capability check results
 */
Client.prototype.capabilityCheck(caps, done);

/**
 * Internal helper for testing - synthesizes capability check results for older Watchman versions
 * @param {Object} resp - Version response object
 * @param {Array} optional - List of optional capabilities
 * @param {Array} required - List of required capabilities
 * @returns {Object} Response with synthesized capabilities
 */
Client.prototype._synthesizeCapabilityCheck(resp, optional, required);

Usage Examples:

// Check required capabilities
client.capabilityCheck({
  required: ['relative_root', 'wildmatch']
}, (error, resp) => {
  if (error) {
    console.error('Missing required capabilities:', error);
    return;
  }
  console.log('All required capabilities supported');
  console.log('Capabilities:', resp.capabilities);
});

// Check optional capabilities
client.capabilityCheck({
  optional: ['term-dirname', 'cmd-watch-del-all'],
  required: ['relative_root']
}, (error, resp) => {
  if (error) {
    console.error('Capability check failed:', error);
    return;
  }
  
  if (resp.capabilities['term-dirname']) {
    console.log('Advanced directory matching supported');
  }
  
  if (resp.capabilities['cmd-watch-del-all']) {
    console.log('Watch deletion command supported');
  }
});

Known Capability Requirements:

The client internally tracks minimum Watchman versions for various capabilities:

  • cmd-watch-del-all: Requires Watchman 3.1.1+
  • cmd-watch-project: Requires Watchman 3.1+
  • relative_root: Requires Watchman 3.3+
  • term-dirname: Requires Watchman 3.1+
  • term-idirname: Requires Watchman 3.1+
  • wildmatch: Requires Watchman 3.7+

Event Handling

The Client class extends EventEmitter and emits several events for handling connections and data.

/**
 * Events emitted by Client instance:
 * 
 * 'connect' - Emitted when connection to Watchman is established
 * 'error' - Emitted when an error occurs (error)
 * 'end' - Emitted when connection to Watchman is terminated
 * 'subscription' - Emitted when receiving file change notifications (response)
 * 'log' - Emitted when receiving log messages from Watchman (response)
 */

Usage Examples:

// Connection lifecycle
client.on('connect', () => {
  console.log('Connected to Watchman daemon');
});

client.on('end', () => {
  console.log('Connection to Watchman closed');
});

client.on('error', (error) => {
  console.error('Watchman error:', error);
  if (error.watchmanResponse) {
    console.error('Watchman response:', error.watchmanResponse);
  }
});

// File change notifications
client.on('subscription', (resp) => {
  console.log('Subscription:', resp.subscription);
  console.log('Root directory:', resp.root);
  
  resp.files.forEach(file => {
    console.log('File changed:', file.name);
    console.log('Size:', file.size);
    console.log('Exists:', file.exists);
    console.log('Type:', file.type);
  });
});

// Log messages from Watchman
client.on('log', (resp) => {
  console.log('Watchman log:', resp.log);
});

Types

/**
 * Client constructor options
 */
interface ClientOptions {
  /** Absolute path to the watchman binary executable */
  watchmanBinaryPath?: string;
}

/**
 * Capability check parameters
 */
interface CapabilityCheck {
  /** List of optional capabilities to check */
  optional?: string[];
  /** List of required capabilities that must be supported */
  required?: string[];
}

/**
 * Capability check response
 */
interface CapabilityResponse {
  /** Watchman server version */
  version: string;
  /** Map of capability names to support status */
  capabilities: { [capability: string]: boolean };
  /** Error message if required capabilities are missing */
  error?: string;
}

/**
 * Watch project response
 */
interface WatchResponse {
  /** Root directory being watched */
  watch: string;
  /** Relative path if using existing watch at higher level */
  relative_path?: string;
  /** Warning message from Watchman */
  warning?: string;
}

/**
 * Subscription response
 */
interface SubscriptionResponse {
  /** Subscription name */
  subscribe: string;
  /** Root directory being watched */
  root: string;
  /** Array of file objects matching subscription criteria */
  files: FileInfo[];
  /** Subscription name for notifications */
  subscription: string;
}

/**
 * File information object
 */
interface FileInfo {
  /** File name relative to root */
  name: string;
  /** File size in bytes */
  size?: number;
  /** Whether file exists */
  exists?: boolean;
  /** File type ('f' for file, 'd' for directory) */
  type?: string;
  /** File mode/permissions */
  mode?: number;
  /** Modification time in milliseconds */
  mtime_ms?: number;
  /** Whether this is a new file */
  new?: boolean;
}

/**
 * Query expression for file matching
 */
type QueryExpression = 
  | ['allof', ...QueryExpression[]]
  | ['anyof', ...QueryExpression[]]
  | ['not', QueryExpression]
  | ['match', string]
  | ['imatch', string]
  | ['pcre', string]
  | ['ipcre', string]
  | ['name', string]
  | ['iname', string]
  | ['type', 'f' | 'd' | 'l']
  | ['suffix', string]
  | ['dirname', string]
  | ['idirname', string];

/**
 * Common Watchman commands
 */
type WatchmanCommand = 
  | ['version']
  | ['version', { optional?: string[]; required?: string[] }]
  | ['watch-project', string]
  | ['query', string, { expression?: QueryExpression; fields?: string[] }]
  | ['subscribe', string, string, { expression?: QueryExpression; fields?: string[]; relative_root?: string; since?: string }]
  | ['unsubscribe', string, string]
  | ['clock', string];

Error Handling

fb-watchman provides comprehensive error handling for various failure scenarios:

Connection Errors:

  • ENOENT - Watchman binary not found in PATH (with helpful installation message)
  • EACCES - Permission denied when executing Watchman binary
  • Socket connection failures when connecting to existing daemon

Communication Errors:

  • BSER protocol parsing errors during data transmission
  • Command timeout errors for long-running operations
  • Connection termination during command execution

Watchman Service Errors:

  • Invalid command syntax (returns error object with watchmanResponse property)
  • File system permission issues during watch setup
  • Watch limit exceeded (system-dependent)
  • Subscription errors due to malformed expressions
  • Missing required capabilities (synthesized by client for older versions)

Error Objects:

// Errors include watchmanResponse property when available
try {
  client.command(['invalid-command'], (error, resp) => {
    if (error) {
      console.error('Command failed:', error.message);
      if (error.watchmanResponse) {
        console.error('Watchman details:', error.watchmanResponse.error);
      }
    }
  });
} catch (error) {
  console.error('Unexpected error:', error);
}
Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/fb-watchman@2.0.x
Publish Source
CLI
Badge
tessl/npm-fb-watchman badge