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

threadts-universal

v1.0.0

Published

Universal TypeScript library for effortless parallel computing across all JavaScript ecosystems

Readme

🚀 ThreadTS Universal

npm version License: MIT TypeScript 🚀 CI/CD Pipeline CodeQL

The definitive universal TypeScript library that makes true parallelism as effortless as async/await across all JavaScript ecosystems.

import threadts from 'threadts-universal';

// Transform any function into parallel execution with one line
const result = await threadts.run((x) => x * 2, 42);
console.log(result); // 84

// Full Array API support with parallel execution
const found = await threadts.find([1, 2, 3, 4, 5], (x) => x > 3);
console.log(found); // 4

✨ Features

🎯 One-Command Paradigm

  • Single-line API: await threadts.run(fn, data) → instant parallel execution
  • Zero-Config: Intelligent defaults with infinite customization layers
  • Quantum Performance: Sub-5ms overhead vs handwritten worker wrappers

🌍 Universal Compatibility

  • Browser: Web Workers, OffscreenCanvas, Transferable Objects
  • Node.js: Worker Threads, Cluster Mode, Native Addons
  • Deno: Web Workers with permissions sandboxing
  • Bun: Optimized worker implementation
  • Identical API: Same code works everywhere

Advanced Features

  • Auto-scaling Pools: From 1 to ∞ workers based on load
  • Full Array API: map, filter, reduce, reduceRight, find, findIndex, some, every, forEach, flatMap, groupBy, partition, count, indexOf, lastIndexOf, at, slice, concat, range, repeat, unique, uniqueBy, chunk, zip
  • ES2023+ Immutable Methods: findLast, findLastIndex, toSorted, toReversed, withElement, toSpliced, groupByObject - all methods that don't mutate the original array
  • Enhanced Pipeline API: Fluent chaining with lazy evaluation
    • Intermediate: map, filter, flatMap, take, skip, chunk, tap, peek, window, unique, distinct, reverse, sort, zip, zipWith, interleave, compact, flatten, shuffle, sample, dropWhile, takeWhile, slicePipe, concatPipe, rotate, truthy, falsy
    • Terminal: reduce, forEach, find, findIndex, findLast, findLastIndex, some, every, count, groupBy, partition, first, last, isEmpty, sum, average, min, max, join, includes
    • Collectors: toArray(), toSet(), toMap()
  • Progress Tracking: Real-time progress monitoring
  • Intelligent Caching: Automatic result caching with @memoize and @cache decorators
  • Priority Queues: High/Normal/Low priority execution
  • Timeout & Cancellation: AbortController integration
  • Decorator Suite: @parallelMethod(), @retry(), @rateLimit(), @timeout(), @debounce(), @throttle(), @logged(), @cache(), @concurrent(), @circuitBreaker(), @measure(), @validate(), @lazy()
  • Custom Decorators: Utilities for creating your own decorators (createMethodDecorator, createMethodDecoratorWithClass, createClassDecorator)
  • Monitoring: Built-in performance monitoring, health checks, and error handling

🚀 Quick Start

Installation

npm install threadts-universal

Basic Usage

import threadts from 'threadts-universal';

// Simple parallel execution
const doubled = await threadts.run((x) => x * 2, 21);

// Complex calculations
const fibonacci = await threadts.run((n) => {
  if (n <= 1) return n;
  let a = 0,
    b = 1;
  for (let i = 2; i <= n; i++) {
    [a, b] = [b, a + b];
  }
  return b;
}, 40);

// Parallel array processing
const squares = await threadts.map([1, 2, 3, 4, 5], (x) => x * x);
// Result: [1, 4, 9, 16, 25]

// Find elements in parallel
const firstMatch = await threadts.find([1, 2, 3, 4, 5], (x) => x > 3);
// Result: 4

// Check conditions across array
const hasEven = await threadts.some([1, 3, 5, 6], (x) => x % 2 === 0);
// Result: true

// Group and partition data
const [evens, odds] = await threadts.partition(
  [1, 2, 3, 4, 5],
  (x) => x % 2 === 0
);
// evens: [2, 4], odds: [1, 3, 5]

// Pipeline API for chained operations
const result = await threadts
  .pipe([1, 2, 3, 4, 5])
  .map((x) => x * 2)
  .filter((x) => x > 4)
  .reduce((acc, x) => acc + x, 0)
  .execute();
// Result: 24

Method Decorators

import {
  parallelMethod,
  memoize,
  retry,
  rateLimit,
  timeout,
  debounce,
} from 'threadts-universal';

class DataProcessor {
  @parallelMethod()
  async processLargeDataset(data: number[]): Promise<number[]> {
    return data.map((x) => x * x * x);
  }

  @parallelMethod({ timeout: 5000, priority: 'high' })
  async criticalCalculation(input: ComplexData): Promise<Result> {
    // Heavy computation automatically runs in worker
    return heavyProcessing(input);
  }

  @memoize(100) // Cache up to 100 results
  async expensiveComputation(input: string): Promise<Result> {
    return computeResult(input);
  }

  @retry(3, 1000) // 3 attempts with exponential backoff
  async unreliableOperation(): Promise<void> {
    await callExternalService();
  }

  @rateLimit(10) // Max 10 calls per second
  async rateLimitedAPI(): Promise<Data> {
    return fetchFromAPI();
  }
}

📚 API Reference

Core Methods

threadts.run<T>(fn, data?, options?): Promise<T>

