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

@eslam-reda-div/bridger

v1.0.0

Published

Universal Runtime Bridge - Use any Python/PHP package directly from Node.js

Readme

🌉 Bridger

Universal Runtime Bridge — Use any Python or PHP package directly from Node.js.

Instead of rewriting libraries, Bridger runs them in their native runtime and bridges the communication via a fast IPC protocol. You call Python's numpy.sum() or PHP's Collection::make() as if they were native JavaScript functions.

Architecture

Your Node.js App
       │
   bridge("python:numpy")
       │
  ┌────┴─────┐
  │  Bridger │  ← JS Proxy + IPC Protocol
  └────┬─────┘
       │
  ┌────┴──────────┐
  │ Python Worker  │  ← Persistent child process
  │ (numpy loaded) │
  └───────────────┘

Quick Start

1. Initialize

npx bridger init

2. Install packages

# Python packages (from pip)
npx bridger install python:numpy
npx bridger install python:pandas
npx bridger install python:requests
npx bridger install python:scikit-learn

# PHP packages (from composer)
npx bridger install php:spatie/collection
npx bridger install php:guzzlehttp/guzzle

3. Use in your code

const { bridge, shutdown } = require("bridger");

async function main() {
  // ─── Python ──────────────────────────
  const numpy = await bridge("python:numpy");

  const sum = await numpy.sum([1, 2, 3, 4, 5]);
  console.log("Sum:", sum); // 15

  const mean = await numpy.mean([10, 20, 30]);
  console.log("Mean:", mean); // 20

  const arr = await numpy.array([
    [1, 2],
    [3, 4],
  ]);
  const shape = await arr.shape();
  console.log("Shape:", shape); // [2, 2]

  const dot = await numpy.dot([1, 2, 3], [4, 5, 6]);
  console.log("Dot product:", dot); // 32

  // ─── Python: pandas ──────────────────
  const pd = await bridge("python:pandas");

  const df = await pd.DataFrame({ name: ["Alice", "Bob"], age: [30, 25] });
  const ages = await df.age.mean();
  console.log("Mean age:", ages);

  // ─── Python: requests ────────────────
  const requests = await bridge("python:requests");
  const response = await requests.get("https://api.github.com");
  const status = await response.status_code();
  console.log("GitHub API status:", status);

  // ─── PHP ─────────────────────────────
  const php = await bridge("php:spatie/collection");
  const items = await php.Spatie.Collection.Collection.make([1, 2, 3]);
  const arr2 = await items.toArray();
  console.log("Collection:", arr2);

  // Clean up worker processes
  await shutdown();
}

main().catch(console.error);

CLI Reference

bridger install <language:package[@version]>   # Install a package
bridger remove <language:package>              # Remove a package
bridger upgrade [language:package[@version]]   # Upgrade package(s)
bridger list                                   # List installed packages with versions
bridger info <language:package>                # Show package details & dependencies
bridger init                                   # Initialize & detect runtimes
bridger --version                              # Show version

Version-pinned installs

npx bridger install python:[email protected]
npx bridger install python:pandas@>=2.0,<3.0
npx bridger install php:spatie/collection@^7.0

Install multiple packages at once

npx bridger install python:numpy python:pandas python:matplotlib

API Reference

bridge(spec)

Import a package from another language.

const { bridge } = require("bridger");

const numpy = await bridge("python:numpy");
const php = await bridge("php:some/package");

Returns a Proxy object that mirrors the remote package's API.

Calling functions

Every call is async (crosses process boundary):

const result = await numpy.sum([1, 2, 3]);
const arr = await numpy.linspace(0, 10, 100);

Getting attribute values

Use $value() to read non-callable attributes:

const pi = await numpy.pi(); // Works: pi is returned as value since it's not callable
const version = await numpy.__version__();

Chained access

Property chains work naturally:

const result = await numpy.random.randint(0, 100);
const normal = await numpy.random.normal(0, 1, [100]);

Working with objects

Objects returned from Python/PHP become proxy references:

const arr = await numpy.array([1, 2, 3]); // Returns a proxy to the remote object
const doubled = await arr.$mul(2); // Operator: arr * 2
const list = await doubled.tolist(); // Get back a JS array

Indexing & Length

const arr = await numpy.array([10, 20, 30, 40, 50]);

await arr.$getitem(0); // 10  — like arr[0] in Python
await arr.$setitem(1, 99); // arr[1] = 99
await arr.$len(); // 5  — like len(arr)
await arr.$contains(30); // true — like 30 in arr

Operators

