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

nodejs-shared

v2.0.1

Published

Batteries-included Node.js utility belt for files, images, validation, and more.

Readme

nodejs-shared

Batteries-included Node.js utility belt for files, images, validation, and more.

Install

npm install nodejs-shared

API

Browser

Parses user agent strings to extract browser, OS, and platform info.

| Method | Description | |--------|-------------| | parse(ua) | Parses a UA string. Returns {platform, osName, osVersion, browserName} or null for bots/unknown. |

import {Browser} from 'nodejs-shared';

// Mobile browser
const mobile = Browser.parse(
  'Mozilla/5.0 (Linux; Android 13; Pixel 7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Mobile Safari/537.36'
);
// {platform: 'mobile', osName: 'Android', osVersion: 13, browserName: 'Chrome'}

// Desktop browser
const desktop = Browser.parse(
  'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36'
);
// {platform: 'desktop', osName: 'Windows', osVersion: 10, browserName: 'Chrome'}

// Bot or unknown UA → null
Browser.parse('Googlebot/2.1'); // null

FileUtils

File system operations: read, write, copy, delete, permissions, and more.

| Method | Description | |--------|-------------| | basename(filePath, withExtension?) | Extracts the file name from a path. withExtension: include extension in result (default false). | | changePermission(filePath, mode?) | Changes file permissions (default 0o755). | | changeOwner(filePath, username, groupName?) | Changes owner and/or group. | | copy(source, destination) | Copies a file or directory recursively. | | deleteDirectory(dirPath) | Deletes a directory and all contents. | | deleteFile(filePath) | Deletes a file. No-op if it doesn't exist. | | exists(filePath) | Checks if a file or directory exists. | | getExtension(filePath) | Returns the file extension without the dot. | | getFilemtime(filePath) | Returns last modification time as Unix timestamp. | | getTmpDirectory() | Returns the system temp directory path. | | getTmpPath(extension?) | Generates a unique temp file path with the given extension. | | glob(pattern, options?) | Finds files matching a glob pattern. cwd: base directory. ignore: patterns to exclude. | | isBase64(str) | Checks if a string is valid base64. | | isDirectory(path) | Checks if a path is a directory. | | isFile(path) | Checks if a path is a file. | | isPath(str) | Checks if a string is a valid file system path (format only, not existence). | | makeDirectory(dirPath, options?) | Creates a directory recursively. mode: permissions (default 0o755). owner: {username, groupName}. | | makeTmpDirectory(options?) | Creates a unique temp directory. Same options as makeDirectory. | | readAsBase64(filePath) | Reads a file as a base64 string. | | readAsDataUrl(filePath) | Reads a file as a data URL string (MIME type auto-detected). | | readAsJson(filePath) | Reads and parses a JSON file. | | readAsString(filePath) | Reads a file as a UTF-8 string. | | rename(source, destination) | Renames (moves) a file or directory. | | write(filePath, content?, options?) | Writes content to a file. Creates parent dirs automatically. mode: permissions (default 0o755). owner: {username, groupName}. |

import {FileUtils} from 'nodejs-shared';

// File name extraction
FileUtils.basename('/home/user/report.pdf');        // 'report'
FileUtils.basename('/home/user/report.pdf', true);  // 'report.pdf'

// Read files in various formats
const config = FileUtils.readAsJson('/etc/app/config.json');
const text = FileUtils.readAsString('/etc/hosts');
const base64 = FileUtils.readAsBase64('/uploads/photo.jpg');
const dataUrl = FileUtils.readAsDataUrl('/uploads/photo.png');
// 'data:image/png;base64,iVBORw0KGgo...'

// Write with file permissions and ownership
FileUtils.write('/var/log/app.log', 'started', {
  mode: 0o644,
  owner: {username: 'nginx', groupName: 'nginx'},
});

// Create directory with permissions and ownership
FileUtils.makeDirectory('/var/www/uploads', {
  mode: 0o755,
  owner: {username: 'nginx', groupName: 'nginx'},
});

