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

sharedthread

v1.1.2

Published

A high-performance asynchronous multithreading library that wraps Node.js Worker Threads and SharedArrayBuffer for efficient, zero-copy variable sharing.

Readme

SharedThread

v1.1.2

A high-performance asynchronous multithreading library that wraps Node.js Worker Threads and SharedArrayBuffer for efficient, zero-copy variable sharing.

Features

  • Zero-Copy Architecture: Uses SharedArrayBuffer to mutate variables across threads.
  • Type-Safe Worker Communication: Fully typed API for passing tasks, payloads, and shared memory structures.
  • Promise-Based API: Interact with low-level worker threads using clean, modern async/await patterns.

Installation

Install the package and its peer dependencies via npm:

npm install sharedthread

Usage

External Worker File (Recommended)

Create a new instance using new MainThread(filepath).

main.js

import { MainThread } from 'sharedthread';

// check optimal parallel threads available on the host machine
console.log(`Optimal Threads: ${MainThread.optimalThreads}`); 

async function startWorker() {
  const thread = new MainThread("./my-worker.js", {
    timeout: 5000, // messaging timeout for functions that expect responses
    workerOptions: { // any data you sent here can be accessed from the worker immediately
      workerData: { initialPayload: "Custom Meta Data" }
    }
  });

  // listen to cross-thread communication events(not required)
  thread.on('message', (data, label) => {
    console.log(`Received message with label [${label}]:`, data);
  });

  thread.on('error', (err) => {
    console.error('Thread threw an unhandled error:', err);
  });

  // wait safely until the worker signals it is online
  await thread.ready();
  console.log(`Thread status active: ${thread.isActive}`); // outputs: true
}

startWorker().catch(console.error);

my-worker.js

import { WorkerThread } from 'sharedthread';

console.log(`Read workerData as ${WorkerThread.workerData}`);

WorkerThread.on('message', (data, label) => {
  console.log(`Received message with label [${label}]:`, data);
});

Same File Worker

In CommonJS(require syntax), __filename is the current file. In ES Modules(import syntax), import.meta.filename is is the current file;

main.js

import { MainThread, WorkerThread, isMainThread } from 'sharedthread';

if(isMainThread){
  startWorker().catch(console.error);
}else{
  workerFunction().catch(console.error);
}

async function startWorker() {
  const thread = new MainThread(import.meta.filename);

  //do stuff for main thread
  console.log("main code");
}

async function workerFunction(){
  //do stuff in worker
  console.log("worker code")
}

Variable Sharing

A SharedHeap instance acts as a memory manager allowing for shared variables across threads. Shared variables are defined on the SharedHeap instance using .fromData on the specified data type. MainThread.declareHeap and MainThread.declareVar are used to predefine a variable to all future worker threads. .addHeap, .addVar, .syncHeap, and .syncVar on the thread instance are used if new variables need to be defined and refernced after thread creation.

*directly calling the constructor of a type is not recomended as it constructs from a memory address directly instead of using the build in memory manager

*variables don't need to declared/synced if it is only indirectly referenced(such as through a pointer)

main.js

import { MainThread, SharedFloat64, SharedHeap, SharedInt32, SharedUint32 } from "sharedthread";

// create a heap with 1000 bytes
let myHeap = new SharedHeap(1000);

// create a int32 on the heap with starting value of 10
let myInt32 = SharedInt32.fromData(myHeap, 10);

// declare the heap and variable(only use before worker creation)
MainThread.declareHeap(myHeap, "myHeap");
MainThread.declareVar(myInt32, "myInt32");

async function startWorker(){
  // create worker
  const thread = new MainThread("./my-worker.js");
  thread.on("error", console.error);
  await thread.ready();

  // create a new heap
  let myHeap2 = new SharedHeap(1000);

  // create a int32 on the old heap
  let myUint32 = SharedUint32.fromData(myHeap, 83);

  // create a float64(can hold same values as js number type) on the new heap
  let myFloat64 = SharedFloat64.fromData(myHeap2, 3.14);

  // add the new heap and variables(only use after thread creation)
  thread.addHeap(myHeap2, "myHeap2");
  thread.addVar(myUint32, "myUint32");
  thread.addVar(myFloat64, "myFloat64");

  // wait for the worker thread to modify the values
  await thread.waitFor("modify values");

  // check values after modification
  console.log("on main");

  console.log(myInt32.value); // outputs: 30
  console.log(myUint32.value); // outputs: 59
  console.log(myFloat64.value); // outputs: 2.71

  //terminate the thread now that tasks are finished
  thread.terminate();
}
startWorker().catch(console.error);

