npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@4lpha/easyadb

v0.0.7

Published

Adb Kit

Readme

EasyAdb

A TypeScript library that provides a clean API for interacting with Android devices using the Android Debug Bridge (ADB).

Features

  • Easy-to-use API for common ADB operations
  • TypeScript support with comprehensive type definitions
  • Connection management to Android devices
  • Device information retrieval (memory, storage, properties)
  • File transfer capabilities
  • App management functions
  • Command execution on connected devices
  • Timeouts and error handling
  • Caching mechanism for frequently accessed device properties
  • Automatic ADB download and setup

Installation

npm install @4lpha/easyadb

Prerequisites

  • ADB (Android Debug Bridge) must be installed on your system and available in your PATH
  • For connection to physical devices, USB debugging must be enabled on the device

ADB Management

EasyAdb provides tools to check for and download the ADB executable automatically.

Check if ADB Exists

You can check if ADB is installed and accessible:

import { AdbClient } from "@4lpha/easyadb";

const adb = new AdbClient();
const exists = await adb.checkAdbExists();

if (exists) {
  console.log("ADB is available");
} else {
  console.log("ADB not found");
}

Download ADB

If ADB is not installed, you can download the latest platform-tools automatically:

const adb = new AdbClient();

// Download ADB to default location (current_dir/.adb)
const adbPath = await adb.downloadAdb({
  verbose: true, // Enable logging
  onProgress: (downloaded, total) => {
    const percentage = Math.round((downloaded / total) * 100);
    console.log(`Downloaded: ${percentage}%`);
  }
});

console.log(`ADB downloaded to: ${adbPath}`);

// The client automatically updates its ADB path to the downloaded version
await adb.startServer();

You can also use the standalone functions:

import { downloadAdb, checkAdbExists } from "@4lpha/easyadb";

const exists = await checkAdbExists();
if (!exists) {
  const path = await downloadAdb();
}

Basic Usage

Connecting to a Device

import { AdbClient } from "@4lpha/easyadb";

// Create an ADB client
const adb = new AdbClient();

// Start the ADB server if not already running
await adb.startServer();

// Get a list of connected devices
const devices = await adb.getDevices();
console.log(`Found ${devices.length} devices`);

// Connect to a specific device using its ID
const deviceClient = await adb.getDevice("DEVICE_SERIAL_ID");

// Or connect to a device over network
await adb.connect("192.168.1.100:5555");

Device Information

// Get basic device information
const id = deviceClient.getDeviceId();
const name = await deviceClient.getName();
const model = await deviceClient.getModel();
const manufacturer = await deviceClient.getManufacturer();
const androidVersion = await deviceClient.getAndroidVersion();
const sdkVersion = await deviceClient.getSDKVersion();
const brand = await deviceClient.getBrand();
const screenDensity = await deviceClient.getScreenDensity();
const language = await deviceClient.getSystemLanguage();
const locale = await deviceClient.getLocale();

// Get device memory information
const memory = await deviceClient.getDeviceMemory();
console.log(`Total Memory: ${memory.totalMemory} kB`);
console.log(`Free Memory: ${memory.memFree} kB`);
console.log(`Used Memory: ${memory.memUsed} kB`);

// Get storage information
const storage = await deviceClient.getStorage();
console.log(`Available storage: ${storage[0].Available} kB`);

// Get all device properties as a string
const allProps = await deviceClient.getAllProps();
console.log(allProps);

// Get filtered device properties as an object
const specificProps = await deviceClient.getAllProps([
  "ro.product.model",
  "ro.build.version.release",
]);
console.log(specificProps); // { 'ro.product.model': 'Pixel', 'ro.build.version.release': '12' }

// List files in a directory
const files = await deviceClient.ls("/sdcard");
console.log(files);

Executing Shell Commands

// Execute a shell command on the device
const result = await deviceClient.shell("ls /sdcard");
console.log(result);

// You can also execute ADB commands directly
const output = await adb.exec("shell ls /sdcard");

// Or use the ls convenience method
const files = await deviceClient.ls("/sdcard");
console.log(files);

File Transfer

// Push a file to the device
await deviceClient.push(
  "/path/to/local/file",
  "/sdcard/destination",
  (transferred, total) => {
    console.log(
      `Transferred: ${transferred} bytes of ${total || "unknown"} bytes`
    );
  }
);