// Find files by glob pattern
const images = FileUtils.glob('/uploads/**/*.{jpg,png}');

// Search relative to a base directory
FileUtils.glob('**/*.txt', {cwd: '/var/data'});

// Exclude specific patterns
FileUtils.glob('/uploads/**/*', {ignore: ['/uploads/tmp/**']});

// Generate a unique temp file path
const tmpPath = FileUtils.getTmpPath('jpg'); // '/tmp/a1b2c3d4.jpg'

ImageUtils

Image manipulation powered by Sharp and ImageMagick.

| Method | Description | |--------|-------------| | cropImage(input, output, options) | Crops a rectangular region. left, top, width, height in pixels. Negative offsets are clamped to 0. | | mergeImages(inputs, output, options?) | Merges multiple images. direction: 'vertical'/'horizontal' (default 'vertical'). color: canvas background (default black). align: 'start'/'center'/'end' (default 'start'). offset: spacing in px between images (default 0). margin: number, CSS shorthand string, or {top, right, bottom, left} object (default 0). | | resizeImage(input, options?) | Resizes an image. width, height: target size. output: save path (omit to overwrite original). contain: fit within bounds instead of cropping (default false, crops to cover). | | getGifFrameCount(input) | Returns the number of frames in a GIF. Accepts file path or data URL. | | extractFirstGifFrame(input, output?) | Extracts the first frame of an animated GIF. Accepts file path or data URL. | | getImageDimensions(filePath) | Returns {width, height} or null. | | convertImageFormat(input, output?, options?) | Converts between formats (JPEG, PNG, BMP, etc.) via ImageMagick. Accepts file path or data URL. margin: border size in pixels. background: border color (default 'white'). bmpVersion: 'bmp2'/'bmp3'/'bmp4' (default 'bmp3'). trueColor: use TrueColor for BMP (default true). Returns data URL. |

import {ImageUtils} from 'nodejs-shared';

// Crop a 200x200 region starting at (100, 50)
await ImageUtils.cropImage('photo.jpg', 'cropped.jpg', {
  left: 100, top: 50, width: 200, height: 200,
});

// Merge side by side, centered vertically, 10px gap between images
await ImageUtils.mergeImages(
  ['left.png', 'right.png'],
  'combined.png',
  {direction: 'horizontal', align: 'center', offset: 10},
);

// Stack vertically with white background and margin
await ImageUtils.mergeImages(
  ['header.png', 'body.png', 'footer.png'],
  'stacked.png',
  {
    direction: 'vertical',
    color: 'white',               // canvas background color
    margin: '20 30',              // CSS shorthand: top/bottom 20px, left/right 30px
    // margin: 10,                // uniform 10px on all sides
    // margin: {top: 5, right: 10, bottom: 15, left: 20},  // per-side
  },
);

// Resize to width (height auto-calculated to keep aspect ratio)
await ImageUtils.resizeImage('photo.jpg', {width: 800, output: 'thumb.jpg'});

// Fit within 400x300 without cropping (letterboxed)
await ImageUtils.resizeImage('photo.jpg', {
  width: 400, height: 300, contain: true, output: 'fitted.jpg',
});

// Omit output to overwrite original file
await ImageUtils.resizeImage('photo.jpg', {width: 800});

// GIF frame count (accepts file path or data URL)
const frames = await ImageUtils.getGifFrameCount('animation.gif'); // 35

// Extract first frame from animated GIF
await ImageUtils.extractFirstGifFrame('animation.gif', 'first-frame.jpg');

// Get image dimensions
ImageUtils.getImageDimensions('photo.jpg'); // {width: 1920, height: 1080}

// Convert format (returns data URL of the converted image)
const dataUrl = await ImageUtils.convertImageFormat('photo.jpg', 'photo.png');

// Convert with 10px black border around the image
await ImageUtils.convertImageFormat('photo.jpg', 'photo.bmp', {
  margin: 10, background: 'black',
});

