From 9ffc9d965e3c851281189243312c492a6ccd0b40 Mon Sep 17 00:00:00 2001 From: David Snelling Date: Wed, 4 Jun 2025 10:01:23 -0700 Subject: [PATCH] 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. --- src/utils/environment.ts | 57 ++++++++++++++++ src/utils/workerUtils.ts | 137 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 194 insertions(+) create mode 100644 src/utils/environment.ts create mode 100644 src/utils/workerUtils.ts diff --git a/src/utils/environment.ts b/src/utils/environment.ts new file mode 100644 index 00000000..477b07ca --- /dev/null +++ b/src/utils/environment.ts @@ -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(); +} diff --git a/src/utils/workerUtils.ts b/src/utils/workerUtils.ts new file mode 100644 index 00000000..8e500ee3 --- /dev/null +++ b/src/utils/workerUtils.ts @@ -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(fn: Function, ...args: any[]): Promise { + 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(fn: Function, ...args: any[]): Promise { + 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(fn: Function, ...args: any[]): Promise { + if (isBrowser()) { + return executeInWebWorker(fn, ...args); + } else if (isNode()) { + return executeInWorkerThread(fn, ...args); + } else { + // Fall back to executing in the main thread + return Promise.resolve(fn(...args) as T); + } +}