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

@websublime/workspace-tools

v2.0.23

Published

Bindings for node from crate workspace-tools

Readme

@websublime/workspace-tools

https://github.com/websublime/workspace-tools/actions

Node.js bindings for workspace management tools - version bumping, changelogs, and monorepo automation.

Install

pnpm add @websublime/workspace-tools
# or
npm install @websublime/workspace-tools
# or
yarn add @websublime/workspace-tools

Overview

This package provides native Node.js bindings (via napi-rs) for workspace management operations. It supports all major package managers (npm, yarn, pnpm, bun) and provides a comprehensive API for:

  • Workspace Status - Get information about your workspace, packages, and pending changesets
  • Changesets - Create, update, list, and manage changesets for version tracking
  • Version Bumping - Preview, apply, and snapshot version bumps with dependency propagation
  • Command Execution - Run commands across workspace packages with filtering and timeout support
  • Configuration - View and inspect workspace configuration settings
  • Initialization - Set up changeset-based version management in your workspace

Quick Start

import {
  status,
  changesetAdd,
  bumpPreview,
  bumpApply,
  execute,
} from '@websublime/workspace-tools'

// Get workspace status
const statusResult = await status({ root: '.' })
if (statusResult.success) {
  console.log(`Found ${statusResult.data.packages.length} packages`)
  console.log(`Package manager: ${statusResult.data.packageManager.name}`)
}

// Add a changeset
const changesetResult = await changesetAdd({
  root: '.',
  packages: ['@scope/my-package'],
  bump: 'minor',
  message: 'Add new feature',
})

// Preview version bumps
const previewResult = await bumpPreview({ root: '.', showDiff: true })
if (previewResult.success) {
  for (const pkg of previewResult.data.packages) {
    console.log(`${pkg.name}: ${pkg.currentVersion} → ${pkg.nextVersion}`)
  }
}

// Apply version bumps with git integration
const applyResult = await bumpApply({
  root: '.',
  gitCommit: true,
  gitTag: true,
})

// Execute commands across packages
const execResult = await execute({
  root: '.',
  cmd: 'npm:test',
  parallel: true,
  timeoutSecs: 300,
})

API Reference

All functions return a Promise with an ApiResponse structure:

interface ApiResponse<T> {
  success: boolean
  data?: T // Present when success is true
  error?: ErrorInfo // Present when success is false
}

interface ErrorInfo {
  code: string // e.g., 'EVALIDATION', 'ENOENT', 'ETIMEOUT'
  message: string
  context?: string
  kind: string
}

Core Functions

| Function | Description | |----------|-------------| | getVersion() | Returns the version of the native bindings | | status(params) | Get comprehensive workspace status information | | init(params) | Initialize changeset-based version management | | configShow(params) | Show current workspace configuration |

Changeset Functions

| Function | Description | |----------|-------------| | changesetAdd(params) | Create a new changeset | | changesetUpdate(params) | Update an existing changeset | | changesetList(params) | List pending changesets with filtering | | changesetShow(params) | Show details of a specific changeset | | changesetRemove(params) | Remove a changeset | | changesetHistory(params) | Query archived changeset history | | changesetCheck(params) | Check if a changeset exists for a branch |

Bump Functions

| Function | Description | |----------|-------------| | bumpPreview(params) | Preview version bumps without applying changes | | bumpApply(params) | Apply version bumps with optional Git integration | | bumpSnapshot(params) | Generate snapshot versions for pre-release testing |

Execute Function

| Function | Description | |----------|-------------| | execute(params) | Run commands across workspace packages with timeout support |

Config Functions

| Function | Description | |----------|-------------| | configShow(params) | Show current workspace configuration settings |


Detailed API

status(params: StatusParams): Promise<StatusApiResponse>

Get comprehensive workspace status information.

interface StatusParams {
  root: string // Workspace root directory
  configPath?: string // Optional custom config path
}

interface StatusData {
  repository: RepositoryInfo
  packageManager: PackageManagerInfo
  branch?: BranchInfo
  changesets: ChangesetInfo[]
  packages: PackageInfo[]
}

Example:

const result = await status({ root: '.' })
if (result.success) {
  console.log(`Repository type: ${result.data.repository.kind}`)
  console.log(`Branch: ${result.data.branch?.name}`)
  console.log(`Pending changesets: ${result.data.changesets.length}`)
}

init(params: InitParams): Promise<InitApiResponse>

Initialize changeset-based version management.

interface InitParams {
  root: string
  changesetPath?: string // Default: '.changesets'
  environments?: string[] // Available environments
  defaultEnv?: string // Default environment
  strategy?: 'independent' | 'unified'
  configFormat?: 'toml' | 'json' | 'yaml'
  force?: boolean // Overwrite existing config
}

Example:

const result = await init({
  root: '.',
  strategy: 'independent',
  configFormat: 'toml',
})

configShow(params: ConfigShowParams): Promise<ConfigShowApiResponse>

Show current workspace configuration from repo.config.{json,toml,yaml}.

