feat: add environment detection and threading utility functions

Introduced new utilities for detecting execution environments (browser, Node.js, Web Worker) and checking threading availability. Added `workerUtils` for executing functions in Web Workers or Worker Threads seamlessly based on the environment.
This commit is contained in:
David Snelling 2025-06-04 10:01:23 -07:00
parent 9e10caf604
commit 9ffc9d965e
2 changed files with 194 additions and 0 deletions

57
src/utils/environment.ts Normal file
View file

@ -0,0 +1,57 @@
/**
* Utility functions for environment detection
*/
/**
* Check if code is running in a browser environment
*/
export function isBrowser(): boolean {
return typeof window !== 'undefined' && typeof document !== 'undefined';
}
/**
* Check if code is running in a Node.js environment
*/
export function isNode(): boolean {
return typeof process !== 'undefined' &&
process.versions != null &&
process.versions.node != null;
}
/**
* Check if code is running in a Web Worker environment
*/
export function isWebWorker(): boolean {
return typeof self === 'object' &&
self.constructor &&
self.constructor.name === 'DedicatedWorkerGlobalScope';
}
/**
* Check if Web Workers are available in the current environment
*/
export function areWebWorkersAvailable(): boolean {
return isBrowser() && typeof Worker !== 'undefined';
}
/**
* Check if Worker Threads are available in the current environment (Node.js)
*/
export function areWorkerThreadsAvailable(): boolean {
if (!isNode()) return false;
try {
// Dynamic import to avoid errors in browser environments
require('worker_threads');
return true;
} catch (e) {
return false;
}
}
/**
* Determine if threading is available in the current environment
*/
export function isThreadingAvailable(): boolean {
return areWebWorkersAvailable() || areWorkerThreadsAvailable();
}

137
src/utils/workerUtils.ts Normal file
View file

@ -0,0 +1,137 @@
/**
* Utility functions for working with Web Workers and Worker Threads
*/
import { isNode, isBrowser } from './environment.js';
/**
* Execute a function in a Web Worker (browser environment)
*
* @param fn The function to execute
* @param args The arguments to pass to the function
* @returns A promise that resolves with the result of the function
*/
export function executeInWebWorker<T>(fn: Function, ...args: any[]): Promise<T> {
return new Promise((resolve, reject) => {
// Create a blob URL for the worker script
const fnString = fn.toString();
const workerScript = `
self.onmessage = function(e) {
try {
const fn = ${fnString};
const result = fn(...e.data);
self.postMessage({ success: true, data: result });
} catch (error) {
self.postMessage({
success: false,
error: error instanceof Error ? error.message : String(error)
});
}
};
`;
const blob = new Blob([workerScript], { type: 'application/javascript' });
const blobURL = URL.createObjectURL(blob);
// Create a worker
const worker = new Worker(blobURL);
// Set up message handling
worker.onmessage = function(e: MessageEvent<{ success: boolean; data: T; error?: string }>) {
URL.revokeObjectURL(blobURL); // Clean up
worker.terminate(); // Terminate the worker
if (e.data.success) {
resolve(e.data.data);
} else {
reject(new Error(e.data.error));
}
};
worker.onerror = function(error: ErrorEvent) {
URL.revokeObjectURL(blobURL); // Clean up
worker.terminate(); // Terminate the worker
reject(error);
};
// Start the worker
worker.postMessage(args);
});
}
/**
* Execute a function in a Worker Thread (Node.js environment)
*
* @param fn The function to execute
* @param args The arguments to pass to the function
* @returns A promise that resolves with the result of the function
*/
export function executeInWorkerThread<T>(fn: Function, ...args: any[]): Promise<T> {
return new Promise((resolve, reject) => {
try {
// Dynamic import to avoid errors in browser environments
const { Worker } = require('worker_threads');
// Create a worker script
const fnString = fn.toString();
const workerScript = `
const { parentPort } = require('worker_threads');
parentPort.once('message', (data) => {
try {
const fn = ${fnString};
const result = fn(...data);
parentPort.postMessage({ success: true, data: result });
} catch (error) {
parentPort.postMessage({
success: false,
error: error instanceof Error ? error.message : String(error)
});
}
});
`;
// Create a worker
const worker = new Worker(workerScript, { eval: true });
// Set up message handling
worker.on('message', (data: { success: boolean; data: T; error?: string }) => {
worker.terminate(); // Terminate the worker
if (data.success) {
resolve(data.data);
} else {
reject(new Error(data.error));
}
});
worker.on('error', (error: Error) => {
worker.terminate(); // Terminate the worker
reject(error);
});
// Start the worker
worker.postMessage(args);
} catch (error) {
reject(error);
}
});
}
/**
* Execute a function in a separate thread based on the environment
*
* @param fn The function to execute
* @param args The arguments to pass to the function
* @returns A promise that resolves with the result of the function
*/
export function executeInThread<T>(fn: Function, ...args: any[]): Promise<T> {
if (isBrowser()) {
return executeInWebWorker<T>(fn, ...args);
} else if (isNode()) {
return executeInWorkerThread<T>(fn, ...args);
} else {
// Fall back to executing in the main thread
return Promise.resolve(fn(...args) as T);
}
}