my-worker.js

import { WorkerThread } from "sharedthread";

// get predeclared heap and variables
let myHeap = WorkerThread.getHeap("myHeap");
let myInt32 = WorkerThread.getVar("myInt32");

async function runWorker() {
  // sync to heap and variables declared after thread creation
  // note that syncHeap is a required function(for any variables declared on this heap) even if the heap variable isn't used
  let myHeap2 = await WorkerThread.syncHeap("myHeap2");
  let myUint32 = await WorkerThread.syncVar("myUint32");
  let myFloat64 = await WorkerThread.syncVar("myFloat64");

  //check values
  console.log("on worker");

  console.log(myInt32.value); // outputs: 10
  console.log(myUint32.value); // outputs: 83
  console.log(myFloat64.value); // outputs: 3.14

  //modify values
  myInt32.value *= 3;
  myUint32.value -= 24;
  myFloat64.value = 2.71;

  //tell the main thread that the value was modified
  //note that for frequent read/write, a mutex should be used instead
  WorkerThread.signal("modify values");
}
runWorker();

Shared Array

A SharedArray is a array of a fixed size and fixed type that can be accessed across workers. For a resizable array use SharedArrayList instead.

*length is an optional paramater for SharedArrayList but not for SharedArray

main.js

import { MainThread, SharedHeap, SharedArray, SharedInt32 } from "sharedthread";

const myHeap = new SharedHeap(1000);
const myArray = SharedArray.fromData(myHeap, {
type: SharedInt32,
  length: 3,
  // the array property can be used to set an initial value(optional)
  array: [3, 8, 4],
});

MainThread.declareHeap(myHeap, "myHeap");
MainThread.declareVar(myArray, "myArray");

async function startWorker(){
  // create worker
  const thread = new MainThread("./my-worker.js");
  thread.on("error", console.error);
  await thread.ready();

  //wait for thread to finish reading values
  await thread.waitFor("finished reading");

  //subtract 5 from every value
  for(let i = 0; i < myArray.length; i++){
    myArray[i].value -= 5;
  }

  //tell thread that the values were modified
  thread.signal("modify value");

}
startWorker().catch(console.error);

my-worker.js

import { WorkerThread } from "sharedthread";

let myArray = WorkerThread.getVar("myArray");

async function runWorker(){
  //read value before modification
  console.log("before");
  for(let int32 of myArray){
    console.log(int32.value);// outputs 3, 8, 4
  }

  WorkerThread.signal("finished reading");
  await WorkerThread.waitFor("modify value");

  //read data from array after modification
  console.log("after")
  for(let int32 of myArray){
    console.log(int32.value);// outputs -2, 3, -1
  }
}
runWorker();

Custom Struct

Any class that extends SharedStruct can act as a shared struct. Structs automatically convert reference types to pointers to that reference type. *.deref and .value calls on SharedStruct, SharedArray, SharedArrayList may be bypassed using a public property on SharedStruct(.autoDeref and .autoValue), this does NOT work on isolated primitives and pointers *if the property is set set it on both the worker and main thread(it is advised to put it in the types file)

my-types.js

import { TypeRegistry, SharedStruct, SharedArray, SharedInt32 } from "sharedthread";

SharedStruct.autoDeref = false;//set to true to skip .deref calls on structs
SharedStruct.autoValue = false//set to true to skip .value calls on structs

//it is reccomended to have the type declaration in a different file
//this allows both the main thead and the worker to access the type
export class CustomStruct extends SharedStruct{
  static properties = {
    foo: { type: SharedInt32, param: 9},// int32 foo = 9
    bar: { type: SharedArray, param: { // int32[] bar = new int[7]
      type: SharedInt32,
      length: 7,
    }},
  }
}
/*equivalent structure
struct MyStruct{
  int32 foo = 9;
  int32[] arr = new int32[7];
}
*/

//notifies the libary to a custom type
TypeRegistry.registerType(CustomStruct);

main.js

import { MainThread, SharedHeap } from "sharedthread";
import { CustomStruct } from "./my-types.js";