MediaUtils

Data URL handling and image file writing.

| Method | Description | |--------|-------------| | isDataUrl(str) | Checks if a string is a valid base64 data URL. | | parseDataUrl(str) | Extracts mimeType, base64, extension, and bytesize from a data URL. Returns null if invalid. | | writeImage(path, data, options?) | Writes an image from a data URL, Buffer, or SVG string. mode: file permissions (default 0o755). owner: {username, groupName}. |

import {MediaUtils} from 'nodejs-shared';

// Validate data URL format
MediaUtils.isDataUrl('data:image/png;base64,iVBORw0KGgo='); // true
MediaUtils.isDataUrl('https://example.com/image.png');       // false

// Parse data URL into components
const parts = MediaUtils.parseDataUrl('data:image/jpeg;base64,/9j/4AAQ...');
// {mimeType: 'image/jpeg', base64: '/9j/4AAQ...', extension: 'jpg', bytesize: 45056}

// Write image from data URL (base64 decoded automatically)
MediaUtils.writeImage('/uploads/photo.jpg', 'data:image/jpeg;base64,...');

// Write image from Buffer
MediaUtils.writeImage('/uploads/photo.png', buffer);

// Write raw SVG string directly
MediaUtils.writeImage('/uploads/icon.svg', '<svg>...</svg>');

// Write with file permissions and ownership
MediaUtils.writeImage('/uploads/photo.png', buffer, {
  mode: 0o644,
  owner: {username: 'nginx', groupName: 'nginx'},
});

ProcessUtils

System user and group lookup (Unix/Linux).

| Method | Description | |--------|-------------| | getUid(username) | Returns the UID for a username. | | getGid(groupName) | Returns the GID for a group name. |

import {ProcessUtils} from 'nodejs-shared';

ProcessUtils.getUid('www-data'); // 33
ProcessUtils.getGid('www-data'); // 33

Reflect

Reflection utilities for inspecting class methods.

| Method | Description | |--------|-------------| | getStaticMethods(clazz) | Returns a Set<string> of static method names. | | getMethods(instance) | Returns a Set<string> of instance method names. |

import {Reflect} from 'nodejs-shared';

class UserService {
  static create(name) { /* ... */ }
  static findById(id) { /* ... */ }
  save() { /* ... */ }
  delete() { /* ... */ }
}

Reflect.getStaticMethods(UserService);      // Set {'create', 'findById'}
Reflect.getMethods(new UserService());      // Set {'constructor', 'save', 'delete'}

Regex

Regular expression utilities.

| Method | Description | |--------|-------------| | escape(str, replace?) | Escapes RegExp special characters. replace: {char: replacement} map to customize escaping per character. |

import {Regex} from 'nodejs-shared';

// Escape special chars for safe use in new RegExp()
Regex.escape('price: $9.99 (USD)');
// 'price: \\$9\\.99 \\(USD\\)'

// Custom replacements: '.' → character class, '*' → wildcard
Regex.escape('file.*.txt', {'.': '[.]', '*': '.*'});
// 'file[.].*[.]txt'

Validators

30 validation functions wrapping validator.js. All follow the pattern (value, options?) => boolean.

import {validators} from 'nodejs-shared';

// Basic usage
validators.isEmail('[email protected]');                    // true
validators.isURL('https://example.com');                   // true
validators.isIP('192.168.1.1');                            // true
validators.isJSON('{"key": "value"}');                     // true

// Range / bounds
validators.isInt('42', {min: 1, max: 100});                // true
validators.isFloat('3.14', {gt: 0, lt: 10});               // true

// Date format
validators.isDate('15-03-2025', {format: 'DD-MM-YYYY'});   // true

// Whitespace-only string → empty
validators.isEmpty('  ', {ignoreWhitespace: true});         // true

// Allow spaces in alpha check
validators.isAlpha('hello world', {ignore: ' '});           // true