interface ConfigShowParams {
  root: string // Workspace root directory
  configPath?: string // Optional custom config file path
}

interface ConfigShowData {
  configPath: string // Path to loaded config file
  configFormat: string // Format: 'json', 'toml', or 'yaml'
  config: ConfigData // Full configuration object
}

interface ConfigData {
  changeset: ChangesetConfigInfo
  version: VersionConfigInfo
  dependency: DependencyConfigInfo
  upgrade: UpgradeConfigInfo
  changelog: ChangelogConfigInfo
  audit: AuditConfigInfo
  git: GitConfigInfo
  execute: ExecuteConfigInfo
}

Example:

const result = await configShow({ root: '.' })

if (result.success) {
  console.log(`Config loaded from: ${result.data.configPath}`)
  console.log(`Format: ${result.data.configFormat}`)

  const { config } = result.data

  // Version settings
  console.log(`Strategy: ${config.version.strategy}`)
  console.log(`Default bump: ${config.version.defaultBump}`)

  // Changeset settings
  console.log(`Changeset path: ${config.changeset.path}`)
  console.log(`History path: ${config.changeset.historyPath}`)

  // Dependency propagation
  console.log(`Propagate deps: ${config.dependency.propagateDependencies}`)
  console.log(`Max depth: ${config.dependency.maxDepth}`)

  // Execute settings
  console.log(`Timeout: ${config.execute.timeoutSecs}s`)
  console.log(`Max parallel: ${config.execute.maxParallel}`)
}

With custom config path:

const result = await configShow({
  root: '.',
  configPath: 'custom/repo.config.json',
})

changesetAdd(params: ChangesetAddParams): Promise<ChangesetAddApiResponse>

Create a new changeset.

interface ChangesetAddParams {
  root: string
  packages: string[] // Package names to include
  bump: 'major' | 'minor' | 'patch' | 'none'
  message: string // Changeset message/description
  env?: string // Environment (e.g., 'production')
}

Example:

const result = await changesetAdd({
  root: '.',
  packages: ['@scope/core', '@scope/utils'],
  bump: 'minor',
  message: 'Add new utility functions',
})

if (result.success) {
  console.log(`Created changeset: ${result.data.id}`)
}

changesetList(params: ChangesetListParams): Promise<ChangesetListApiResponse>

List pending changesets with filtering and sorting.

interface ChangesetListParams {
  root: string
  configPath?: string
  env?: string // Filter by environment
  bump?: string // Filter by bump type
  sort?: 'date' | 'bump' | 'branch' // Sort order
}

Example:

const result = await changesetList({
  root: '.',
  bump: 'minor',
  sort: 'date',
})

if (result.success) {
  for (const changeset of result.data.changesets) {
    console.log(`${changeset.id}: ${changeset.bump} - ${changeset.packages.join(', ')}`)
  }
}

bumpPreview(params: BumpPreviewParams): Promise<BumpPreviewApiResponse>

Preview version bumps without applying changes.

interface BumpPreviewParams {
  root: string
  configPath?: string
  packages?: string[] // Filter to specific packages
  showDiff?: boolean // Show detailed diffs
}

interface BumpPreviewData {
  strategy: string
  packages: PackageVersionInfo[]
  summary: BumpSummaryInfo
  changesets: string[]
}

interface PackageVersionInfo {
  name: string
  path: string
  currentVersion: string
  nextVersion: string
  bump: string
  dependencyUpdates: DependencyUpdateInfo[]
}

Example:

const result = await bumpPreview({ root: '.', showDiff: true })

if (result.success) {
  console.log(`Strategy: ${result.data.strategy}`)
  console.log(`Summary: ${result.data.summary.totalPackages} packages`)
  console.log(`  Major: ${result.data.summary.majorBumps}`)
  console.log(`  Minor: ${result.data.summary.minorBumps}`)
  console.log(`  Patch: ${result.data.summary.patchBumps}`)

  for (const pkg of result.data.packages) {
    console.log(`${pkg.name}: ${pkg.currentVersion} → ${pkg.nextVersion} (${pkg.bump})`)
  }
}

bumpApply(params: BumpApplyParams): Promise<BumpApplyApiResponse>

Apply version bumps with optional Git integration and prerelease support.

interface BumpApplyParams {
  root: string
  configPath?: string
  packages?: string[]
  gitCommit?: boolean // Create a commit
  gitTag?: boolean // Create version tags
  gitPush?: boolean // Push to remote
  prerelease?: string // Prerelease tag (e.g., 'alpha', 'beta', 'rc')
  noChangelog?: boolean // Skip changelog generation
  noArchive?: boolean // Skip archiving changesets
  alwaysArchive?: boolean // Archive even on failure
  force?: boolean // Skip confirmations
}

interface BumpApplyData {
  strategy: string
  packagesUpdated: number
  changesetsArchived: number
  filesModified: string[]
  tagsCreated: string[]
  commitSha?: string
}

Example:

// Standard release
const result = await bumpApply({
  root: '.',
  gitCommit: true,
  gitTag: true,
  gitPush: true,
})

