@computekit/core 0.1.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 +820 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +270 -0
- package/dist/index.d.ts +270 -0
- package/dist/index.js +800 -0
- package/dist/index.js.map +1 -0
- package/dist/types-AaH5nWxG.d.cts +143 -0
- package/dist/types-AaH5nWxG.d.ts +143 -0
- package/dist/worker.cjs +148 -0
- package/dist/worker.cjs.map +1 -0
- package/dist/worker.d.cts +23 -0
- package/dist/worker.d.ts +23 -0
- package/dist/worker.js +143 -0
- package/dist/worker.js.map +1 -0
- package/package.json +61 -0
- package/src/cli.ts +246 -0
- package/src/index.test.ts +93 -0
- package/src/index.ts +232 -0
- package/src/pool.ts +591 -0
- package/src/types.ts +229 -0
- package/src/utils.ts +305 -0
- package/src/wasm.ts +205 -0
- package/src/worker/index.ts +11 -0
- package/src/worker/runtime.ts +149 -0
package/src/types.ts
ADDED
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ComputeKit Core Types
|
|
3
|
+
* Type definitions for the WASM + Worker toolkit
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/** Configuration options for ComputeKit */
|
|
7
|
+
export interface ComputeKitOptions {
|
|
8
|
+
/** Maximum number of workers in the pool (default: navigator.hardwareConcurrency || 4) */
|
|
9
|
+
maxWorkers?: number;
|
|
10
|
+
/** Timeout for compute operations in milliseconds (default: 30000) */
|
|
11
|
+
timeout?: number;
|
|
12
|
+
/** Enable debug logging (default: false) */
|
|
13
|
+
debug?: boolean;
|
|
14
|
+
/** Custom path to worker script */
|
|
15
|
+
workerPath?: string;
|
|
16
|
+
/** Whether to use SharedArrayBuffer when available (default: true) */
|
|
17
|
+
useSharedMemory?: boolean;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** Options for individual compute operations */
|
|
21
|
+
export interface ComputeOptions {
|
|
22
|
+
/** Timeout for this specific operation (overrides global) */
|
|
23
|
+
timeout?: number;
|
|
24
|
+
/** Transfer these ArrayBuffers to the worker (improves performance) */
|
|
25
|
+
transfer?: ArrayBuffer[];
|
|
26
|
+
/** Priority level for scheduling (0-10, higher = more priority) */
|
|
27
|
+
priority?: number;
|
|
28
|
+
/** Abort signal to cancel the operation */
|
|
29
|
+
signal?: AbortSignal;
|
|
30
|
+
/** Progress callback for long-running operations */
|
|
31
|
+
onProgress?: (progress: ComputeProgress) => void;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Progress information for compute operations */
|
|
35
|
+
export interface ComputeProgress {
|
|
36
|
+
/** Progress percentage (0-100) */
|
|
37
|
+
percent: number;
|
|
38
|
+
/** Current step/phase name */
|
|
39
|
+
phase?: string;
|
|
40
|
+
/** Estimated time remaining in milliseconds */
|
|
41
|
+
estimatedTimeRemaining?: number;
|
|
42
|
+
/** Any additional data from the compute function */
|
|
43
|
+
data?: unknown;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Result wrapper with metadata */
|
|
47
|
+
export interface ComputeResult<T> {
|
|
48
|
+
/** The computed result */
|
|
49
|
+
data: T;
|
|
50
|
+
/** Time taken in milliseconds */
|
|
51
|
+
duration: number;
|
|
52
|
+
/** Whether the result came from cache */
|
|
53
|
+
cached: boolean;
|
|
54
|
+
/** Worker ID that processed this */
|
|
55
|
+
workerId: string;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** Function definition for registration */
|
|
59
|
+
export interface ComputeFunction<TInput = unknown, TOutput = unknown> {
|
|
60
|
+
/** The compute function implementation */
|
|
61
|
+
fn: (input: TInput) => TOutput | Promise<TOutput>;
|
|
62
|
+
/** Optional WASM module to load */
|
|
63
|
+
wasmModule?: WebAssembly.Module | ArrayBuffer | string;
|
|
64
|
+
/** Whether this function supports progress reporting */
|
|
65
|
+
supportsProgress?: boolean;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** WASM module configuration */
|
|
69
|
+
export interface WasmModuleConfig {
|
|
70
|
+
/** Path to the WASM file or base64 encoded WASM */
|
|
71
|
+
source: string | ArrayBuffer;
|
|
72
|
+
/** Imports to provide to the WASM module */
|
|
73
|
+
imports?: WebAssembly.Imports;
|
|
74
|
+
/** Memory configuration */
|
|
75
|
+
memory?: {
|
|
76
|
+
initial: number;
|
|
77
|
+
maximum?: number;
|
|
78
|
+
shared?: boolean;
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** Worker message types */
|
|
83
|
+
export type WorkerMessageType =
|
|
84
|
+
| 'execute'
|
|
85
|
+
| 'result'
|
|
86
|
+
| 'error'
|
|
87
|
+
| 'progress'
|
|
88
|
+
| 'init'
|
|
89
|
+
| 'ready'
|
|
90
|
+
| 'terminate';
|
|
91
|
+
|
|
92
|
+
/** Base worker message */
|
|
93
|
+
export interface WorkerMessage<T = unknown> {
|
|
94
|
+
id: string;
|
|
95
|
+
type: WorkerMessageType;
|
|
96
|
+
payload?: T;
|
|
97
|
+
timestamp: number;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/** Execute message payload */
|
|
101
|
+
export interface ExecutePayload {
|
|
102
|
+
functionName: string;
|
|
103
|
+
input: unknown;
|
|
104
|
+
options?: ComputeOptions;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** Result message payload */
|
|
108
|
+
export interface ResultPayload<T = unknown> {
|
|
109
|
+
data: T;
|
|
110
|
+
duration: number;
|
|
111
|
+
transfer?: ArrayBuffer[];
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** Error message payload */
|
|
115
|
+
export interface ErrorPayload {
|
|
116
|
+
message: string;
|
|
117
|
+
stack?: string;
|
|
118
|
+
code?: string;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/** Progress message payload */
|
|
122
|
+
export interface ProgressPayload {
|
|
123
|
+
taskId: string;
|
|
124
|
+
progress: ComputeProgress;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/** Worker state */
|
|
128
|
+
export type WorkerState = 'idle' | 'busy' | 'error' | 'terminated';
|
|
129
|
+
|
|
130
|
+
/** Worker info */
|
|
131
|
+
export interface WorkerInfo {
|
|
132
|
+
id: string;
|
|
133
|
+
state: WorkerState;
|
|
134
|
+
currentTask?: string;
|
|
135
|
+
tasksCompleted: number;
|
|
136
|
+
errors: number;
|
|
137
|
+
createdAt: number;
|
|
138
|
+
lastActiveAt: number;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/** Pool statistics */
|
|
142
|
+
export interface PoolStats {
|
|
143
|
+
workers: WorkerInfo[];
|
|
144
|
+
totalWorkers: number;
|
|
145
|
+
activeWorkers: number;
|
|
146
|
+
idleWorkers: number;
|
|
147
|
+
queueLength: number;
|
|
148
|
+
tasksCompleted: number;
|
|
149
|
+
tasksFailed: number;
|
|
150
|
+
averageTaskDuration: number;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/** Event data for worker:created */
|
|
154
|
+
export interface WorkerCreatedEvent {
|
|
155
|
+
info: WorkerInfo;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/** Event data for worker:terminated */
|
|
159
|
+
export interface WorkerTerminatedEvent {
|
|
160
|
+
info: WorkerInfo;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/** Event data for worker:error */
|
|
164
|
+
export interface WorkerErrorEvent {
|
|
165
|
+
error: Error;
|
|
166
|
+
info: WorkerInfo;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/** Event data for task:start */
|
|
170
|
+
export interface TaskStartEvent {
|
|
171
|
+
taskId: string;
|
|
172
|
+
functionName: string;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/** Event data for task:complete */
|
|
176
|
+
export interface TaskCompleteEvent {
|
|
177
|
+
taskId: string;
|
|
178
|
+
duration: number;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/** Event data for task:error */
|
|
182
|
+
export interface TaskErrorEvent {
|
|
183
|
+
taskId: string;
|
|
184
|
+
error: Error;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/** Event data for task:progress */
|
|
188
|
+
export interface TaskProgressEvent {
|
|
189
|
+
taskId: string;
|
|
190
|
+
progress: ComputeProgress;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/** Events emitted by ComputeKit */
|
|
194
|
+
export type ComputeKitEvents = {
|
|
195
|
+
'worker:created': WorkerCreatedEvent;
|
|
196
|
+
'worker:terminated': WorkerTerminatedEvent;
|
|
197
|
+
'worker:error': WorkerErrorEvent;
|
|
198
|
+
'task:start': TaskStartEvent;
|
|
199
|
+
'task:complete': TaskCompleteEvent;
|
|
200
|
+
'task:error': TaskErrorEvent;
|
|
201
|
+
'task:progress': TaskProgressEvent;
|
|
202
|
+
[key: string]: unknown;
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
/** Type helper for compute function signatures */
|
|
206
|
+
export type ComputeFn<TInput, TOutput> = (
|
|
207
|
+
input: TInput,
|
|
208
|
+
options?: ComputeOptions
|
|
209
|
+
) => Promise<TOutput>;
|
|
210
|
+
|
|
211
|
+
/** Registry of compute functions */
|
|
212
|
+
export type ComputeRegistry = Map<string, ComputeFunction>;
|
|
213
|
+
|
|
214
|
+
/** Transferable types */
|
|
215
|
+
export type Transferable = ArrayBuffer | MessagePort | ImageBitmap | OffscreenCanvas;
|
|
216
|
+
|
|
217
|
+
/** Serializable types for worker communication */
|
|
218
|
+
export type Serializable =
|
|
219
|
+
| string
|
|
220
|
+
| number
|
|
221
|
+
| boolean
|
|
222
|
+
| null
|
|
223
|
+
| undefined
|
|
224
|
+
| Serializable[]
|
|
225
|
+
| { [key: string]: Serializable }
|
|
226
|
+
| ArrayBuffer
|
|
227
|
+
| ArrayBufferView
|
|
228
|
+
| Map<Serializable, Serializable>
|
|
229
|
+
| Set<Serializable>;
|
package/src/utils.ts
ADDED
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ComputeKit Utilities
|
|
3
|
+
* Helper functions for the WASM + Worker toolkit
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Generate a unique ID
|
|
8
|
+
*/
|
|
9
|
+
export function generateId(): string {
|
|
10
|
+
return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 11)}`;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Check if running in a Web Worker context
|
|
15
|
+
*/
|
|
16
|
+
export function isWorkerContext(): boolean {
|
|
17
|
+
return (
|
|
18
|
+
typeof self !== 'undefined' &&
|
|
19
|
+
typeof Window === 'undefined' &&
|
|
20
|
+
typeof self.postMessage === 'function'
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Check if running in a browser context
|
|
26
|
+
*/
|
|
27
|
+
export function isBrowserContext(): boolean {
|
|
28
|
+
return typeof window !== 'undefined' && typeof document !== 'undefined';
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Check if SharedArrayBuffer is available
|
|
33
|
+
*/
|
|
34
|
+
export function isSharedArrayBufferAvailable(): boolean {
|
|
35
|
+
try {
|
|
36
|
+
return typeof SharedArrayBuffer !== 'undefined';
|
|
37
|
+
} catch {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Check if WASM is supported
|
|
44
|
+
*/
|
|
45
|
+
export function isWasmSupported(): boolean {
|
|
46
|
+
try {
|
|
47
|
+
if (typeof WebAssembly === 'object') {
|
|
48
|
+
const module = new WebAssembly.Module(
|
|
49
|
+
Uint8Array.of(0x0, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00)
|
|
50
|
+
);
|
|
51
|
+
return module instanceof WebAssembly.Module;
|
|
52
|
+
}
|
|
53
|
+
} catch {
|
|
54
|
+
// WASM not supported
|
|
55
|
+
}
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Get the number of logical processors
|
|
61
|
+
*/
|
|
62
|
+
export function getHardwareConcurrency(): number {
|
|
63
|
+
if (typeof navigator !== 'undefined' && navigator.hardwareConcurrency) {
|
|
64
|
+
return navigator.hardwareConcurrency;
|
|
65
|
+
}
|
|
66
|
+
return 4; // Reasonable default
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Create a deferred promise
|
|
71
|
+
*/
|
|
72
|
+
export interface Deferred<T> {
|
|
73
|
+
promise: Promise<T>;
|
|
74
|
+
resolve: (value: T | PromiseLike<T>) => void;
|
|
75
|
+
reject: (reason?: unknown) => void;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function createDeferred<T>(): Deferred<T> {
|
|
79
|
+
let resolve!: (value: T | PromiseLike<T>) => void;
|
|
80
|
+
let reject!: (reason?: unknown) => void;
|
|
81
|
+
|
|
82
|
+
const promise = new Promise<T>((res, rej) => {
|
|
83
|
+
resolve = res;
|
|
84
|
+
reject = rej;
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
return { promise, resolve, reject };
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Create a timeout promise
|
|
92
|
+
*/
|
|
93
|
+
export function createTimeout(ms: number, message?: string): Promise<never> {
|
|
94
|
+
return new Promise((_, reject) => {
|
|
95
|
+
setTimeout(() => {
|
|
96
|
+
reject(new Error(message || `Operation timed out after ${ms}ms`));
|
|
97
|
+
}, ms);
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Race a promise against a timeout
|
|
103
|
+
*/
|
|
104
|
+
export async function withTimeout<T>(
|
|
105
|
+
promise: Promise<T>,
|
|
106
|
+
ms: number,
|
|
107
|
+
message?: string
|
|
108
|
+
): Promise<T> {
|
|
109
|
+
return Promise.race([promise, createTimeout(ms, message)]);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Detect transferable objects in data
|
|
114
|
+
*/
|
|
115
|
+
export function findTransferables(data: unknown): Transferable[] {
|
|
116
|
+
const transferables: Transferable[] = [];
|
|
117
|
+
const seen = new WeakSet();
|
|
118
|
+
|
|
119
|
+
function traverse(obj: unknown): void {
|
|
120
|
+
if (obj === null || typeof obj !== 'object') return;
|
|
121
|
+
if (seen.has(obj as object)) return;
|
|
122
|
+
seen.add(obj as object);
|
|
123
|
+
|
|
124
|
+
if (obj instanceof ArrayBuffer) {
|
|
125
|
+
transferables.push(obj);
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (ArrayBuffer.isView(obj)) {
|
|
130
|
+
transferables.push(obj.buffer);
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (obj instanceof MessagePort) {
|
|
135
|
+
transferables.push(obj);
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (typeof ImageBitmap !== 'undefined' && obj instanceof ImageBitmap) {
|
|
140
|
+
transferables.push(obj);
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (typeof OffscreenCanvas !== 'undefined' && obj instanceof OffscreenCanvas) {
|
|
145
|
+
transferables.push(obj);
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (Array.isArray(obj)) {
|
|
150
|
+
obj.forEach(traverse);
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (obj instanceof Map) {
|
|
155
|
+
obj.forEach((value, key) => {
|
|
156
|
+
traverse(key);
|
|
157
|
+
traverse(value);
|
|
158
|
+
});
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (obj instanceof Set) {
|
|
163
|
+
obj.forEach(traverse);
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
Object.values(obj).forEach(traverse);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
traverse(data);
|
|
171
|
+
return transferables;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Clone data, detaching transferables
|
|
176
|
+
*/
|
|
177
|
+
export function cloneForTransfer<T>(data: T): { data: T; transfer: Transferable[] } {
|
|
178
|
+
const transfer = findTransferables(data);
|
|
179
|
+
return { data, transfer };
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Create a typed event emitter
|
|
184
|
+
*/
|
|
185
|
+
export type EventHandler<T = unknown> = (data: T) => void;
|
|
186
|
+
|
|
187
|
+
export class EventEmitter<TEvents extends Record<string, unknown>> {
|
|
188
|
+
private handlers = new Map<keyof TEvents, Set<EventHandler>>();
|
|
189
|
+
|
|
190
|
+
on<K extends keyof TEvents>(event: K, handler: EventHandler<TEvents[K]>): () => void {
|
|
191
|
+
if (!this.handlers.has(event)) {
|
|
192
|
+
this.handlers.set(event, new Set());
|
|
193
|
+
}
|
|
194
|
+
this.handlers.get(event)!.add(handler as EventHandler);
|
|
195
|
+
|
|
196
|
+
// Return unsubscribe function
|
|
197
|
+
return () => this.off(event, handler);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
off<K extends keyof TEvents>(event: K, handler: EventHandler<TEvents[K]>): void {
|
|
201
|
+
this.handlers.get(event)?.delete(handler as EventHandler);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
emit<K extends keyof TEvents>(event: K, data: TEvents[K]): void {
|
|
205
|
+
this.handlers.get(event)?.forEach((handler) => {
|
|
206
|
+
try {
|
|
207
|
+
handler(data);
|
|
208
|
+
} catch (err) {
|
|
209
|
+
console.error(`Error in event handler for ${String(event)}:`, err);
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
removeAllListeners(event?: keyof TEvents): void {
|
|
215
|
+
if (event) {
|
|
216
|
+
this.handlers.delete(event);
|
|
217
|
+
} else {
|
|
218
|
+
this.handlers.clear();
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Simple LRU cache
|
|
225
|
+
*/
|
|
226
|
+
export class LRUCache<K, V> {
|
|
227
|
+
private cache = new Map<K, V>();
|
|
228
|
+
private maxSize: number;
|
|
229
|
+
|
|
230
|
+
constructor(maxSize: number = 100) {
|
|
231
|
+
this.maxSize = maxSize;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
get(key: K): V | undefined {
|
|
235
|
+
const value = this.cache.get(key);
|
|
236
|
+
if (value !== undefined) {
|
|
237
|
+
// Move to end (most recently used)
|
|
238
|
+
this.cache.delete(key);
|
|
239
|
+
this.cache.set(key, value);
|
|
240
|
+
}
|
|
241
|
+
return value;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
set(key: K, value: V): void {
|
|
245
|
+
if (this.cache.has(key)) {
|
|
246
|
+
this.cache.delete(key);
|
|
247
|
+
} else if (this.cache.size >= this.maxSize) {
|
|
248
|
+
// Delete oldest (first) entry
|
|
249
|
+
const firstKey = this.cache.keys().next().value;
|
|
250
|
+
if (firstKey !== undefined) {
|
|
251
|
+
this.cache.delete(firstKey);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
this.cache.set(key, value);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
has(key: K): boolean {
|
|
258
|
+
return this.cache.has(key);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
delete(key: K): boolean {
|
|
262
|
+
return this.cache.delete(key);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
clear(): void {
|
|
266
|
+
this.cache.clear();
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
get size(): number {
|
|
270
|
+
return this.cache.size;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Serialize function to string for worker
|
|
276
|
+
*/
|
|
277
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
|
278
|
+
export function serializeFunction(fn: Function): string {
|
|
279
|
+
return fn.toString();
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Logger utility
|
|
284
|
+
*/
|
|
285
|
+
export interface Logger {
|
|
286
|
+
debug(...args: unknown[]): void;
|
|
287
|
+
info(...args: unknown[]): void;
|
|
288
|
+
warn(...args: unknown[]): void;
|
|
289
|
+
error(...args: unknown[]): void;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
export function createLogger(prefix: string, enabled: boolean = false): Logger {
|
|
293
|
+
const noop = () => {};
|
|
294
|
+
const log = (level: string) =>
|
|
295
|
+
enabled ? (...args: unknown[]) => console.log(`[${prefix}:${level}]`, ...args) : noop;
|
|
296
|
+
|
|
297
|
+
return {
|
|
298
|
+
debug: log('debug'),
|
|
299
|
+
info: log('info'),
|
|
300
|
+
warn: enabled
|
|
301
|
+
? (...args: unknown[]) => console.warn(`[${prefix}:warn]`, ...args)
|
|
302
|
+
: noop,
|
|
303
|
+
error: (...args: unknown[]) => console.error(`[${prefix}:error]`, ...args),
|
|
304
|
+
};
|
|
305
|
+
}
|