threadts-universal
v1.0.0
Published
Universal TypeScript library for effortless parallel computing across all JavaScript ecosystems
Maintainers
Readme
🚀 ThreadTS Universal
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()
- Intermediate:
- Progress Tracking: Real-time progress monitoring
- Intelligent Caching: Automatic result caching with
@memoizeand@cachedecorators - 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-universalBasic 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: 24Method 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: 4threadts.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: 3threadts.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: truethreadts.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: truethreadts.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: 3Extended 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: 3threadts.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: 3threadts.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: 2threadts.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: undefinedthreadts.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: -1threadts.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, groupByObjectthreadts.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 instancesDecorators
@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 millisecondspriority: Execution priority ('low', 'normal', 'high')maxRetries: Number of retry attempts on failurepoolSize: Custom worker pool sizecacheResults: 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 operationPerformance 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 utilitiesDecorator 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(); // AggregationAdapter 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.