// Prerelease (beta)
const betaResult = await bumpApply({
  root: '.',
  prerelease: 'beta',
  gitCommit: true,
  gitTag: true,
})

if (result.success) {
  console.log(`Updated ${result.data.packagesUpdated} packages`)
  console.log(`Commit: ${result.data.commitSha}`)
  console.log(`Tags: ${result.data.tagsCreated.join(', ')}`)
}

bumpSnapshot(params: BumpSnapshotParams): Promise<BumpSnapshotApiResponse>

Generate snapshot versions for pre-release testing and CI.

interface BumpSnapshotParams {
  root: string
  configPath?: string
  packages?: string[]
  format?: string // Snapshot format template
}

interface BumpSnapshotData {
  strategy: string
  packages: SnapshotVersionInfo[]
  format: string
}

Format Variables:

  • {version} - Current package version
  • {branch} - Current Git branch (sanitized)
  • {commit} - Full Git commit hash
  • {short_commit} - Short Git commit hash (7 chars)
  • {timestamp} - Unix timestamp

Example:

const result = await bumpSnapshot({
  root: '.',
  format: '{version}-snapshot.{short_commit}',
})

if (result.success) {
  for (const pkg of result.data.packages) {
    console.log(`${pkg.name}: ${pkg.originalVersion} → ${pkg.snapshotVersion}`)
  }
}

execute(params: ExecuteParams): Promise<ExecuteApiResponse>

Execute commands across workspace packages with filtering, parallelism, and timeout support.

interface ExecuteParams {
  root: string
  cmd: string // Command to execute (e.g., 'npm:test' or 'ls -la')
  filterPackage?: string[] // Run only on specific packages
  affected?: boolean // Run only on affected packages
  since?: string // Since commit/branch/tag (with affected)
  until?: string // Until commit/branch/tag (with affected)
  branch?: string // Compare against branch (with affected)
  parallel?: boolean // Run commands in parallel
  args?: string[] // Additional arguments
  timeoutSecs?: number // Global timeout in seconds
  perPackageTimeoutSecs?: number // Per-package timeout
}

interface ExecuteData {
  command: string
  results: PackageExecutionResult[]
  summary: ExecuteSummary
}

interface PackageExecutionResult {
  package: string
  success: boolean
  exitCode: number
  durationMs: number
  error?: string
}

interface ExecuteSummary {
  total: number
  succeeded: number
  failed: number
  totalDurationMs: number
}

Command Formats:

  • npm:<script> - Run an npm script (e.g., npm:test, npm:build)
  • Plain command - Run a system command (e.g., ls -la, node index.js)

Example:

// Run tests on all packages in parallel
const result = await execute({
  root: '.',
  cmd: 'npm:test',
  parallel: true,
  timeoutSecs: 300,
})

if (result.success) {
  console.log(`${result.data.summary.succeeded}/${result.data.summary.total} passed`)

  for (const pkg of result.data.results) {
    const icon = pkg.success ? '✓' : '✗'
    console.log(`${icon} ${pkg.package}: ${pkg.durationMs}ms`)
  }
}

// Run on affected packages only
const affectedResult = await execute({
  root: '.',
  cmd: 'npm:build',
  affected: true,
  branch: 'main',
  parallel: true,
})

// Run on specific packages
const filteredResult = await execute({
  root: '.',
  cmd: 'npm:lint',
  filterPackage: ['@scope/core', '@scope/utils'],
})

Note: filterPackage and affected are mutually exclusive.


Error Handling

All functions return an ApiResponse with consistent error handling:

const result = await bumpPreview({ root: '/invalid/path' })

if (!result.success) {
  switch (result.error.code) {
    case 'ENOENT':
      console.error('Path not found:', result.error.message)
      break
    case 'EVALIDATION':
      console.error('Invalid parameters:', result.error.message)
      break
    case 'ETIMEOUT':
      console.error('Operation timed out:', result.error.message)
      break
    case 'EGIT':
      console.error('Git error:', result.error.message)
      break
    default:
      console.error(`Error [${result.error.code}]:`, result.error.message)
  }
}

Error Codes

| Code | Description | |------|-------------| | EVALIDATION | Invalid parameters | | ENOENT | Path or resource not found | | ETIMEOUT | Operation timed out | | EGIT | Git operation failed | | EEXEC | Execution error | | ECONFIG | Configuration error | | EIO | I/O error |


TypeScript Support

Full TypeScript definitions are included. Import types directly:

import type {
  StatusParams,
  StatusData,
  ChangesetAddParams,
  BumpPreviewParams,
  BumpPreviewData,
  ExecuteParams,
  ExecuteData,
  ConfigShowParams,
  ConfigShowData,
  ConfigData,
  ErrorInfo,
} from '@websublime/workspace-tools'

Development

Requirements

  • Rust (latest stable)
  • Node.js 16+
  • pnpm

Build

pnpm install
pnpm build-binding:release
pnpm build-types

Test

pnpm test

License

MIT