// Using streams
const fs = require("fs");
const readStream = fs.createReadStream("/path/to/local/file");

await deviceClient.push(
  readStream,
  "/sdcard/destination",
  (transferred, total) => {
    console.log(
      `Transferred: ${transferred} bytes of ${total || "unknown"} bytes`
    );
  }
);

// Pull a file from the device
await deviceClient.pull(
  "/sdcard/file.txt",
  "/path/to/local/destination",
  (transferred, total) => {
    console.log(
      `Transferred: ${transferred} bytes of ${total || "unknown"} bytes`
    );
  }
);

App Management

// Install an APK
await deviceClient.install("/sdcard/app.apk");

// Install with options
await deviceClient.install("/sdcard/app.apk", {
  replace: true, // Replace existing app
  allowDowngrade: true, // Allow version downgrade
  grantPermissions: true, // Automatically grant all permissions
});

// Uninstall an app
await deviceClient.uninstall("com.example.app");

// Clear app cache
await deviceClient.clearCache("com.example.app");

// Set default home app
await deviceClient.setHomeApp(
  "com.example.launcher/com.example.launcher.MainActivity"
);

// Disable system UI
await deviceClient.disableSystemUI();

// Enable system UI
await deviceClient.enableSystemUI();

Device Control

// Toggle screen (on/off)
await deviceClient.toggleScreen();

// Get screen state
const screenState = await deviceClient.getScreenState();
console.log(`Screen is ${screenState}`);

// Get screen resolution
const resolution = await deviceClient.getScreenResolution();
console.log(`Screen resolution: ${resolution}`);

// Reboot the device
await deviceClient.reboot();

// Enable/disable launcher
await deviceClient.disableLauncher();
await deviceClient.enableLauncher();

// Device interaction controls
// Tap on screen
await deviceClient.control({
  type: "tap",
  x: 500,
  y: 800,
});

// Swipe on screen
await deviceClient.control({
  type: "swipe",
  x1: 500,
  y1: 1000,
  x2: 500,
  y2: 200,
});

// Input text
await deviceClient.control({
  type: "text",
  text: "Hello world",
});

// Press a key (keyevent)
await deviceClient.control({
  type: "key",
  keycode: 66, // ENTER key
});

// Take a basic screenshot
await deviceClient.getScreenshot("/sdcard/screenshot.png");

// Take a screenshot with timestamp
const timestamp = Date.now();
await deviceClient.getScreenshot(`/sdcard/screenshot_${timestamp}.png`);

// Open URL in device browser
await deviceClient.openUrl("https://www.google.com");

// Open local file
await deviceClient.openUrl("file:///sdcard/document.pdf");

// Open app deep link
await deviceClient.openUrl("myapp://some/path");

// Monitor device logs
const stopLogging = deviceClient.logcat((log) => {
  console.log("Device log:", log);
});

// Filter specific log types
const stopErrorLogging = deviceClient.logcat((log) => {
  if (log.includes("ERROR")) {
    console.error("Error detected:", log);
  }
});

// Stop logging when done
stopLogging();
stopErrorLogging();

Shared Preferences Management

// Set a shared preference for an app
await deviceClient.putSharedConfig(
  "com.example.app",
  "preference_key",
  "value",
  { restart: true }
);

// Get a shared preference value
const value = await deviceClient.getSharedConfig(
  "com.example.app",
  "preference_key"
);

// Get all shared preferences
const allPrefs = await deviceClient.getAllSharedConfig("com.example.app");

// Remove a shared preference
await deviceClient.removeSharedConfig(
  "com.example.app",
  "preference_key",
  "value",
  { restart: true }
);

// Clear a specific shared preference
await deviceClient.clearSharedConfig("com.example.app", "preference_key");

// Clear all shared preferences
await deviceClient.clearAllSharedConfig("com.example.app");

Broadcast Actions

// Send a broadcast
await deviceClient.broadcast("com.example.ACTION", {
  extras: {
    key1: "value1",
    key2: "value2",
  },
});

Advanced Configuration

Custom ADB Path

const adb = new AdbClient({
  path: "/custom/path/to/adb",
  timeout: 15000, // 15 seconds
});

Connection Options