// Must have a decimal point
validators.isDecimal('3.50', {forceDecimal: true});         // true

// Reject +, -, . symbols
validators.isNumeric('+123', {noSymbols: true});            // false

// Accept "yes"/"no"/"1"/"0" as boolean
validators.isBoolean('yes', {loose: true});                 // true

// "Name <addr>" format
validators.isEmail('"User" <[email protected]>', {allowDisplayName: true}); // true

// Allow # fragment in URL
validators.isURL('https://example.com#section', {allowFragments: true});   // true

// UUID v4 only
validators.isUUID('550e8400-e29b-41d4-a716-446655440000', 4); // true

// Accept bare strings/numbers as valid JSON
validators.isJSON('"hello"', {allowPrimitives: true});      // true

// CIDR notation (192.168.1.0/24)
validators.isIP('192.168.1.0/24', {version: 4, allowRange: true}); // true

| Validator | Description | |-----------|-------------| | isAfter(value, date?) | Date is after the comparison date (default: now). | | isAlpha(value, options?) | Contains only letters. ignore: chars/regex to skip. | | isAlphanumeric(value, options?) | Contains only letters and numbers. ignore: chars/regex to skip. | | isBefore(value, date?) | Date is before the comparison date (default: now). | | isBoolean(value, options?) | Is a boolean string. loose: also accept 'yes'/'no', '0'/'1'. | | isDataURI(value) | Is a valid data URI. | | isDate(value, options?) | Is a valid date. format (default 'YYYY/MM/DD'), strictMode, delimiters. | | isDecimal(value, options?) | Is a decimal number. forceDecimal: require decimal point. decimalDigits: allowed digits (e.g. '1,3'). | | isEmail(value, options?) | Is a valid email. allowDisplayName, requireDisplayName, allowUtf8LocalPart, requireTld, hostBlacklist, hostWhitelist. | | isEmpty(value, options?) | Is empty (null, undefined, NaN, [], false, ''). ignoreWhitespace: treat whitespace-only as empty. | | isFQDN(value, options?) | Is a fully qualified domain name. requireTld, allowWildcard. | | isFQDNorIP(value, options?) | Is an FQDN or IP address. requireTld, allowWildcard (FQDN), version, allowRange (IP). | | isFloat(value, options?) | Is a float. min, max, gt, lt. | | isHash(value, algorithm) | Is a hash. algorithm: 'md4', 'md5', 'sha1', 'sha256', 'sha384', 'sha512', etc. | | isHexadecimal(value) | Is a hexadecimal value. | | isHexColor(value) | Is a hex color code. | | isHSL(value) | Is an HSL color. | | isIn(value, values) | Is in the specified array, string, or object. | | isInt(value, options?) | Is an integer. min, max, gt, lt, allowLeadingZeroes. | | isIP(value, options?) | Is an IP address. version (4/6), allowRange (CIDR). | | isJSON(value, options?) | Is valid JSON. allowPrimitives: accept "true", "123", etc. | | isJWT(value) | Is a valid JWT. | | isLength(value, options?) | String length is within bounds. min (default 0), max. | | isLowercase(value) | Is all lowercase. | | isNumeric(value, options?) | Contains only numbers. noSymbols: disallow +, -, .. | | isPort(value) | Is a valid port number (0-65535). | | isRGBColor(value, options?) | Is an RGB/RGBA color. includePercentValues: allow rgb(100%,0%,0%). | | isUppercase(value) | Is all uppercase. | | isURL(value, options?) | Is a valid URL. requireTld, allowWildcard, allowFragments, allowQueryComponents. | | isUUID(value, version?) | Is a valid UUID. version: 1|2|3|4|5|'all' (default 'all'). |

Requirements

  • Node.js 18+
  • ImageMagick — required by ImageUtils for GIF frame operations and format conversion

Testing

npm test

Changelog

See CHANGELOG.md for all release notes.

Author

shumatsumonobu GitHub / X / Facebook

License

MIT