let myHeap = new SharedHeap(1000);
// create a custom strut
let myStruct = CustomStruct.fromData(myHeap, {
    foo: 6,// set initial value of foo to 6
    // note that since bar is a reference type it is translated to a pointer to a SharedArray
    // this means it cannot have a value set in .fromData
});

MainThread.declareHeap(myHeap, "myHeap");
MainThread.declareVar(myStruct, "myStruct");

async function startWorker(){
  // create worker
  const thread = new MainThread("./my-worker.js");
  thread.on("error", console.error);

  // array is a reference type so it needs to be dereferenced when on another reference type
  myStruct.bar.deref[2].value = 5;
}
startWorker().catch(console.error);

my-worker.js

import { WorkerThread } from 'sharedthread';
//this import is required to load the custom type properly
import "./my-types.js";

//verify that the types are loaded properly(declared variables wont be available if not run)
WorkerThread.verifyTypes();

let myStruct = WorkerThread.getVar("myStruct");

async function runWorker(){
  //read data from array
  console.log(myStruct.foo.value); // outputs: 6
  // array is a reference type so it needs to be dereferenced when on another reference type
  console.log(myStruct.bar.deref[2].value); // outputs: 5
}
runWorker();

Pointers

Pointers can point to any shared primitive or reference variable in the same heap.

main.js

import { MainThread, SharedHeap, SharedFloat64, SharedPointer } from "sharedthread";

let myHeap = new SharedHeap(1000);
let myFloat64 = SharedFloat64.fromData(myHeap, Math.PI);
let myPointer = SharedPointer.fromData(myHeap, {
    type: SharedFloat64,
    addr: myFloat64.addr,
})

// notice how we only declare the heap and the pointer
// this is because the float64 is indirectly referenced
MainThread.declareHeap(myHeap, "myHeap");
MainThread.declareVar(myPointer, "myPointer");

async function startWorker(){
  // create worker
  const thread = new MainThread("./my-worker.js");
  thread.on("error", console.error);

  //wait for the worker thread
  await thread.waitFor("pointer modified");

  //read the new number
  console.log(myPointer.deref.value);//outputs: 2.718281828459045(Math.E)

}
startWorker().catch(console.error);

my-worker.js

import { SharedFloat64, WorkerThread } from 'sharedthread';


let myHeap = WorkerThread.getHeap("myHeap");
let myPointer = WorkerThread.getVar("myPointer");

async function runWorker(){
  console.log(myPointer.heldType);// outputs: Class SharedFloat64{...}
  console.log(myPointer.deref.value);// outputs: 3.141592653589793(Math.PI)

  //add a different float32 to the pointer(has to be the same type as before and same heap as pointer)
  let myNewFloat64 = SharedFloat64.fromData(myHeap, Math.E);
  myPointer.deref = myNewFloat64;

  //tell the main thread the pointer has been modified
  WorkerThread.signal("pointer modified");
}
runWorker();

Mutex

A mutex can restrict access to a variable so only one thread has access at a time, preventing data corruption.

main.js

import { MainThread, Mutex, SharedHeap, SharedInt32 } from "sharedthread";

let myHeap = new SharedHeap(1000);
let myMutex = Mutex.fromData(myHeap);
let myInt32 = SharedInt32.fromData(myHeap, 0);

MainThread.declareHeap(myHeap, "myHeap");
MainThread.declareVar(myMutex, "myMutex");
MainThread.declareVar(myInt32, "myInt32");

async function startWorker(){
  // create 4 workers
  const threads = [];
  for(let i = 0; i < 4; i++){
    let thread = new MainThread("./my-worker.js");
    thread.on("error", console.error);

    threads.push(thread);
  }

  //wait for all threads to finish adding
  await Promise.all(threads.map(t => t.waitFor("done")));

  console.log(myInt32.value);//outputs: 400

  //terminate all threads
  threads.forEach(t => t.terminate());
}
startWorker().catch(console.error);

my-worker.js

import { WorkerThread } from 'sharedthread';

let myMutex = WorkerThread.getVar("myMutex");
let myInt32 = WorkerThread.getVar("myInt32");

async function runWorker(){
  for(let i = 0; i < 100; i++){
    //enter critical section using mutex
    await myMutex.use(() => {
        myInt32.value += 1;
    });
    //alternative syntax
    /*
    await myMutex.lock();
    myInt32.value += 1;
    myMutex.unlock();
    */
  }

  WorkerThread.signal("done");
}
runWorker();