const a = await numpy.array([1, 2, 3]);

await a.$add(10); // [11, 12, 13]
await a.$sub(1); // [0, 1, 2]
await a.$mul(3); // [3, 6, 9]
await a.$div(2); // [0.5, 1.0, 1.5]
await a.$pow(2); // [1, 4, 9]
await a.$neg(); // [-1, -2, -3]
await a.$matmul(b); // matrix multiplication
await a.$eq(other); // element-wise comparison
await a.$op("floordiv", 2); // any operator via $op()

Iteration & Inspection

const items = await collection.$iter(); // Collect iterable to JS array
const repr = await arr.$repr(); // repr(arr) string
const str = await arr.$str(); // str(arr) string
const type = await arr.$type(); // { type: 'ndarray', module: 'numpy', ... }
const attrs = await arr.$dir(); // list of attribute names

Keyword Arguments (Python)

const df = await pd.DataFrame.$call({
  data: { name: ["Alice", "Bob"], age: [25, 30] },
});

Introspection

Discover what's available on a module or object:

const info = await numpy.$introspect();
console.log(info.functions); // [{name: 'sum', signature: '...'}, ...]
console.log(info.classes); // [{name: 'ndarray', methods: [...]}, ...]

PHP classes

Access PHP classes via namespace path, use .new() to instantiate:

const php = await bridge("php:vendor/package");

// Static method
const result = await php.Namespace.ClassName.staticMethod(args);

// Create instance
const instance = await php.Namespace.ClassName.new(args);

// Call instance method
const value = await instance.someMethod();

shutdown()

Shutdown all runtime workers. Call when your app is done:

const { shutdown } = require("bridger");
await shutdown();

createBridge(projectRoot)

Create an independent bridge instance pointing to a specific project:

const { createBridge } = require("bridger");
const myBridge = createBridge("/path/to/project");
const np = await myBridge.import("python:numpy");

How It Works

  1. Package Installation: bridger install python:numpy creates a Python venv in .runtimes/python/venv/ and installs numpy via pip.

  2. Worker Process: When you call bridge('python:numpy'), Bridger spawns a persistent Python worker process that communicates via stdin/stdout JSON protocol.

  3. Proxy Objects: The returned object is a JavaScript Proxy. Property access builds a path, and function calls send RPC messages to the worker.

  4. Object References: Complex Python/PHP objects (classes, arrays, DataFrames) are stored in the worker's memory and referenced by ID. The JS proxy holds the ID and forwards method calls.

  5. Serialization: Primitives, arrays, and dicts are serialized directly. Large numpy arrays and DataFrames are stored as references to avoid serialization overhead.

.runtimes/ Directory

.runtimes/
├── bridger.json          # Manifest — package constraints & runtime versions
├── bridger.lock.json     # Lockfile — exact resolved versions & dependencies
├── python/
│   └── venv/             # Python virtual environment with installed packages
└── php/
    ├── composer.json      # Composer manifest
    ├── composer.lock      # Composer lockfile
    └── vendor/            # Installed PHP packages

bridger.json (manifest)

{
  "version": "1.0.0",
  "runtimes": {
    "python": { "version": "3.11.5" },
    "php": { "version": "8.3.6" }
  },
  "packages": {
    "python:numpy": {
      "language": "python",
      "package": "numpy",
      "version": "*"
    },
    "python:pandas": {
      "language": "python",
      "package": "pandas",
      "version": ">=2.0"
    }
  }
}

bridger.lock.json (lockfile)

{
  "lockfileVersion": 1,
  "runtimes": {
    "python": { "version": "3.11.5", "path": ".runtimes/python/venv" }
  },
  "packages": {
    "python:numpy": {
      "installedVersion": "2.4.2",
      "versionConstraint": "*",
      "dependencies": [],
      "installedAt": "2024-01-15T10:30:00.000Z"
    }
  }
}

Add .runtimes/ to your .gitignore.

Requirements

  • Node.js >= 16
  • Python 3 (for Python packages) — must be on PATH
  • PHP >= 8.0 (for PHP packages) — must be on PATH
  • Composer (for PHP packages) — must be on PATH

Supported Languages

| Language | Package Manager | Status | | -------- | --------------- | --------------- | | Python | pip | ✅ Full support | | PHP | Composer | ✅ Full support |

Limitations

  • All calls are async (they cross process boundaries)
  • Callback functions cannot be passed across language boundaries
  • Very large data transfers may have serialization overhead (use object references for large data)
  • First call has startup latency (worker process initialization)

License

MIT