@computekit/core 0.1.2 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +80 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +138 -14
- package/dist/index.d.ts +138 -14
- package/dist/index.js +80 -8
- package/dist/index.js.map +1 -1
- package/dist/types-BNUPDwV-.d.cts +363 -0
- package/dist/types-BNUPDwV-.d.ts +363 -0
- package/dist/worker.cjs +54 -2
- package/dist/worker.cjs.map +1 -1
- package/dist/worker.d.cts +1 -1
- package/dist/worker.d.ts +1 -1
- package/dist/worker.js +54 -2
- package/dist/worker.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +154 -28
- package/src/pool.ts +9 -1
- package/src/registry.ts +132 -0
- package/src/types.ts +222 -0
- package/src/utils.ts +74 -0
- package/src/worker/runtime.ts +8 -1
- package/dist/types-2XRPtzH9.d.cts +0 -145
- package/dist/types-2XRPtzH9.d.ts +0 -145
package/dist/worker.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/utils.ts","../src/worker/runtime.ts"],"names":[],"mappings":"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","file":"worker.js","sourcesContent":["/**\n * ComputeKit Utilities\n * Helper functions for the WASM + Worker toolkit\n */\n\n/**\n * Generate a unique ID\n */\nexport function generateId(): string {\n return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 11)}`;\n}\n\n/**\n * Check if running in a Web Worker context\n */\nexport function isWorkerContext(): boolean {\n return (\n typeof self !== 'undefined' &&\n typeof Window === 'undefined' &&\n typeof self.postMessage === 'function'\n );\n}\n\n/**\n * Check if running in a browser context\n */\nexport function isBrowserContext(): boolean {\n return typeof window !== 'undefined' && typeof document !== 'undefined';\n}\n\n/**\n * Check if SharedArrayBuffer is available\n */\nexport function isSharedArrayBufferAvailable(): boolean {\n try {\n return typeof SharedArrayBuffer !== 'undefined';\n } catch {\n return false;\n }\n}\n\n/**\n * Check if WASM is supported\n */\nexport function isWasmSupported(): boolean {\n try {\n if (typeof WebAssembly === 'object') {\n const module = new WebAssembly.Module(\n Uint8Array.of(0x0, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00)\n );\n return module instanceof WebAssembly.Module;\n }\n } catch {\n // WASM not supported\n }\n return false;\n}\n\n/**\n * Get the number of logical processors\n */\nexport function getHardwareConcurrency(): number {\n if (typeof navigator !== 'undefined' && navigator.hardwareConcurrency) {\n return navigator.hardwareConcurrency;\n }\n return 4; // Reasonable default\n}\n\n/**\n * Create a deferred promise\n */\nexport interface Deferred<T> {\n promise: Promise<T>;\n resolve: (value: T | PromiseLike<T>) => void;\n reject: (reason?: unknown) => void;\n}\n\nexport function createDeferred<T>(): Deferred<T> {\n let resolve!: (value: T | PromiseLike<T>) => void;\n let reject!: (reason?: unknown) => void;\n\n const promise = new Promise<T>((res, rej) => {\n resolve = res;\n reject = rej;\n });\n\n return { promise, resolve, reject };\n}\n\n/**\n * Create a timeout promise\n */\nexport function createTimeout(ms: number, message?: string): Promise<never> {\n return new Promise((_, reject) => {\n setTimeout(() => {\n reject(new Error(message || `Operation timed out after ${ms}ms`));\n }, ms);\n });\n}\n\n/**\n * Race a promise against a timeout\n */\nexport async function withTimeout<T>(\n promise: Promise<T>,\n ms: number,\n message?: string\n): Promise<T> {\n return Promise.race([promise, createTimeout(ms, message)]);\n}\n\n/**\n * Detect transferable objects in data\n */\nexport function findTransferables(data: unknown): Transferable[] {\n const transferables: Transferable[] = [];\n const seen = new WeakSet();\n\n function traverse(obj: unknown): void {\n if (obj === null || typeof obj !== 'object') return;\n if (seen.has(obj as object)) return;\n seen.add(obj as object);\n\n if (obj instanceof ArrayBuffer) {\n transferables.push(obj);\n return;\n }\n\n if (ArrayBuffer.isView(obj)) {\n transferables.push(obj.buffer);\n return;\n }\n\n if (obj instanceof MessagePort) {\n transferables.push(obj);\n return;\n }\n\n if (typeof ImageBitmap !== 'undefined' && obj instanceof ImageBitmap) {\n transferables.push(obj);\n return;\n }\n\n if (typeof OffscreenCanvas !== 'undefined' && obj instanceof OffscreenCanvas) {\n transferables.push(obj);\n return;\n }\n\n if (Array.isArray(obj)) {\n obj.forEach(traverse);\n return;\n }\n\n if (obj instanceof Map) {\n obj.forEach((value, key) => {\n traverse(key);\n traverse(value);\n });\n return;\n }\n\n if (obj instanceof Set) {\n obj.forEach(traverse);\n return;\n }\n\n Object.values(obj).forEach(traverse);\n }\n\n traverse(data);\n return transferables;\n}\n\n/**\n * Clone data, detaching transferables\n */\nexport function cloneForTransfer<T>(data: T): { data: T; transfer: Transferable[] } {\n const transfer = findTransferables(data);\n return { data, transfer };\n}\n\n/**\n * Create a typed event emitter\n */\nexport type EventHandler<T = unknown> = (data: T) => void;\n\nexport class EventEmitter<TEvents extends Record<string, unknown>> {\n private handlers = new Map<keyof TEvents, Set<EventHandler>>();\n\n on<K extends keyof TEvents>(event: K, handler: EventHandler<TEvents[K]>): () => void {\n if (!this.handlers.has(event)) {\n this.handlers.set(event, new Set());\n }\n this.handlers.get(event)!.add(handler as EventHandler);\n\n // Return unsubscribe function\n return () => this.off(event, handler);\n }\n\n off<K extends keyof TEvents>(event: K, handler: EventHandler<TEvents[K]>): void {\n this.handlers.get(event)?.delete(handler as EventHandler);\n }\n\n emit<K extends keyof TEvents>(event: K, data: TEvents[K]): void {\n this.handlers.get(event)?.forEach((handler) => {\n try {\n handler(data);\n } catch (err) {\n console.error(`Error in event handler for ${String(event)}:`, err);\n }\n });\n }\n\n removeAllListeners(event?: keyof TEvents): void {\n if (event) {\n this.handlers.delete(event);\n } else {\n this.handlers.clear();\n }\n }\n}\n\n/**\n * Simple LRU cache\n */\nexport class LRUCache<K, V> {\n private cache = new Map<K, V>();\n private maxSize: number;\n\n constructor(maxSize: number = 100) {\n this.maxSize = maxSize;\n }\n\n get(key: K): V | undefined {\n const value = this.cache.get(key);\n if (value !== undefined) {\n // Move to end (most recently used)\n this.cache.delete(key);\n this.cache.set(key, value);\n }\n return value;\n }\n\n set(key: K, value: V): void {\n if (this.cache.has(key)) {\n this.cache.delete(key);\n } else if (this.cache.size >= this.maxSize) {\n // Delete oldest (first) entry\n const firstKey = this.cache.keys().next().value;\n if (firstKey !== undefined) {\n this.cache.delete(firstKey);\n }\n }\n this.cache.set(key, value);\n }\n\n has(key: K): boolean {\n return this.cache.has(key);\n }\n\n delete(key: K): boolean {\n return this.cache.delete(key);\n }\n\n clear(): void {\n this.cache.clear();\n }\n\n get size(): number {\n return this.cache.size;\n }\n}\n\n/**\n * Serialize function to string for worker\n */\n// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type\nexport function serializeFunction(fn: Function): string {\n return fn.toString();\n}\n\n/**\n * Logger utility\n */\nexport interface Logger {\n debug(...args: unknown[]): void;\n info(...args: unknown[]): void;\n warn(...args: unknown[]): void;\n error(...args: unknown[]): void;\n}\n\nexport function createLogger(prefix: string, enabled: boolean = false): Logger {\n const noop = () => {};\n const log = (level: string) =>\n enabled ? (...args: unknown[]) => console.log(`[${prefix}:${level}]`, ...args) : noop;\n\n return {\n debug: log('debug'),\n info: log('info'),\n warn: enabled\n ? (...args: unknown[]) => console.warn(`[${prefix}:warn]`, ...args)\n : noop,\n error: (...args: unknown[]) => console.error(`[${prefix}:error]`, ...args),\n };\n}\n","/**\n * ComputeKit Worker Runtime\n * Code that runs inside Web Workers\n */\n\nimport type {\n WorkerMessage,\n ExecutePayload,\n ResultPayload,\n ErrorPayload,\n ComputeProgress,\n} from '../types';\n\nimport { generateId, findTransferables } from '../utils';\n\n/** Registry of compute functions available in the worker */\n// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type\nconst functionRegistry = new Map<string, Function>();\n\n/** Current task context for progress reporting */\nlet currentTaskId: string | null = null;\n\n/**\n * Report progress from within a compute function\n */\nexport function reportProgress(progress: Partial<ComputeProgress>): void {\n if (!currentTaskId) {\n console.warn('reportProgress called outside of compute function');\n return;\n }\n\n const message: WorkerMessage = {\n id: generateId(),\n type: 'progress',\n payload: {\n taskId: currentTaskId,\n progress: {\n percent: progress.percent ?? 0,\n phase: progress.phase,\n estimatedTimeRemaining: progress.estimatedTimeRemaining,\n data: progress.data,\n },\n },\n timestamp: Date.now(),\n };\n\n self.postMessage(message);\n}\n\n/**\n * Register a compute function in the worker\n */\nexport function registerFunction(\n name: string,\n // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type\n fn: Function\n): void {\n functionRegistry.set(name, fn);\n}\n\n/**\n * Execute a registered function\n */\nasync function executeFunction(functionName: string, input: unknown): Promise<unknown> {\n const fn = functionRegistry.get(functionName);\n\n if (!fn) {\n throw new Error(`Function \"${functionName}\" not found in worker`);\n }\n\n return fn(input);\n}\n\n/**\n * Handle incoming messages from the main thread\n */\nasync function handleMessage(event: MessageEvent<WorkerMessage>): Promise<void> {\n const { id, type, payload } = event.data;\n\n if (type === 'execute') {\n const { functionName, input } = payload as ExecutePayload;\n const startTime = performance.now();\n\n // Set current task for progress reporting\n currentTaskId = id;\n\n try {\n const result = await executeFunction(functionName, input);\n const duration = performance.now() - startTime;\n\n // Find transferable objects in result\n const transfer = findTransferables(result);\n\n const response: WorkerMessage<ResultPayload> = {\n id,\n type: 'result',\n payload: {\n data: result,\n duration,\n },\n timestamp: Date.now(),\n };\n\n self.postMessage(response, transfer as Transferable[]);\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n\n const response: WorkerMessage<ErrorPayload> = {\n id,\n type: 'error',\n payload: {\n message: error.message,\n stack: error.stack,\n },\n timestamp: Date.now(),\n };\n\n self.postMessage(response);\n } finally {\n currentTaskId = null;\n }\n } else if (type === 'init') {\n // Handle initialization if needed\n const response: WorkerMessage = {\n id,\n type: 'ready',\n timestamp: Date.now(),\n };\n self.postMessage(response);\n }\n}\n\n/**\n * Initialize the worker runtime\n */\nexport function initWorkerRuntime(): void {\n self.onmessage = handleMessage;\n\n // Signal that worker is ready\n const readyMessage: WorkerMessage = {\n id: generateId(),\n type: 'ready',\n timestamp: Date.now(),\n };\n self.postMessage(readyMessage);\n}\n\n// Export for use in worker entry point\nexport { functionRegistry };\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/utils.ts","../src/worker/runtime.ts"],"names":[],"mappings":"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","file":"worker.js","sourcesContent":["/**\n * ComputeKit Utilities\n * Helper functions for the WASM + Worker toolkit\n */\n\n/**\n * Generate a unique ID\n */\nexport function generateId(): string {\n return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 11)}`;\n}\n\n/**\n * Check if running in a Web Worker context\n */\nexport function isWorkerContext(): boolean {\n return (\n typeof self !== 'undefined' &&\n typeof Window === 'undefined' &&\n typeof self.postMessage === 'function'\n );\n}\n\n/**\n * Check if running in a browser context\n */\nexport function isBrowserContext(): boolean {\n return typeof window !== 'undefined' && typeof document !== 'undefined';\n}\n\n/**\n * Check if SharedArrayBuffer is available\n */\nexport function isSharedArrayBufferAvailable(): boolean {\n try {\n return typeof SharedArrayBuffer !== 'undefined';\n } catch {\n return false;\n }\n}\n\n/**\n * Check if WASM is supported\n */\nexport function isWasmSupported(): boolean {\n try {\n if (typeof WebAssembly === 'object') {\n const module = new WebAssembly.Module(\n Uint8Array.of(0x0, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00)\n );\n return module instanceof WebAssembly.Module;\n }\n } catch {\n // WASM not supported\n }\n return false;\n}\n\n/**\n * Get the number of logical processors\n */\nexport function getHardwareConcurrency(): number {\n if (typeof navigator !== 'undefined' && navigator.hardwareConcurrency) {\n return navigator.hardwareConcurrency;\n }\n return 4; // Reasonable default\n}\n\n/**\n * Create a deferred promise\n */\nexport interface Deferred<T> {\n promise: Promise<T>;\n resolve: (value: T | PromiseLike<T>) => void;\n reject: (reason?: unknown) => void;\n}\n\nexport function createDeferred<T>(): Deferred<T> {\n let resolve!: (value: T | PromiseLike<T>) => void;\n let reject!: (reason?: unknown) => void;\n\n const promise = new Promise<T>((res, rej) => {\n resolve = res;\n reject = rej;\n });\n\n return { promise, resolve, reject };\n}\n\n/**\n * Create a timeout promise\n */\nexport function createTimeout(ms: number, message?: string): Promise<never> {\n return new Promise((_, reject) => {\n setTimeout(() => {\n reject(new Error(message || `Operation timed out after ${ms}ms`));\n }, ms);\n });\n}\n\n/**\n * Race a promise against a timeout\n */\nexport async function withTimeout<T>(\n promise: Promise<T>,\n ms: number,\n message?: string\n): Promise<T> {\n return Promise.race([promise, createTimeout(ms, message)]);\n}\n\n/**\n * Detect transferable objects in data\n */\nexport function findTransferables(data: unknown): Transferable[] {\n const transferables: Transferable[] = [];\n const seen = new WeakSet();\n\n function traverse(obj: unknown): void {\n if (obj === null || typeof obj !== 'object') return;\n if (seen.has(obj as object)) return;\n seen.add(obj as object);\n\n if (obj instanceof ArrayBuffer) {\n transferables.push(obj);\n return;\n }\n\n if (ArrayBuffer.isView(obj)) {\n transferables.push(obj.buffer);\n return;\n }\n\n if (obj instanceof MessagePort) {\n transferables.push(obj);\n return;\n }\n\n if (typeof ImageBitmap !== 'undefined' && obj instanceof ImageBitmap) {\n transferables.push(obj);\n return;\n }\n\n if (typeof OffscreenCanvas !== 'undefined' && obj instanceof OffscreenCanvas) {\n transferables.push(obj);\n return;\n }\n\n if (Array.isArray(obj)) {\n obj.forEach(traverse);\n return;\n }\n\n if (obj instanceof Map) {\n obj.forEach((value, key) => {\n traverse(key);\n traverse(value);\n });\n return;\n }\n\n if (obj instanceof Set) {\n obj.forEach(traverse);\n return;\n }\n\n Object.values(obj).forEach(traverse);\n }\n\n traverse(data);\n return transferables;\n}\n\n/**\n * Clone data, detaching transferables\n */\nexport function cloneForTransfer<T>(data: T): { data: T; transfer: Transferable[] } {\n const transfer = findTransferables(data);\n return { data, transfer };\n}\n\n/**\n * Create a typed event emitter\n */\nexport type EventHandler<T = unknown> = (data: T) => void;\n\nexport class EventEmitter<TEvents extends Record<string, unknown>> {\n private handlers = new Map<keyof TEvents, Set<EventHandler>>();\n\n on<K extends keyof TEvents>(event: K, handler: EventHandler<TEvents[K]>): () => void {\n if (!this.handlers.has(event)) {\n this.handlers.set(event, new Set());\n }\n this.handlers.get(event)!.add(handler as EventHandler);\n\n // Return unsubscribe function\n return () => this.off(event, handler);\n }\n\n off<K extends keyof TEvents>(event: K, handler: EventHandler<TEvents[K]>): void {\n this.handlers.get(event)?.delete(handler as EventHandler);\n }\n\n emit<K extends keyof TEvents>(event: K, data: TEvents[K]): void {\n this.handlers.get(event)?.forEach((handler) => {\n try {\n handler(data);\n } catch (err) {\n console.error(`Error in event handler for ${String(event)}:`, err);\n }\n });\n }\n\n removeAllListeners(event?: keyof TEvents): void {\n if (event) {\n this.handlers.delete(event);\n } else {\n this.handlers.clear();\n }\n }\n}\n\n/**\n * Simple LRU cache\n */\nexport class LRUCache<K, V> {\n private cache = new Map<K, V>();\n private maxSize: number;\n\n constructor(maxSize: number = 100) {\n this.maxSize = maxSize;\n }\n\n get(key: K): V | undefined {\n const value = this.cache.get(key);\n if (value !== undefined) {\n // Move to end (most recently used)\n this.cache.delete(key);\n this.cache.set(key, value);\n }\n return value;\n }\n\n set(key: K, value: V): void {\n if (this.cache.has(key)) {\n this.cache.delete(key);\n } else if (this.cache.size >= this.maxSize) {\n // Delete oldest (first) entry\n const firstKey = this.cache.keys().next().value;\n if (firstKey !== undefined) {\n this.cache.delete(firstKey);\n }\n }\n this.cache.set(key, value);\n }\n\n has(key: K): boolean {\n return this.cache.has(key);\n }\n\n delete(key: K): boolean {\n return this.cache.delete(key);\n }\n\n clear(): void {\n this.cache.clear();\n }\n\n get size(): number {\n return this.cache.size;\n }\n}\n\n/**\n * Serialize function to string for worker\n */\n// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type\nexport function serializeFunction(fn: Function): string {\n return fn.toString();\n}\n\n/**\n * Estimate the byte size of a value for structured cloning.\n * This is an approximation useful for debugging and performance monitoring.\n */\nexport function estimatePayloadSize(value: unknown): number {\n if (value === null || value === undefined) return 0;\n if (typeof value === 'boolean') return 4;\n if (typeof value === 'number') return 8;\n if (typeof value === 'string') return value.length * 2; // UTF-16\n\n if (value instanceof ArrayBuffer) return value.byteLength;\n if (ArrayBuffer.isView(value)) return value.byteLength;\n if (value instanceof Blob) return value.size;\n\n const seen = new WeakSet<object>();\n\n function traverse(obj: unknown): number {\n if (obj === null || typeof obj !== 'object') {\n if (typeof obj === 'boolean') return 4;\n if (typeof obj === 'number') return 8;\n if (typeof obj === 'string') return (obj as string).length * 2;\n return 0;\n }\n\n if (seen.has(obj)) return 0; // Avoid infinite loops\n seen.add(obj);\n\n if (obj instanceof ArrayBuffer) return obj.byteLength;\n if (ArrayBuffer.isView(obj)) return obj.byteLength;\n if (obj instanceof Blob) return obj.size;\n if (obj instanceof Date) return 8;\n if (obj instanceof RegExp) return obj.source.length * 2;\n\n if (Array.isArray(obj)) {\n return obj.reduce((sum, item) => sum + traverse(item), 0);\n }\n\n if (obj instanceof Map) {\n let size = 0;\n obj.forEach((val, key) => {\n size += traverse(key) + traverse(val);\n });\n return size;\n }\n\n if (obj instanceof Set) {\n let size = 0;\n obj.forEach((val) => {\n size += traverse(val);\n });\n return size;\n }\n\n // Plain object\n return Object.entries(obj).reduce(\n (sum, [key, val]) => sum + key.length * 2 + traverse(val),\n 0\n );\n }\n\n return traverse(value);\n}\n\n/**\n * Format bytes to human-readable string\n */\nexport function formatBytes(bytes: number): string {\n if (bytes === 0) return '0 B';\n const k = 1024;\n const sizes = ['B', 'KB', 'MB', 'GB'];\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n return `${(bytes / Math.pow(k, i)).toFixed(i > 0 ? 1 : 0)} ${sizes[i]}`;\n}\n\n/**\n * Logger utility\n */\nexport interface Logger {\n debug(...args: unknown[]): void;\n info(...args: unknown[]): void;\n warn(...args: unknown[]): void;\n error(...args: unknown[]): void;\n}\n\nexport function createLogger(prefix: string, enabled: boolean = false): Logger {\n const noop = () => {};\n const log = (level: string) =>\n enabled ? (...args: unknown[]) => console.log(`[${prefix}:${level}]`, ...args) : noop;\n\n return {\n debug: log('debug'),\n info: log('info'),\n warn: enabled\n ? (...args: unknown[]) => console.warn(`[${prefix}:warn]`, ...args)\n : noop,\n error: (...args: unknown[]) => console.error(`[${prefix}:error]`, ...args),\n };\n}\n","/**\n * ComputeKit Worker Runtime\n * Code that runs inside Web Workers\n */\n\nimport type {\n WorkerMessage,\n ExecutePayload,\n ResultPayload,\n ErrorPayload,\n ComputeProgress,\n} from '../types';\n\nimport { generateId, findTransferables, estimatePayloadSize } from '../utils';\n\n/** Registry of compute functions available in the worker */\n// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type\nconst functionRegistry = new Map<string, Function>();\n\n/** Current task context for progress reporting */\nlet currentTaskId: string | null = null;\n\n/**\n * Report progress from within a compute function\n */\nexport function reportProgress(progress: Partial<ComputeProgress>): void {\n if (!currentTaskId) {\n console.warn('reportProgress called outside of compute function');\n return;\n }\n\n const message: WorkerMessage = {\n id: generateId(),\n type: 'progress',\n payload: {\n taskId: currentTaskId,\n progress: {\n percent: progress.percent ?? 0,\n phase: progress.phase,\n estimatedTimeRemaining: progress.estimatedTimeRemaining,\n data: progress.data,\n },\n },\n timestamp: Date.now(),\n };\n\n self.postMessage(message);\n}\n\n/**\n * Register a compute function in the worker\n */\nexport function registerFunction(\n name: string,\n // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type\n fn: Function\n): void {\n functionRegistry.set(name, fn);\n}\n\n/**\n * Execute a registered function\n */\nasync function executeFunction(functionName: string, input: unknown): Promise<unknown> {\n const fn = functionRegistry.get(functionName);\n\n if (!fn) {\n throw new Error(`Function \"${functionName}\" not found in worker`);\n }\n\n return fn(input);\n}\n\n/**\n * Handle incoming messages from the main thread\n */\nasync function handleMessage(event: MessageEvent<WorkerMessage>): Promise<void> {\n const { id, type, payload } = event.data;\n\n if (type === 'execute') {\n const { functionName, input } = payload as ExecutePayload;\n const startTime = performance.now();\n\n // Set current task for progress reporting\n currentTaskId = id;\n\n try {\n const result = await executeFunction(functionName, input);\n const duration = performance.now() - startTime;\n\n // Find transferable objects in result\n const transfer = findTransferables(result);\n\n // Estimate output size for debugging\n const outputSize = estimatePayloadSize(result);\n\n const response: WorkerMessage<ResultPayload> = {\n id,\n type: 'result',\n payload: {\n data: result,\n duration,\n outputSize,\n },\n timestamp: Date.now(),\n };\n\n self.postMessage(response, transfer as Transferable[]);\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n const duration = performance.now() - startTime;\n\n const response: WorkerMessage<ErrorPayload> = {\n id,\n type: 'error',\n payload: {\n message: error.message,\n stack: error.stack,\n functionName,\n duration,\n },\n timestamp: Date.now(),\n };\n\n self.postMessage(response);\n } finally {\n currentTaskId = null;\n }\n } else if (type === 'init') {\n // Handle initialization if needed\n const response: WorkerMessage = {\n id,\n type: 'ready',\n timestamp: Date.now(),\n };\n self.postMessage(response);\n }\n}\n\n/**\n * Initialize the worker runtime\n */\nexport function initWorkerRuntime(): void {\n self.onmessage = handleMessage;\n\n // Signal that worker is ready\n const readyMessage: WorkerMessage = {\n id: generateId(),\n type: 'ready',\n timestamp: Date.now(),\n };\n self.postMessage(readyMessage);\n}\n\n// Export for use in worker entry point\nexport { functionRegistry };\n"]}
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -11,6 +11,14 @@ import type {
|
|
|
11
11
|
ComputeKitEvents,
|
|
12
12
|
} from './types';
|
|
13
13
|
|
|
14
|
+
import type {
|
|
15
|
+
ComputeFunctionRegistry,
|
|
16
|
+
RegisteredFunctionName,
|
|
17
|
+
FunctionInput,
|
|
18
|
+
FunctionOutput,
|
|
19
|
+
ComputeFn,
|
|
20
|
+
} from './registry';
|
|
21
|
+
|
|
14
22
|
import { WorkerPool } from './pool';
|
|
15
23
|
import { EventEmitter, isWasmSupported, createLogger } from './utils';
|
|
16
24
|
|
|
@@ -60,48 +68,79 @@ export class ComputeKit extends EventEmitter<ComputeKitEvents> {
|
|
|
60
68
|
/**
|
|
61
69
|
* Register a compute function
|
|
62
70
|
*
|
|
63
|
-
* @param name - Unique name for the function
|
|
71
|
+
* @param name - Unique name for the function (autocompletes if registry is extended)
|
|
64
72
|
* @param fn - The function to execute (will run in a Web Worker)
|
|
65
73
|
*
|
|
66
74
|
* @example
|
|
67
75
|
* ```ts
|
|
76
|
+
* // Basic usage
|
|
68
77
|
* kit.register('sum', (arr: number[]) => arr.reduce((a, b) => a + b, 0));
|
|
78
|
+
*
|
|
79
|
+
* // With typed registry (extend ComputeFunctionRegistry for autocomplete)
|
|
80
|
+
* // declare module '@computekit/core' {
|
|
81
|
+
* // interface ComputeFunctionRegistry {
|
|
82
|
+
* // sum: { input: number[]; output: number };
|
|
83
|
+
* // }
|
|
84
|
+
* // }
|
|
85
|
+
* // kit.register('sum', (arr) => arr.reduce((a, b) => a + b, 0));
|
|
69
86
|
* ```
|
|
70
87
|
*/
|
|
71
|
-
register<
|
|
72
|
-
|
|
73
|
-
|
|
88
|
+
register<
|
|
89
|
+
TName extends RegisteredFunctionName,
|
|
90
|
+
TInput = FunctionInput<TName extends string ? TName : never>,
|
|
91
|
+
TOutput = FunctionOutput<TName extends string ? TName : never>,
|
|
92
|
+
>(
|
|
93
|
+
name: TName,
|
|
94
|
+
fn: TName extends keyof ComputeFunctionRegistry
|
|
95
|
+
? ComputeFn<
|
|
96
|
+
ComputeFunctionRegistry[TName]['input'],
|
|
97
|
+
ComputeFunctionRegistry[TName]['output']
|
|
98
|
+
>
|
|
99
|
+
: ComputeFn<TInput, TOutput>
|
|
74
100
|
): this {
|
|
75
|
-
this.pool.register(name, fn);
|
|
101
|
+
this.pool.register(name as string, fn as ComputeFn<unknown, unknown>);
|
|
76
102
|
return this;
|
|
77
103
|
}
|
|
78
104
|
|
|
79
105
|
/**
|
|
80
106
|
* Execute a registered compute function
|
|
81
107
|
*
|
|
82
|
-
* @param name - Name of the registered function
|
|
83
|
-
* @param input - Input data for the function
|
|
108
|
+
* @param name - Name of the registered function (autocompletes if registry is extended)
|
|
109
|
+
* @param input - Input data for the function (type-safe if registry is extended)
|
|
84
110
|
* @param options - Execution options
|
|
85
|
-
* @returns Promise resolving to the function result
|
|
111
|
+
* @returns Promise resolving to the function result (type-safe if registry is extended)
|
|
86
112
|
*
|
|
87
113
|
* @example
|
|
88
114
|
* ```ts
|
|
89
115
|
* const sum = await kit.run('sum', [1, 2, 3, 4, 5]);
|
|
116
|
+
*
|
|
117
|
+
* // With typed registry, input/output types are inferred:
|
|
118
|
+
* // const result = await kit.run('fibonacci', 50); // result: number
|
|
90
119
|
* ```
|
|
91
120
|
*/
|
|
92
|
-
async run<
|
|
93
|
-
|
|
94
|
-
|
|
121
|
+
async run<
|
|
122
|
+
TName extends RegisteredFunctionName,
|
|
123
|
+
TInput = FunctionInput<TName extends string ? TName : never>,
|
|
124
|
+
TOutput = FunctionOutput<TName extends string ? TName : never>,
|
|
125
|
+
>(
|
|
126
|
+
name: TName,
|
|
127
|
+
input: TName extends keyof ComputeFunctionRegistry
|
|
128
|
+
? ComputeFunctionRegistry[TName]['input']
|
|
129
|
+
: TInput,
|
|
95
130
|
options?: ComputeOptions
|
|
96
|
-
): Promise<
|
|
97
|
-
|
|
131
|
+
): Promise<
|
|
132
|
+
TName extends keyof ComputeFunctionRegistry
|
|
133
|
+
? ComputeFunctionRegistry[TName]['output']
|
|
134
|
+
: TOutput
|
|
135
|
+
> {
|
|
136
|
+
return this.pool.execute(name as string, input, options);
|
|
98
137
|
}
|
|
99
138
|
|
|
100
139
|
/**
|
|
101
140
|
* Execute a registered compute function with full result metadata
|
|
102
141
|
*
|
|
103
|
-
* @param name - Name of the registered function
|
|
104
|
-
* @param input - Input data for the function
|
|
142
|
+
* @param name - Name of the registered function (autocompletes if registry is extended)
|
|
143
|
+
* @param input - Input data for the function (type-safe if registry is extended)
|
|
105
144
|
* @param options - Execution options
|
|
106
145
|
* @returns Promise resolving to ComputeResult with metadata
|
|
107
146
|
*
|
|
@@ -111,13 +150,33 @@ export class ComputeKit extends EventEmitter<ComputeKitEvents> {
|
|
|
111
150
|
* console.log(`Took ${result.duration}ms`);
|
|
112
151
|
* ```
|
|
113
152
|
*/
|
|
114
|
-
async runWithMetadata<
|
|
115
|
-
|
|
116
|
-
|
|
153
|
+
async runWithMetadata<
|
|
154
|
+
TName extends RegisteredFunctionName,
|
|
155
|
+
TInput = FunctionInput<TName extends string ? TName : never>,
|
|
156
|
+
TOutput = FunctionOutput<TName extends string ? TName : never>,
|
|
157
|
+
>(
|
|
158
|
+
name: TName,
|
|
159
|
+
input: TName extends keyof ComputeFunctionRegistry
|
|
160
|
+
? ComputeFunctionRegistry[TName]['input']
|
|
161
|
+
: TInput,
|
|
117
162
|
options?: ComputeOptions
|
|
118
|
-
): Promise<
|
|
163
|
+
): Promise<
|
|
164
|
+
ComputeResult<
|
|
165
|
+
TName extends keyof ComputeFunctionRegistry
|
|
166
|
+
? ComputeFunctionRegistry[TName]['output']
|
|
167
|
+
: TOutput
|
|
168
|
+
>
|
|
169
|
+
> {
|
|
170
|
+
type ActualOutput = TName extends keyof ComputeFunctionRegistry
|
|
171
|
+
? ComputeFunctionRegistry[TName]['output']
|
|
172
|
+
: TOutput;
|
|
173
|
+
|
|
119
174
|
const startTime = performance.now();
|
|
120
|
-
const data = await this.pool.execute
|
|
175
|
+
const data = (await this.pool.execute(
|
|
176
|
+
name as string,
|
|
177
|
+
input,
|
|
178
|
+
options
|
|
179
|
+
)) as ActualOutput;
|
|
121
180
|
const duration = performance.now() - startTime;
|
|
122
181
|
|
|
123
182
|
return {
|
|
@@ -175,23 +234,64 @@ export function getDefaultInstance(): ComputeKit {
|
|
|
175
234
|
|
|
176
235
|
/**
|
|
177
236
|
* Register a function on the default instance
|
|
237
|
+
*
|
|
238
|
+
* @example
|
|
239
|
+
* ```ts
|
|
240
|
+
* import { register } from '@computekit/core';
|
|
241
|
+
*
|
|
242
|
+
* register('fibonacci', (n: number) => {
|
|
243
|
+
* if (n <= 1) return n;
|
|
244
|
+
* let a = 0, b = 1;
|
|
245
|
+
* for (let i = 2; i <= n; i++) {
|
|
246
|
+
* [a, b] = [b, a + b];
|
|
247
|
+
* }
|
|
248
|
+
* return b;
|
|
249
|
+
* });
|
|
250
|
+
* ```
|
|
178
251
|
*/
|
|
179
|
-
export function register<
|
|
180
|
-
|
|
181
|
-
|
|
252
|
+
export function register<
|
|
253
|
+
TName extends RegisteredFunctionName,
|
|
254
|
+
TInput = FunctionInput<TName extends string ? TName : never>,
|
|
255
|
+
TOutput = FunctionOutput<TName extends string ? TName : never>,
|
|
256
|
+
>(
|
|
257
|
+
name: TName,
|
|
258
|
+
fn: TName extends keyof ComputeFunctionRegistry
|
|
259
|
+
? ComputeFn<
|
|
260
|
+
ComputeFunctionRegistry[TName]['input'],
|
|
261
|
+
ComputeFunctionRegistry[TName]['output']
|
|
262
|
+
>
|
|
263
|
+
: ComputeFn<TInput, TOutput>
|
|
182
264
|
): void {
|
|
183
265
|
getDefaultInstance().register(name, fn);
|
|
184
266
|
}
|
|
185
267
|
|
|
186
268
|
/**
|
|
187
269
|
* Run a function on the default instance
|
|
270
|
+
*
|
|
271
|
+
* @example
|
|
272
|
+
* ```ts
|
|
273
|
+
* import { run } from '@computekit/core';
|
|
274
|
+
*
|
|
275
|
+
* const result = await run('fibonacci', 50);
|
|
276
|
+
* console.log(result); // Type is inferred if registry is extended
|
|
277
|
+
* ```
|
|
188
278
|
*/
|
|
189
|
-
export async function run<
|
|
190
|
-
|
|
191
|
-
|
|
279
|
+
export async function run<
|
|
280
|
+
TName extends RegisteredFunctionName,
|
|
281
|
+
TInput = FunctionInput<TName extends string ? TName : never>,
|
|
282
|
+
TOutput = FunctionOutput<TName extends string ? TName : never>,
|
|
283
|
+
>(
|
|
284
|
+
name: TName,
|
|
285
|
+
input: TName extends keyof ComputeFunctionRegistry
|
|
286
|
+
? ComputeFunctionRegistry[TName]['input']
|
|
287
|
+
: TInput,
|
|
192
288
|
options?: ComputeOptions
|
|
193
|
-
): Promise<
|
|
194
|
-
|
|
289
|
+
): Promise<
|
|
290
|
+
TName extends keyof ComputeFunctionRegistry
|
|
291
|
+
? ComputeFunctionRegistry[TName]['output']
|
|
292
|
+
: TOutput
|
|
293
|
+
> {
|
|
294
|
+
return getDefaultInstance().run(name, input, options);
|
|
195
295
|
}
|
|
196
296
|
|
|
197
297
|
// Re-export types
|
|
@@ -205,8 +305,34 @@ export type {
|
|
|
205
305
|
WorkerInfo,
|
|
206
306
|
WasmModuleConfig,
|
|
207
307
|
ComputeKitEvents,
|
|
308
|
+
// Pipeline types
|
|
309
|
+
StageStatus,
|
|
310
|
+
StageInfo,
|
|
311
|
+
StageConfig,
|
|
312
|
+
PipelineMode,
|
|
313
|
+
PipelineStatus,
|
|
314
|
+
PipelineState,
|
|
315
|
+
PipelineMetrics,
|
|
316
|
+
PipelineOptions,
|
|
317
|
+
PipelineEvents,
|
|
318
|
+
// Parallel batch types
|
|
319
|
+
ParallelBatchConfig,
|
|
320
|
+
BatchItemResult,
|
|
321
|
+
ParallelBatchResult,
|
|
208
322
|
} from './types';
|
|
209
323
|
|
|
324
|
+
// Re-export typed registry types
|
|
325
|
+
export type {
|
|
326
|
+
ComputeFunctionRegistry,
|
|
327
|
+
RegisteredFunctionName,
|
|
328
|
+
FunctionInput,
|
|
329
|
+
FunctionOutput,
|
|
330
|
+
ComputeFn,
|
|
331
|
+
InferComputeFn,
|
|
332
|
+
DefineFunction,
|
|
333
|
+
HasRegisteredFunctions,
|
|
334
|
+
} from './registry';
|
|
335
|
+
|
|
210
336
|
// Re-export utilities
|
|
211
337
|
export {
|
|
212
338
|
isWasmSupported,
|
package/src/pool.ts
CHANGED
|
@@ -24,6 +24,8 @@ import {
|
|
|
24
24
|
findTransferables,
|
|
25
25
|
getHardwareConcurrency,
|
|
26
26
|
createLogger,
|
|
27
|
+
estimatePayloadSize,
|
|
28
|
+
formatBytes,
|
|
27
29
|
type Deferred,
|
|
28
30
|
type Logger,
|
|
29
31
|
} from './utils';
|
|
@@ -427,8 +429,14 @@ self.postMessage({ type: 'ready' });
|
|
|
427
429
|
this.stats.tasksCompleted++;
|
|
428
430
|
this.stats.totalDuration += resultPayload.duration;
|
|
429
431
|
|
|
432
|
+
// Log with payload sizes in debug mode
|
|
433
|
+
const inputSize = estimatePayloadSize(task.input);
|
|
434
|
+
const outputSize = resultPayload.outputSize ?? 0;
|
|
430
435
|
this.logger.debug(
|
|
431
|
-
`Task ${id}
|
|
436
|
+
`Task ${id} (${task.functionName}): ` +
|
|
437
|
+
`input=${formatBytes(inputSize)}, ` +
|
|
438
|
+
`output=${formatBytes(outputSize)}, ` +
|
|
439
|
+
`duration=${resultPayload.duration.toFixed(2)}ms`
|
|
432
440
|
);
|
|
433
441
|
task.deferred.resolve(resultPayload.data);
|
|
434
442
|
|
package/src/registry.ts
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ComputeKit Typed Registry
|
|
3
|
+
*
|
|
4
|
+
* This module provides type-safe function registration and execution.
|
|
5
|
+
* Users can extend the ComputeFunctionRegistry interface to get autocomplete
|
|
6
|
+
* and type safety for their registered functions.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```ts
|
|
10
|
+
* // Extend the registry interface (in a .d.ts file or at the top of your file)
|
|
11
|
+
* declare module '@computekit/core' {
|
|
12
|
+
* interface ComputeFunctionRegistry {
|
|
13
|
+
* fibonacci: { input: number; output: number };
|
|
14
|
+
* sum: { input: number[]; output: number };
|
|
15
|
+
* processData: { input: { items: string[] }; output: { count: number } };
|
|
16
|
+
* }
|
|
17
|
+
* }
|
|
18
|
+
*
|
|
19
|
+
* // Now you get autocomplete and type safety!
|
|
20
|
+
* const kit = new ComputeKit();
|
|
21
|
+
* kit.register('fibonacci', (n) => ...); // n is inferred as number
|
|
22
|
+
* const result = await kit.run('fibonacci', 42); // result is number
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Registry interface for compute functions.
|
|
28
|
+
* Extend this interface using module augmentation to add your own functions.
|
|
29
|
+
*
|
|
30
|
+
* Each entry should be in the format:
|
|
31
|
+
* ```ts
|
|
32
|
+
* functionName: { input: InputType; output: OutputType }
|
|
33
|
+
* ```
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* ```ts
|
|
37
|
+
* declare module '@computekit/core' {
|
|
38
|
+
* interface ComputeFunctionRegistry {
|
|
39
|
+
* myFunction: { input: string; output: number };
|
|
40
|
+
* }
|
|
41
|
+
* }
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
|
45
|
+
export interface ComputeFunctionRegistry {}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Helper type to get all registered function names.
|
|
49
|
+
* If no functions are registered, falls back to string.
|
|
50
|
+
*/
|
|
51
|
+
export type RegisteredFunctionName = keyof ComputeFunctionRegistry extends never
|
|
52
|
+
? string
|
|
53
|
+
: keyof ComputeFunctionRegistry;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Helper type to check if the registry has any entries.
|
|
57
|
+
*/
|
|
58
|
+
export type HasRegisteredFunctions = keyof ComputeFunctionRegistry extends never
|
|
59
|
+
? false
|
|
60
|
+
: true;
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Get the input type for a registered function.
|
|
64
|
+
* Falls back to TFallback if the function is not registered.
|
|
65
|
+
*/
|
|
66
|
+
export type FunctionInput<
|
|
67
|
+
TName extends string,
|
|
68
|
+
TFallback = unknown,
|
|
69
|
+
> = TName extends keyof ComputeFunctionRegistry
|
|
70
|
+
? ComputeFunctionRegistry[TName]['input']
|
|
71
|
+
: TFallback;
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Get the output type for a registered function.
|
|
75
|
+
* Falls back to TFallback if the function is not registered.
|
|
76
|
+
*/
|
|
77
|
+
export type FunctionOutput<
|
|
78
|
+
TName extends string,
|
|
79
|
+
TFallback = unknown,
|
|
80
|
+
> = TName extends keyof ComputeFunctionRegistry
|
|
81
|
+
? ComputeFunctionRegistry[TName]['output']
|
|
82
|
+
: TFallback;
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Type for a compute function based on registry or explicit types.
|
|
86
|
+
*/
|
|
87
|
+
export type ComputeFn<TInput, TOutput> = (input: TInput) => TOutput | Promise<TOutput>;
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Infer the compute function type for a registered function name.
|
|
91
|
+
*/
|
|
92
|
+
export type InferComputeFn<TName extends string> =
|
|
93
|
+
TName extends keyof ComputeFunctionRegistry
|
|
94
|
+
? ComputeFn<
|
|
95
|
+
ComputeFunctionRegistry[TName]['input'],
|
|
96
|
+
ComputeFunctionRegistry[TName]['output']
|
|
97
|
+
>
|
|
98
|
+
: ComputeFn<unknown, unknown>;
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Type helper for creating registry entries.
|
|
102
|
+
* Use this to define your function types more easily.
|
|
103
|
+
*
|
|
104
|
+
* @example
|
|
105
|
+
* ```ts
|
|
106
|
+
* declare module '@computekit/core' {
|
|
107
|
+
* interface ComputeFunctionRegistry {
|
|
108
|
+
* fibonacci: DefineFunction<number, number>;
|
|
109
|
+
* sum: DefineFunction<number[], number>;
|
|
110
|
+
* }
|
|
111
|
+
* }
|
|
112
|
+
* ```
|
|
113
|
+
*/
|
|
114
|
+
export type DefineFunction<TInput, TOutput> = {
|
|
115
|
+
input: TInput;
|
|
116
|
+
output: TOutput;
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Helper type for typing the register method.
|
|
121
|
+
* Provides proper type inference based on whether the function is in the registry.
|
|
122
|
+
*/
|
|
123
|
+
export type RegisterFn<
|
|
124
|
+
TName extends string,
|
|
125
|
+
TInput,
|
|
126
|
+
TOutput,
|
|
127
|
+
> = TName extends keyof ComputeFunctionRegistry
|
|
128
|
+
? ComputeFn<
|
|
129
|
+
ComputeFunctionRegistry[TName]['input'],
|
|
130
|
+
ComputeFunctionRegistry[TName]['output']
|
|
131
|
+
>
|
|
132
|
+
: ComputeFn<TInput, TOutput>;
|