Executes a function in a worker thread.

const result = await threadts.run(
  (data: { x: number; y: number }) => data.x + data.y,
  { x: 10, y: 20 },
  {
    timeout: 5000,
    priority: 'high',
    transferable: [], // For transferable objects
  }
);

threadts.parallel<T>(tasks): Promise<T[]>

Executes multiple functions in parallel.

const results = await threadts.parallel([
  { fn: (x) => x * 2, data: 5 },
  { fn: (x) => x * 3, data: 7 },
  { fn: (x) => x * 4, data: 9 },
]);
// Results: [10, 21, 36]

threadts.map<T, R>(array, fn, options?): Promise<R[]>

Maps an array through a function in parallel.

const results = await threadts.map(
  [1, 2, 3, 4, 5],
  (x, index) => ({ value: x * x, index }),
  { batchSize: 2 }
);

threadts.filter<T>(array, fn, options?): Promise<T[]>

Filters an array in parallel.

const evens = await threadts.filter(
  [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
  (x) => x % 2 === 0
);

threadts.reduce<T, R>(array, fn, initialValue, options?): Promise<R>

Reduces an array in parallel (for associative operations).

const sum = await threadts.reduce(
  [1, 2, 3, 4, 5],
  (acc, curr) => acc + curr,
  0
);

threadts.reduceRight<T, R>(array, fn, initialValue, options?): Promise<R>

Reduces an array from right to left.

const result = await threadts.reduceRight(
  ['a', 'b', 'c'],
  (acc, item) => acc + item,
  ''
);
// Result: 'cba'

threadts.find<T>(array, predicate, options?): Promise<T | undefined>

Finds the first element that satisfies the predicate (processes in parallel batches).

const found = await threadts.find([1, 2, 3, 4, 5], (x) => x > 3);
// Result: 4

threadts.findIndex<T>(array, predicate, options?): Promise<number>

Finds the index of the first element that satisfies the predicate.

const index = await threadts.findIndex([1, 2, 3, 4, 5], (x) => x > 3);
// Result: 3

threadts.some<T>(array, predicate, options?): Promise<boolean>

Tests whether at least one element satisfies the predicate.

const hasEven = await threadts.some([1, 3, 5, 6, 7], (x) => x % 2 === 0);
// Result: true

threadts.every<T>(array, predicate, options?): Promise<boolean>

Tests whether all elements satisfy the predicate.

const allPositive = await threadts.every([1, 2, 3, 4, 5], (x) => x > 0);
// Result: true

threadts.forEach<T>(array, fn, options?): Promise<void>

Iterates over an array in parallel (like Array.forEach but parallel).

await threadts.forEach([1, 2, 3], (x) => {
  console.log(x);
});

threadts.flatMap<T, R>(array, fn, options?): Promise<R[]>

Maps each element to an array and flattens the result.

const result = await threadts.flatMap([1, 2, 3], (x) => [x, x * 2]);
// Result: [1, 2, 2, 4, 3, 6]

threadts.groupBy<T, K>(array, keyFn, options?): Promise<Map<K, T[]>>

Groups array elements by a key returned from the function.

const grouped = await threadts.groupBy(
  [
    { type: 'a', value: 1 },
    { type: 'b', value: 2 },
    { type: 'a', value: 3 },
  ],
  (item) => item.type
);
// Map { 'a' => [{type: 'a', value: 1}, {type: 'a', value: 3}], 'b' => [{type: 'b', value: 2}] }

threadts.partition<T>(array, predicate, options?): Promise<[T[], T[]]>

Partitions an array into two arrays based on a predicate.

const [evens, odds] = await threadts.partition(
  [1, 2, 3, 4, 5],
  (x) => x % 2 === 0
);
// evens: [2, 4], odds: [1, 3, 5]

threadts.count<T>(array, predicate, options?): Promise<number>

Counts elements that satisfy a predicate. More efficient than filter().length.

const count = await threadts.count([1, 2, 3, 4, 5], (x) => x > 2);
// Result: 3

Extended Array Operations

ThreadTS Universal provides a comprehensive set of array operations beyond the standard JavaScript Array API. These operations are optimized for parallel execution and can be accessed both as instance methods and through the getArrayOps() factory.

threadts.indexOf<T>(array, searchElement, fromIndex?, options?): Promise<number>

Finds the first index of an element in the array.

const index = await threadts.indexOf([1, 2, 3, 2, 1], 2);
// Result: 1

const indexFrom = await threadts.indexOf([1, 2, 3, 2, 1], 2, 2);
// Result: 3

threadts.lastIndexOf<T>(array, searchElement, fromIndex?, options?): Promise<number>

Finds the last index of an element in the array.

const index = await threadts.lastIndexOf([1, 2, 3, 2, 1], 2);
// Result: 3

threadts.at<T>(array, index, options?): Promise<T | undefined>

Returns the element at the specified index, supporting negative indices.

const last = await threadts.at([1, 2, 3, 4, 5], -1);
// Result: 5

const second = await threadts.at([1, 2, 3, 4, 5], 1);
// Result: 2

threadts.slice<T>(array, start?, end?, options?): Promise<T[]>

Extracts a section of the array.

const middle = await threadts.slice([1, 2, 3, 4, 5], 1, 4);
// Result: [2, 3, 4]

const fromEnd = await threadts.slice([1, 2, 3, 4, 5], -2);
// Result: [4, 5]

threadts.concat<T>(array, ...items): Promise<T[]>

Concatenates arrays or values.

const combined = await threadts.concat([1, 2], [3, 4], 5);
// Result: [1, 2, 3, 4, 5]

threadts.range(start, end, step?, options?): Promise<number[]>

Generates an array of numbers in a specified range.

const numbers = await threadts.range(0, 10, 2);
// Result: [0, 2, 4, 6, 8]

const countdown = await threadts.range(5, 0, -1);
// Result: [5, 4, 3, 2, 1]

threadts.repeat<T>(value, count, options?): Promise<T[]>

Creates an array with the value repeated n times.

const repeated = await threadts.repeat('x', 5);
// Result: ['x', 'x', 'x', 'x', 'x']

threadts.unique<T>(array, options?): Promise<T[]>

Returns an array with duplicate values removed.

const unique = await threadts.unique([1, 2, 2, 3, 3, 3]);
// Result: [1, 2, 3]

threadts.uniqueBy<T, K>(array, keyFn, options?): Promise<T[]>

Returns unique elements based on a key function.

const users = [
  { id: 1, name: 'Alice' },
  { id: 2, name: 'Bob' },
  { id: 1, name: 'Alice Clone' },
];
const uniqueById = await threadts.uniqueBy(users, (u) => u.id);
// Result: [{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }]

threadts.chunk<T>(array, size, options?): Promise<T[][]>

Splits an array into chunks of the specified size.

const chunks = await threadts.chunk([1, 2, 3, 4, 5], 2);
// Result: [[1, 2], [3, 4], [5]]

threadts.zip<T, U>(array1, array2, options?): Promise<[T, U][]>

Combines two arrays element-wise into pairs.

const zipped = await threadts.zip(['a', 'b', 'c'], [1, 2, 3]);
// Result: [['a', 1], ['b', 2], ['c', 3]]

ES2023+ Immutable Array Methods

ThreadTS Universal provides full support for ES2023+ immutable array methods. These methods return new arrays instead of mutating the original, making them ideal for functional programming patterns.

threadts.findLast<T>(array, predicate, options?): Promise<T | undefined>

Finds the last element that satisfies the predicate (searches from end).

const result = await threadts.findLast([1, 2, 3, 4, 5], (x) => x < 4);
// Result: 3 (the last number less than 4)

const noMatch = await threadts.findLast([1, 2, 3], (x) => x > 10);
// Result: undefined

threadts.findLastIndex<T>(array, predicate, options?): Promise<number>

Finds the index of the last element that satisfies the predicate.

const index = await threadts.findLastIndex([1, 2, 3, 2, 1], (x) => x === 2);
// Result: 3 (the last occurrence of 2)

const notFound = await threadts.findLastIndex([1, 2, 3], (x) => x > 10);
// Result: -1

threadts.toSorted<T>(array, compareFn?, options?): Promise<T[]>

Returns a new sorted array without mutating the original.

const original = [3, 1, 4, 1, 5];
const sorted = await threadts.toSorted(original);
// sorted: [1, 1, 3, 4, 5]
// original: [3, 1, 4, 1, 5] (unchanged!)

// With custom comparator (descending)
const descending = await threadts.toSorted([3, 1, 4], (a, b) => b - a);
// Result: [4, 3, 1]

threadts.toReversed<T>(array, options?): Promise<T[]>

Returns a new reversed array without mutating the original.

const original = [1, 2, 3];
const reversed = await threadts.toReversed(original);
// reversed: [3, 2, 1]
// original: [1, 2, 3] (unchanged!)

threadts.withElement<T>(array, index, value, options?): Promise<T[]>

Returns a new array with the element at the given index replaced.

const original = [1, 2, 3];
const updated = await threadts.withElement(original, 1, 10);
// updated: [1, 10, 3]
// original: [1, 2, 3] (unchanged!)

// Supports negative indices
const lastReplaced = await threadts.withElement([1, 2, 3], -1, 99);
// Result: [1, 2, 99]

threadts.toSpliced<T>(array, start, deleteCount?, ...items): Promise<T[]>

Returns a new array with elements removed, replaced, or added at a given index.

const original = [1, 2, 3, 4];

// Remove and insert
const spliced = await threadts.toSpliced(original, 1, 2, 10, 20);
// spliced: [1, 10, 20, 4]
// original: [1, 2, 3, 4] (unchanged!)

// Delete only
const deleted = await threadts.toSpliced([1, 2, 3, 4], 1, 2);
// Result: [1, 4]

// Insert only
const inserted = await threadts.toSpliced([1, 2, 3], 1, 0, 99);
// Result: [1, 99, 2, 3]

threadts.groupByObject<T, K>(array, keyFn, options?): Promise<Partial<Record<K, T[]>>>

Groups elements into a plain object based on a key function (similar to Object.groupBy).

const items = [
  { type: 'fruit', name: 'apple' },
  { type: 'vegetable', name: 'carrot' },
  { type: 'fruit', name: 'banana' },
];
const grouped = await threadts.groupByObject(items, (item) => item.type);
// Result: {
//   fruit: [{ type: 'fruit', name: 'apple' }, { type: 'fruit', name: 'banana' }],
//   vegetable: [{ type: 'vegetable', name: 'carrot' }]
// }

Using the Array Operations Factory

For advanced use cases, you can access all array operations through the factory:

// Get array operations bound to the current ThreadTS instance
const arrayOps = threadts.getArrayOps();

// Use operations directly
const index = await arrayOps.indexOf([1, 2, 3], 2);
const unique = await arrayOps.unique([1, 1, 2, 2, 3]);
const chunks = await arrayOps.chunk([1, 2, 3, 4, 5, 6], 2);

// ES2023+ operations
const sorted = await arrayOps.toSorted([3, 1, 2]);
const reversed = await arrayOps.toReversed([1, 2, 3]);
const lastMatch = await arrayOps.findLast([1, 2, 3, 2], (x) => x === 2);

// Available operations:
// indexOf, lastIndexOf, at, slice, concat, fill, includes, join,
// reverse, sort, range, repeat, zip, unzip, unique, uniqueBy, flat, chunk,
// findLast, findLastIndex, toSorted, toReversed, withElement, toSpliced, groupByObject

threadts.batch(tasks, batchSize?): Promise<TaskResult[]>

Executes multiple tasks as a batch with configurable batch size.

const results = await threadts.batch(
  [
    { fn: (x) => x * 2, data: 5 },
    { fn: (x) => x + 1, data: 10 },
  ],
  2 // Process 2 tasks at a time
);

Pipeline API

The Pipeline API provides a fluent interface for chaining parallel operations with lazy evaluation.

// Chain multiple operations
const result = await threadts
  .pipe([1, 2, 3, 4, 5])
  .map((x) => x * 2)
  .filter((x) => x > 4)
  .reduce((acc, x) => acc + x, 0)
  .execute();
// Result: 24 (6 + 8 + 10)

// Using flatMap in pipeline
const flattened = await threadts
  .pipe([1, 2, 3])
  .flatMap((x) => [x, x * 10])
  .filter((x) => x > 5)
  .toArray();
// Result: [10, 20, 30]

// Terminal operations: reduce, forEach, find, some, every, count
const hasLarge = await threadts
  .pipe([1, 2, 3, 4, 5])
  .map((x) => x * 2)
  .some((x) => x > 8)
  .execute();
// Result: true

// Using take, skip, and chunk
const paginated = await threadts
  .pipe([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
  .skip(2)
  .take(5)
  .execute();
// Result: [3, 4, 5, 6, 7]

const chunks = await threadts.pipe([1, 2, 3, 4, 5]).chunk(2).execute();
// Result: [[1, 2], [3, 4], [5]]

// Using tap for debugging/side-effects
const result = await threadts
  .pipe([1, 2, 3])
  .tap((x) => console.log('Processing:', x))
  .map((x) => x * 2)
  .execute();
// Logs: Processing: 1, Processing: 2, Processing: 3
// Result: [2, 4, 6]

// Using window for sliding windows
const windows = await threadts.pipe([1, 2, 3, 4, 5]).window(3).execute();
// Result: [[1, 2, 3], [2, 3, 4], [3, 4, 5]]

const windowsWithStep = await threadts
  .pipe([1, 2, 3, 4, 5, 6])
  .window(2, 2) // size=2, step=2
  .execute();
// Result: [[1, 2], [3, 4], [5, 6]]

// Using unique, reverse, and sort
const processed = await threadts
  .pipe([3, 1, 2, 1, 3])
  .unique()
  .sort((a, b) => a - b)
  .execute();
// Result: [1, 2, 3]

// Aggregation operations
const sum = await threadts.pipe([1, 2, 3, 4, 5]).sum().execute();
// Result: 15

const avg = await threadts.pipe([1, 2, 3, 4, 5]).average().execute();
// Result: 3

const max = await threadts.pipe([3, 1, 4, 1, 5]).max().execute();
// Result: 5

// First and last elements
const first = await threadts
  .pipe([1, 2, 3])
  .filter((x) => x > 1)
  .first()
  .execute();
// Result: 2

// Collect to different data structures
const set = await threadts.pipe([1, 2, 2, 3]).toSet();
// Result: Set { 1, 2, 3 }

const map = await threadts.pipe(users).toMap((user) => user.id);
// Result: Map { id1 => user1, id2 => user2, ... }

// GroupBy and Partition in pipeline
const grouped = await threadts
  .pipe(users)
  .groupBy((user) => user.role)
  .execute();
// Result: Map { 'admin' => [...], 'user' => [...] }

const [admins, regular] = await threadts
  .pipe(users)
  .partition((user) => user.role === 'admin')
  .execute();

New Pipeline Operations (v2.1.0+)

// Distinct - remove duplicates by key
const items = [{ id: 1 }, { id: 2 }, { id: 1 }, { id: 3 }];
const uniqueById = await threadts
  .pipe(items)
  .distinct((item) => item.id)
  .execute();
// Result: [{ id: 1 }, { id: 2 }, { id: 3 }]

// Zip - combine two arrays element-wise
const names = ['Alice', 'Bob', 'Charlie'];
const ages = [25, 30, 35];
const zipped = await threadts.pipe(names).zip(ages).execute();
// Result: [['Alice', 25], ['Bob', 30], ['Charlie', 35]]

// ZipWith - combine with a custom function
const combined = await threadts
  .pipe(names)
  .zipWith(ages, (name, age) => `${name} is ${age}`)
  .execute();
// Result: ['Alice is 25', 'Bob is 30', 'Charlie is 35']

// Interleave - alternate elements from two arrays
const letters = ['a', 'b', 'c'];
const numbers = [1, 2, 3];
const interleaved = await threadts.pipe(letters).interleave(numbers).execute();
// Result: ['a', 1, 'b', 2, 'c', 3]

// Compact - remove null and undefined values
const sparse = [1, null, 2, undefined, 3];
const compacted = await threadts.pipe(sparse).compact().execute();
// Result: [1, 2, 3]

// Flatten - flatten nested arrays
const nested = [[1, 2], [3, 4], [5]];
const flattened = await threadts.pipe(nested).flatten().execute();
// Result: [1, 2, 3, 4, 5]

const deepNested = [[[1]], [[2]], [[3]]];
const deepFlattened = await threadts.pipe(deepNested).flatten(2).execute();
// Result: [1, 2, 3]

// Shuffle - randomly shuffle elements
const ordered = [1, 2, 3, 4, 5];
const shuffled = await threadts.pipe(ordered).shuffle().execute();
// Result: [3, 1, 5, 2, 4] (random order)

// Sample - get random sample of n elements
const sampled = await threadts.pipe(ordered).sample(3).execute();
// Result: [2, 4, 1] (3 random elements)

// DropWhile - drop elements while predicate is true
const sequence = [1, 2, 3, 10, 4, 5];
const dropped = await threadts
  .pipe(sequence)
  .dropWhile((x) => x < 5)
  .execute();
// Result: [10, 4, 5]

// TakeWhile - take elements while predicate is true
const taken = await threadts
  .pipe(sequence)
  .takeWhile((x) => x < 5)
  .execute();
// Result: [1, 2, 3]

// Peek - debug helper (alias for tap)
const peeked = await threadts
  .pipe([1, 2, 3])
  .peek((x) => console.log('Value:', x))
  .map((x) => x * 2)
  .execute();
// Logs: Value: 1, Value: 2, Value: 3
// Result: [2, 4, 6]

// Join - join elements into string (terminal)
const sentence = await threadts.pipe(['Hello', 'World']).join(' ').execute();
// Result: 'Hello World'

// Includes - check if element exists (terminal)
const hasThree = await threadts.pipe([1, 2, 3, 4]).includes(3).execute();
// Result: true

// SlicePipe - extract a portion of the array
const sliced = await threadts.pipe([1, 2, 3, 4, 5]).slicePipe(1, 4).execute();
// Result: [2, 3, 4]

// ConcatPipe - concatenate with another array
const concatenated = await threadts
  .pipe([1, 2])
  .concatPipe([3, 4, 5])
  .execute();
// Result: [1, 2, 3, 4, 5]

// Rotate - rotate array elements by n positions
const rotatedRight = await threadts.pipe([1, 2, 3, 4, 5]).rotate(2).execute();
// Result: [4, 5, 1, 2, 3]

const rotatedLeft = await threadts.pipe([1, 2, 3, 4, 5]).rotate(-2).execute();
// Result: [3, 4, 5, 1, 2]

// Truthy - filter to only truthy values
const truthyOnly = await threadts
  .pipe([0, 1, '', 'hello', null, true, false])
  .truthy()
  .execute();
// Result: [1, 'hello', true]

// Falsy - filter to only falsy values
const falsyOnly = await threadts
  .pipe([0, 1, '', 'hello', null, true, false])
  .falsy()
  .execute();
// Result: [0, '', null, false]

Advanced Features

Progress Tracking

const result = await threadts.execute((data) => {
  // Emit progress updates
  for (let i = 0; i < 100; i++) {
    postMessage({ progress: i, message: `Processing ${i}%` });
    // Heavy work here
  }
  return finalResult;
}, inputData);

Transferable Objects

const buffer = new ArrayBuffer(1024 * 1024);
const result = await threadts.run(
  (buffer) => {
    // Process buffer in worker
    const view = new Uint8Array(buffer);
    // ... processing
    return processedData;
  },
  buffer,
  { transferable: [buffer] }
);

Pool Management

// Resize worker pool
await threadts.resize(8);

// Get pool statistics
const stats = threadts.getStats();
console.log(stats);
// {
//   activeWorkers: 2,
//   idleWorkers: 6,
//   queuedTasks: 0,
//   completedTasks: 150,
//   averageExecutionTime: 45.2
// }

// Get/Update configuration
const config = threadts.getConfig();
threadts.updateConfig({ timeout: 10000, debug: true });

// Check platform and support
console.log(threadts.getPlatform()); // 'node', 'browser', 'deno', 'bun'
console.log(threadts.isSupported()); // true if workers are supported

// Cleanup
await threadts.terminate(); // Terminate instance
await ThreadTS.terminateAll(); // Terminate all instances

Decorators

@parallelMethod(options?)

Automatically parallelizes method execution.

import { parallelMethod } from 'threadts-universal';

class ImageProcessor {
  @parallelMethod({ cacheResults: true, timeout: 5000 })
  async applyFilter(imageData: ImageData, filter: Filter): Promise<ImageData> {
    // Automatically runs in worker thread
    return processImage(imageData, filter);
  }
}

Options:

  • timeout: Maximum execution time in milliseconds
  • priority: Execution priority ('low', 'normal', 'high')
  • maxRetries: Number of retry attempts on failure
  • poolSize: Custom worker pool size
  • cacheResults: Enable result caching

@memoize(maxCacheSize?)

Caches method results with LRU eviction policy.

@memoize(100) // Cache up to 100 unique results
async expensiveComputation(input: string): Promise<Result> {
  return computeResult(input);
}

@retry(maxAttempts?, baseDelay?)

Retry logic with exponential backoff.

@retry(3, 1000) // 3 attempts, starting with 1s delay
async unreliableOperation(): Promise<void> {
  await callExternalService();
}

@rateLimit(callsPerSecond?)

Limits method call frequency.

@rateLimit(10) // Max 10 calls per second
async apiCall(): Promise<Data> {
  return fetchFromAPI();
}

@timeout(ms?)

Automatically rejects if execution exceeds the specified timeout.

@timeout(5000) // Reject after 5 seconds
async longRunningTask(): Promise<Result> {
  return performTask();
}

@debounce(ms?)

Delays execution until no calls have been made for the specified duration.

@debounce(300) // Wait 300ms after last call
async handleInput(value: string): Promise<void> {
  await saveToServer(value);
}

@throttle(ms?)

Ensures the method is called at most once per specified interval.

@throttle(1000) // At most once per second
async trackEvent(event: Event): Promise<void> {
  await sendAnalytics(event);
}

@logged(options?)

Logs method calls, arguments, results, and execution time.

@logged({ logArgs: true, logResult: true })
async processData(data: Data): Promise<Result> {
  return transform(data);
}
// Logs: [ClassName.processData] Starting with args: [...]
// Logs: [ClassName.processData] Completed in 45ms. Result: {...}

@parallelBatch(batchSize?)

Processes array data in parallel batches.

@parallelBatch(4)
async processBatch(items: Item[]): Promise<Result[]> {
  return items.map(processItem);
}

@parallelMap()

Parallel map operation on array input.

@parallelMap()
async processItems(items: Item[]): Promise<Result[]> {
  return items.map(transform);
}

Decorator Summary

// Parallelization
@parallelMethod() // Parallelize method execution
@parallelBatch(4) // Process arrays in batches of 4
@parallelMap() // Parallel map over array

// Caching
@memoize(100) // LRU cache with 100 entries
@cache(60000, 50) // TTL cache: 60s expiry, max 50 entries

// Reliability
@retry(3, 1000) // Retry up to 3 times with exponential backoff
@timeout(5000) // Timeout after 5 seconds
@circuitBreaker({ failureThreshold: 5, resetTimeout: 30000 }) // Circuit breaker pattern

// Rate Control
@rateLimit(10) // Max 10 calls per second
@debounce(300) // Debounce with 300ms delay
@throttle(1000) // Throttle to once per second
@concurrent(3) // Limit to 3 concurrent executions

// Observability
@logged() // Log execution details
@measure() // Collect timing statistics

// Validation & Initialization
@validate([...validators]) // Validate arguments
@lazy() // Lazy initialization (execute only once)

@cache(ttlMs?, maxSize?)

Caches method results with TTL (Time To Live) support.

@cache(30000, 50) // Cache for 30 seconds, max 50 entries
async fetchData(id: string): Promise<Data> {
  return await api.getData(id);
}
// Clear cache: this.fetchData.clearCache()

@concurrent(maxConcurrent?)

Limits concurrent executions of a method.

@concurrent(3) // Max 3 concurrent executions
async processFile(path: string): Promise<void> {
  await heavyFileOperation(path);
}

@circuitBreaker(options?)

Implements the circuit breaker pattern for fault tolerance.

@circuitBreaker({
  failureThreshold: 5,  // Open circuit after 5 failures
  resetTimeout: 30000,  // Try again after 30 seconds
  halfOpenRequests: 1   // Allow 1 test request in half-open state
})
async callExternalApi(): Promise<Data> {
  return await externalApi.getData();
}
// Check state: this.callExternalApi.getState()
// Reset: this.callExternalApi.reset()

@measure()

Collects timing statistics across multiple calls.

@measure()
async compute(data: number[]): Promise<number> {
  return data.reduce((a, b) => a + b, 0);
}
// Get stats: this.compute.getStats()
// Returns: { count, min, max, avg, median, p95, p99 }

@validate(validators)

Validates method arguments before execution.

@validate([
  (id) => typeof id === 'string' && id.length > 0,
  (data) => data && typeof data.name === 'string'
])
async updateUser(id: string, data: UserData): Promise<void> {
  await api.updateUser(id, data);
}

@lazy()

Lazy initialization - method executes only once.

@lazy()
async loadConfig(): Promise<Config> {
  return await fetchConfig(); // Only called once, result cached forever
}
// Reset: this.loadConfig.reset()

Creating Custom Decorators

ThreadTS provides utility functions for creating your own decorators that work with both legacy (experimentalDecorators) and Stage-3 decorator syntax:

import {
  createMethodDecorator,
  createMethodDecoratorWithClass,
  createClassDecorator,
  isAsyncFunction,
  ensureAsync,
} from 'threadts-universal';

// Simple method decorator
function logExecution() {
  return createMethodDecorator((originalMethod, methodName) => {
    return async function (...args: unknown[]) {
      console.log(`Calling ${methodName}`);
      const result = await originalMethod.apply(this, args);
      console.log(`${methodName} returned:`, result);
      return result;
    } as typeof originalMethod;
  });
}

// Decorator with class name for observability
function auditLog() {
  return createMethodDecoratorWithClass((method, methodName, className) => {
    return async function (...args: unknown[]) {
      console.log(`[${className}.${methodName}] called with:`, args);
      return method.apply(this, args);
    } as typeof method;
  });
}

// Class decorator
function singleton() {
  const instances = new Map();
  return createClassDecorator((OriginalClass, className) => {
    return class extends OriginalClass {
      constructor(...args: unknown[]) {
        if (instances.has(className)) {
          return instances.get(className);
        }
        super(...args);
        instances.set(className, this);
      }
    } as typeof OriginalClass;
  });
}

// Usage
class MyService {
  @logExecution()
  async process(data: string): Promise<string> {
    return data.toUpperCase();
  }

  @auditLog()
  async save(item: Item): Promise<void> {
    // Logs: [MyService.save] called with: [...]
    await db.save(item);
  }
}

Event System

// Listen to task completion events
threadts.on('task-complete', ({ taskId, result, duration }) => {
  console.log(`Task ${taskId} completed in ${duration}ms`);
});

// Listen to task errors
threadts.on('task-error', ({ taskId, error, duration }) => {
  console.error(`Task ${taskId} failed: ${error}`);
});

// Listen to pool resize events
threadts.on('pool-resize', ({ oldSize, newSize }) => {
  console.log(`Pool resized from ${oldSize} to ${newSize}`);
});

// Remove event listener
threadts.off('task-complete', listener);

Monitoring & Diagnostics

import {
  PerformanceMonitor,
  HealthMonitor,
  ErrorHandler,
} from 'threadts-universal';

// Performance Monitoring
const perfMonitor = new PerformanceMonitor();
perfMonitor.startMonitoring(2000); // Check every 2 seconds
const metrics = perfMonitor.collectMetrics();

// Health Monitoring
const healthMonitor = new HealthMonitor();
healthMonitor.startHealthMonitoring(5000); // Check every 5 seconds
const health = await healthMonitor.performHealthCheck();
console.log(health.overall); // 'healthy', 'degraded', or 'unhealthy'

// Error Handling with auto-recovery
const errorHandler = new ErrorHandler();
const result = await errorHandler.executeWithRetry(
  'operation-name',
  async () => riskyOperation(),
  { platform: 'node', workerCount: 4 }
);

🌟 Platform-Specific Features

Browser

  • OffscreenCanvas: GPU-intensive graphics processing
  • Web Locks: Worker coordination and synchronization
  • Transferable Objects: Zero-copy operations
  • Safari Polyfills: Automatic fallback detection

Node.js

  • Worker Threads: CPU-intensive task processing
  • Resource Limits: Memory control per worker
  • Cluster Mode: Multi-core utilization
  • Native Addons: Performance optimization hooks
// Node.js specific resource limits
const result = await threadts.run(heavyTask, data, {
  resourceLimits: {
    maxOldGenerationSizeMb: 128,
    maxYoungGenerationSizeMb: 64,
    codeRangeSizeMb: 16,
    stackSizeMb: 4,
  },
  workerName: 'heavy-worker',
  trackResources: true,
});

Deno

  • Permission Sandboxing: Secure worker execution
  • TypeScript Native: Zero-config TypeScript support
  • Web Standards: Modern web API compatibility
// Deno specific permissions
const result = await threadts.run(task, data, {
  denoPermissions: {
    net: ['api.example.com'],
    read: ['/data'],
    env: true,
    hrtime: true,
  },
});

Bun

  • Ultra-fast Startup: Optimized worker creation
  • Native Performance: Maximum speed execution
  • High Precision Timing: Microsecond accuracy
// Bun specific options
const result = await threadts.run(task, data, {
  bunOptions: {
    name: 'compute-worker',
    highPrecisionTiming: true,
    forceGC: true,
  },
});

📊 Performance

ThreadTS Universal provides quantum-level performance with sub-5ms overhead:

// Benchmark: Fibonacci(40) calculation
const iterations = 1000;

// Sequential execution: ~2.3s
// ThreadTS parallel: ~0.6s (4 cores)
// Overhead: < 5ms per operation

Performance Benchmarks

| Operation | Data Size | Time | Throughput | | --------------------- | ----------- | ------ | --------------- | | Image Processing | 1920x1080 | ~50ms | ~40k pixels/ms | | JSON Transformation | 10k records | ~30ms | ~300 records/ms | | Cryptographic Hashing | 1k items | ~15ms | ~65 hashes/ms | | Array Map | 100k items | ~200ms | ~500k items/s |

🔧 Configuration

Pool Configuration

import { ThreadTS } from 'threadts-universal';

const threadts = ThreadTS.getInstance({
  poolSize: 8, // Number of workers
  timeout: 30000, // Default timeout in ms
  retries: 2, // Default retry attempts
  autoResize: true, // Auto-scale pool
  debug: false, // Enable debug logging
  maxQueueSize: 1000, // Max queued tasks
  workerIdleTimeout: 60000, // Worker idle timeout
  taskPriority: 'normal', // Default priority
});

Platform Detection

import {
  detectPlatform,
  supportsWorkerThreads,
  getOptimalThreadCount,
  getMemoryInfo,
} from 'threadts-universal';

console.log('Platform:', detectPlatform()); // 'node', 'browser', 'deno', 'bun'
console.log('Worker Support:', supportsWorkerThreads());
console.log('Optimal Threads:', getOptimalThreadCount());
console.log('Memory:', getMemoryInfo());

🧪 Testing

# Run all tests
npm test

# Browser tests
npm run test:browser

# Node.js tests
npm run test:node

# Performance benchmarks
npm run test:performance

🏗️ Project Architecture

ThreadTS Universal follows a modular, OOP-first architecture designed for maintainability and extensibility.

Directory Structure

src/
├── index.ts              # Main entry point (auto-detects platform)
├── browser.ts            # Browser-specific entry
├── node.ts               # Node.js-specific entry
├── types.ts              # TypeScript type definitions
│
├── core/
│   ├── threadts.ts       # Main ThreadTS class (singleton pattern)
│   ├── pipeline.ts       # Pipeline & TerminalPipeline classes
│   ├── pipeline-operations.ts  # Pipeline operation handlers
│   └── array-operations.ts     # Extended array operations factory
│
├── adapters/
│   ├── base.ts           # Abstract WorkerAdapter base class
│   ├── browser.ts        # Web Worker adapter
│   ├── node.ts           # Worker Threads adapter
│   ├── deno.ts           # Deno Worker adapter
│   └── bun.ts            # Bun Worker adapter
│
├── decorators/
│   ├── index.ts          # Re-exports all decorators
│   ├── parallel.ts       # @parallelMethod, @parallelClass, @parallelBatch, @parallelMap
│   ├── caching.ts        # @memoize, @cache, @lazy
│   ├── flow-control.ts   # @retry, @timeout, @rateLimit, @debounce, @throttle, @concurrent, @circuitBreaker
│   └── observability.ts  # @logged, @measure, @validate
│
├── monitoring/
│   ├── error-handler.ts  # Centralized error handling
│   ├── health.ts         # Health checks and diagnostics
│   └── performance.ts    # Performance monitoring
│
├── pool/
│   └── manager.ts        # Worker pool management
│
└── utils/
    ├── platform.ts       # Platform detection utilities
    ├── serialization.ts  # Data serialization helpers
    └── validation.ts     # Input validation utilities

Decorator Modules

Decorators are organized by category for better maintainability:

| Module | Decorators | Purpose | | ------------------ | ---------------------------------------------------------------------------------------------- | ----------------------------- | | parallel.ts | @parallelMethod, @parallelClass, @parallel, @parallelBatch, @parallelMap | Parallel execution | | caching.ts | @memoize, @cache, @lazy | Caching & memoization | | flow-control.ts | @retry, @timeout, @rateLimit, @debounce, @throttle, @concurrent, @circuitBreaker | Flow control & resilience | | observability.ts | @logged, @measure, @validate | Logging, metrics & validation |

All decorators are re-exported from decorators/index.ts for convenient imports:

// Import individual decorators
import { parallelMethod, memoize, retry, logged } from 'threadts-universal';

// Or import all from specific category
import { memoize, cache, lazy } from 'threadts-universal/decorators/caching';

Core Classes

ThreadTS (Singleton)

The main class managing worker pools and task execution:

// Always use getInstance() - ThreadTS is a singleton
const threadts = ThreadTS.getInstance({ poolSize: 4 });

// Methods are chainable where appropriate
await threadts.run(fn, data);
await threadts.map(array, fn);
await threadts.pipe(array).filter(fn).map(fn).execute();

Pipeline & TerminalPipeline

Fluent API for data processing with lazy evaluation:

// Pipeline provides intermediate operations (lazy)
const pipeline = threadts
  .pipe(data)
  .filter((x) => x > 0) // Returns Pipeline
  .map((x) => x * 2) // Returns Pipeline
  .sort((a, b) => a - b); // Returns Pipeline

// Terminal operations execute the pipeline
const result = await pipeline.execute(); // Returns TerminalPipeline
const array = await pipeline.toArray(); // Executes and returns array
const sum = await pipeline.sum().execute(); // Aggregation

Adapter Pattern

Platform-specific worker implementations extend the abstract WorkerAdapter:

abstract class WorkerAdapter {
  abstract createWorker(taskFn: Function, data: unknown): void;
  abstract terminate(): Promise<void>;
  abstract postMessage(message: unknown): void;
  // ... common interface
}

// Platform adapters
class BrowserWorkerAdapter extends WorkerAdapter {
  /* Web Worker */
}
class NodeWorkerAdapter extends WorkerAdapter {
  /* Worker Threads */
}
class DenoWorkerAdapter extends WorkerAdapter {
  /* Deno Worker */
}
class BunWorkerAdapter extends WorkerAdapter {
  /* Bun Worker */
}

🤝 Contributing

We welcome contributions! Please see our Contributing Guide for details.

Development Setup

git clone https://github.com/JosunLP/ThreadTS-Universal.git
cd universal
npm install
npm run build
npm test

📄 License

MIT License - see LICENSE file for details.

🌟 Why ThreadTS Universal?

Before ThreadTS

// Complex worker setup
const worker = new Worker('worker.js');
worker.postMessage(data);
worker.onmessage = (event) => {
  const result = event.data;
  // Handle result...
};
worker.onerror = (error) => {
  // Handle error...
};

With ThreadTS

// One line parallel execution
const result = await threadts.run(fn, data);

// Full Array API
const found = await threadts.find(array, predicate);
const all = await threadts.every(array, test);
const any = await threadts.some(array, test);

The ThreadTS Advantage

  • Universal: Same API across all platforms
  • Simple: One-line parallel execution
  • Complete: Full Array API (map, filter, reduce, find, some, every)
  • Fast: Sub-5ms overhead
  • Smart: Auto-scaling, caching, priorities
  • Safe: Built-in error handling, retries, and timeouts
  • Observable: Monitoring, health checks, and events
  • Modern: TypeScript-first with full type safety

Make parallel computing as simple as writing synchronous code. Experience the future of JavaScript parallelism with ThreadTS Universal.

Star on GitHub