const adb = new AdbClient({
  host: "127.0.0.1",
  port: 5037,
});

Logging and Debugging

// Enable verbose output
const result = await adb.verbose().shell("ls /sdcard");

// Suppress exceptions
const output = await adb.noThrow().exec("non-existent-command");
// output will be null instead of throwing an exception

Connection Testing

import { testAdbConnection } from "@4lpha/easyadb";

// Test if ADB is properly connected
const isConnected = await testAdbConnection("adb", true);
if (isConnected) {
  console.log("ADB connection successful!");
} else {
  console.log("ADB connection failed.");
}

Error Handling

The library provides comprehensive error handling:

try {
  const result = await adb.exec("invalid-command");
} catch (error) {
  console.error("ADB execution failed:", error.message);
}

// Or use noThrow to prevent exceptions
const result = await adb.noThrow().exec("invalid-command");
if (result === null) {
  console.log("Command failed but no exception was thrown");
}

Example Use Cases

Automated Testing Scenario

// Example test scenario
async function testLoginFlow() {
  const adb = new AdbClient();
  await adb.startServer();

  const device = await adb.getDevice("emulator-5554");

  // Launch the app
  await device.shell("am start -n com.example.app/.MainActivity");

  // Tap on username field
  await device.control({
    type: "tap",
    x: 500,
    y: 700,
  });

  // Enter username
  await device.control({
    type: "text",
    text: "[email protected]",
  });

  // Tap on password field
  await device.control({
    type: "tap",
    x: 500,
    y: 800,
  });

  // Enter password
  await device.control({
    type: "text",
    text: "password123",
  });

  // Tap login button
  await device.control({
    type: "tap",
    x: 500,
    y: 900,
  });

  // Check if main page loaded
  const logs = [];
  const stopLogging = device.logcat((log) => {
    logs.push(log);
    if (log.includes("MainActivity: onResume")) {
      console.log("Login successful!");
      stopLogging();
    }
  });

  // Take screenshot of result
  await device.getScreenshot("/sdcard/login_result.png");
  await device.pull("/sdcard/login_result.png", "./login_result.png");
}

Device Monitoring and Reporting

// Monitor and report device status
async function monitorDeviceStatus() {
  const adb = new AdbClient();
  await adb.startServer();

  const devices = await adb.getDevices();

  for (const device of devices) {
    // Get device information
    const model = await device.getModel();
    const androidVersion = await device.getAndroidVersion();
    const memory = await device.getDeviceMemory();
    const storage = await device.getStorage();

    console.log(`Device: ${model} (Android ${androidVersion})`);
    console.log(`Memory Usage: ${memory.memUsed}/${memory.totalMemory} kB`);
    console.log(`Storage: ${storage[0].Available}/${storage[0].kBlock} kB`);

    // Take screenshot
    const timestamp = Date.now();
    await device.getScreenshot(`/sdcard/status_${timestamp}.png`);
    await device.pull(
      `/sdcard/status_${timestamp}.png`,
      `./status_${model}_${timestamp}.png`
    );
  }
}

API Reference

Adb Class

The base class that provides core ADB functionality.

| Method | Description | | ---------------------------------------- | ------------------------------------------------------ | | constructor(options?: IAdbOptions) | Creates a new ADB instance with optional configuration | | verbose() | Enables verbose output for the next command | | noThrow() | Prevents exceptions for the next command | | shell(command: string) | Executes a shell command | | exec(command: string, args?: string[]) | Executes an ADB command with optional arguments | | reboot() | Reboots the device | | uninstall(packagename: string) | Uninstalls an app by package name | | downloadAdb(options?: IAdbDownloadOptions) | Downloads ADB platform-tools | | checkAdbExists(adbPath?: string) | Checks if ADB exists at path or in PATH |

API Reference (continued)

AdbClient Class

Extends the Adb class with additional client functionality.

| Method | Description | | -------------------------------------------------- | -------------------------------------------------- | | startServer() | Starts the ADB server if not already running | | killServer() | Stops the ADB server | | getDevices() | Returns a list of connected devices | | getDevice(id: string) | Returns a DeviceClient for the specified device ID | | connect(address: string) | Connects to a device over network | | disconnect(address?: string) | Disconnects from a device or all devices | | waitForDevice(serial?: string, timeout?: number) | Waits for a device to be connected | | tcpip(port?: number) | Restarts ADB in TCP/IP mode |

DeviceClient Class

Provides device-specific functionality.

| Method | Description | | ---------------------------------------------------------------------------------------------------- | --------------------------------------------------- | | getDeviceId() | Returns the device ID | | getName() | Returns the device name | | getModel() | Returns the device model | | getManufacturer() | Returns the device manufacturer | | getBrand() | Returns the device brand | | getAndroidVersion() | Returns the Android version | | getSDKVersion() | Returns the SDK version | | getScreenDensity() | Returns the screen density | | getSystemLanguage() | Returns the system language | | getLocale() | Returns the device locale | | getDeviceMemory() | Returns memory usage information | | getStorage() | Returns storage information | | getScreenResolution() | Returns screen resolution | | getScreenState() | Returns screen state (on/off) | | getAllProps(filter?: string[]) | Returns device properties | | getProp(prop: string) | Returns a specific device property | | shell(command: string) | Executes a shell command on the device | | ls(path: string) | Lists files in a directory | | push(src: string \| ReadStream, dest: string, callback?: TransferCallback) | Transfers a file to the device | | pull(src: string, dest: string \| WriteStream, callback?: TransferCallback) | Transfers a file from the device | | install(apkPath: string, options?: InstallOptions) | Installs an APK | | uninstall(packageName: string) | Uninstalls an app | | clearCache(packageName: string) | Clears app cache | | toggleScreen() | Toggles screen on/off | | reboot() | Reboots the device | | setHomeApp(app: string) | Sets default home app | | disableSystemUI() | Disables system UI | | enableSystemUI() | Enables system UI | | disableLauncher() | Disables launcher | | enableLauncher() | Enables launcher | | getScreenshot(path: string) | Takes a screenshot | | control(options: ControlOptions) | Performs device interaction (tap, swipe, text, key) | | openUrl(url: string) | Opens a URL in the device browser | | logcat(callback: (log: string) => void) | Monitors device logs | | putSharedConfig(packageName: string, key: string, value: string, options?: SharedConfigOptions) | Sets a shared preference | | getSharedConfig(packageName: string, key: string) | Gets a shared preference value | | getAllSharedConfig(packageName: string) | Gets all shared preferences | | removeSharedConfig(packageName: string, key: string, value: string, options?: SharedConfigOptions) | Removes a shared preference | | clearSharedConfig(packageName: string, key: string) | Clears a specific shared preference | | clearAllSharedConfig(packageName: string) | Clears all shared preferences | | broadcast(action: string, options?: BroadcastOptions) | Sends a broadcast action |

Common KeyEvent Codes

Here are some commonly used KeyEvent codes for the control({ type: "key", keycode: number }) function:

| Key | Code | Description | | ------------------------ | ---- | ---------------- | | KEYCODE_ENTER | 66 | Enter key | | KEYCODE_TAB | 61 | Tab key | | KEYCODE_SPACE | 62 | Space key | | KEYCODE_BACK | 4 | Back button | | KEYCODE_HOME | 3 | Home button | | KEYCODE_MENU | 82 | Menu button | | KEYCODE_POWER | 26 | Power button | | KEYCODE_VOLUME_UP | 24 | Volume up | | KEYCODE_VOLUME_DOWN | 25 | Volume down | | KEYCODE_DPAD_UP | 19 | D-pad up | | KEYCODE_DPAD_DOWN | 20 | D-pad down | | KEYCODE_DPAD_LEFT | 21 | D-pad left | | KEYCODE_DPAD_RIGHT | 22 | D-pad right | | KEYCODE_DPAD_CENTER | 23 | D-pad center | | KEYCODE_CAMERA | 27 | Camera button | | KEYCODE_SEARCH | 84 | Search button | | KEYCODE_MEDIA_PLAY_PAUSE | 85 | Play/pause media | | KEYCODE_MEDIA_STOP | 86 | Stop media | | KEYCODE_MEDIA_NEXT | 87 | Next media | | KEYCODE_MEDIA_PREVIOUS | 88 | Previous media |

For alphabetic keys, use codes 29-54 (a-z). For numeric keys, use codes 7-16 (0-9).

Advanced Examples

App Testing with Screenshots

async function testApp() {
  const adb = new AdbClient();
  const device = await adb.getDevice();

  // Clear app data before testing
  await device.clearCache("com.example.app");

  // Start the app
  await device.shell("am start -n com.example.app/.MainActivity");

  // Wait for app to load
  await new Promise((resolve) => setTimeout(resolve, 2000));

  // Take a screenshot of the initial state
  await device.getScreenshot("/sdcard/test_initial.png");

  // Perform a series of interactions
  await device.control({ type: "tap", x: 300, y: 500 });
  await device.control({ type: "text", text: "Test input" });
  await device.control({ type: "key", keycode: 66 }); // Press Enter

  // Wait for response
  await new Promise((resolve) => setTimeout(resolve, 1000));

  // Take a screenshot of the result
  await device.getScreenshot("/sdcard/test_result.png");

  // Pull screenshots to local machine
  await device.pull("/sdcard/test_initial.png", "./test_initial.png");
  await device.pull("/sdcard/test_result.png", "./test_result.png");
}

Device Performance Monitoring

async function monitorPerformance(packageName, durationSeconds) {
  const adb = new AdbClient();
  const device = await adb.getDevice();

  console.log(
    `Starting performance monitoring for ${packageName} for ${durationSeconds} seconds`
  );

  // Start the app
  await device.shell(`am start -n ${packageName}/.MainActivity`);

  const startTime = Date.now();
  const endTime = startTime + durationSeconds * 1000;

  // Collect metrics at intervals
  const metrics = [];

  while (Date.now() < endTime) {
    // Get memory usage
    const memoryOutput = await device.shell(`dumpsys meminfo ${packageName}`);
    const memMatch = memoryOutput.match(/TOTAL\s+(\d+)/);
    const memoryUsage = memMatch ? parseInt(memMatch[1]) : null;

    // Get CPU usage
    const cpuOutput = await device.shell(
      `dumpsys cpuinfo | grep ${packageName}`
    );
    const cpuMatch = cpuOutput.match(/(\d+(?:\.\d+)?)%/);
    const cpuUsage = cpuMatch ? parseFloat(cpuMatch[1]) : null;

    metrics.push({
      timestamp: new Date(),
      memory: memoryUsage,
      cpu: cpuUsage,
    });

    // Take a screenshot
    const timestamp = Date.now();
    await device.getScreenshot(`/sdcard/perf_${timestamp}.png`);

    // Wait before next sample
    await new Promise((resolve) => setTimeout(resolve, 5000));
  }

  // Generate report
  console.log("Performance Report:");
  console.log("==================");

  metrics.forEach((metric) => {
    console.log(`Time: ${metric.timestamp.toISOString()}`);
    console.log(`Memory: ${metric.memory ? metric.memory + " KB" : "N/A"}`);
    console.log(`CPU: ${metric.cpu ? metric.cpu + "%" : "N/A"}`);
    console.log("------------------");
  });

  // Calculate averages
  const avgMemory =
    metrics.reduce((sum, m) => sum + (m.memory || 0), 0) / metrics.length;
  const avgCpu =
    metrics.reduce((sum, m) => sum + (m.cpu || 0), 0) / metrics.length;

  console.log(`Average Memory Usage: ${avgMemory.toFixed(2)} KB`);
  console.log(`Average CPU Usage: ${avgCpu.toFixed(2)}%`);
}

Troubleshooting

Common Issues

  1. "ADB server not running"

    • Solution: Call adb.startServer() before other operations.
  2. "Device not found"

    • Ensure the device is connected and USB debugging is enabled
    • Try adb.waitForDevice() to wait for a device connection
  3. Permission errors

    • Ensure ADB has proper permissions to access files
    • For push/pull operations, check file permissions on both device and host
  4. "Command timed out"

    • Increase the timeout in the AdbClient constructor options
    • Check device connectivity

Debugging Tips

  1. Use verbose() to see detailed command output:

    const result = await adb.verbose().exec("devices");
  2. Check ADB version:

    const version = await adb.exec("version");
    console.log(version);
  3. Test connection:

    const isConnected = await testAdbConnection();
    console.log(isConnected ? "Connected" : "Not connected");

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

This project is licensed under the MIT License - see the LICENSE file for details.


For more information and updates, visit the GitHub repository.