@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/src/wasm.ts ADDED
@@ -0,0 +1,205 @@
1
+ /**
2
+ * ComputeKit WASM Loader
3
+ * Utilities for loading and managing WebAssembly modules
4
+ */
5
+
6
+ import type { WasmModuleConfig } from './types';
7
+ import { createLogger, LRUCache } from './utils';
8
+
9
+ const logger = createLogger('ComputeKit:WASM');
10
+
11
+ /** Cached WASM modules */
12
+ const moduleCache = new LRUCache<string, WebAssembly.Module>(10);
13
+
14
+ /** Cached WASM instances */
15
+ const instanceCache = new LRUCache<string, WebAssembly.Instance>(10);
16
+
17
+ /**
18
+ * Load a WASM module from various sources
19
+ */
20
+ export async function loadWasmModule(
21
+ source: string | ArrayBuffer | Uint8Array
22
+ ): Promise<WebAssembly.Module> {
23
+ // Check cache for string sources
24
+ if (typeof source === 'string') {
25
+ const cached = moduleCache.get(source);
26
+ if (cached) {
27
+ logger.debug('Using cached WASM module:', source);
28
+ return cached;
29
+ }
30
+ }
31
+
32
+ let bytes: ArrayBuffer | Uint8Array;
33
+
34
+ if (typeof source === 'string') {
35
+ if (source.startsWith('data:')) {
36
+ // Base64 encoded WASM
37
+ const base64 = source.split(',')[1];
38
+ bytes = Uint8Array.from(atob(base64), (c) => c.charCodeAt(0));
39
+ } else {
40
+ // URL to WASM file
41
+ logger.debug('Fetching WASM from:', source);
42
+ const response = await fetch(source);
43
+
44
+ if (!response.ok) {
45
+ throw new Error(`Failed to fetch WASM: ${response.statusText}`);
46
+ }
47
+
48
+ // Use streaming compilation if available
49
+ if (WebAssembly.compileStreaming) {
50
+ const module = await WebAssembly.compileStreaming(response);
51
+ moduleCache.set(source, module);
52
+ return module;
53
+ }
54
+
55
+ bytes = await response.arrayBuffer();
56
+ }
57
+ } else {
58
+ bytes = source;
59
+ }
60
+
61
+ // Compile the module
62
+ const module = await WebAssembly.compile(bytes as BufferSource);
63
+
64
+ if (typeof source === 'string') {
65
+ moduleCache.set(source, module);
66
+ }
67
+
68
+ return module;
69
+ }
70
+
71
+ /**
72
+ * Instantiate a WASM module with imports
73
+ */
74
+ export async function instantiateWasm(
75
+ module: WebAssembly.Module,
76
+ imports: WebAssembly.Imports = {}
77
+ ): Promise<WebAssembly.Instance> {
78
+ return WebAssembly.instantiate(module, imports);
79
+ }
80
+
81
+ /**
82
+ * Load and instantiate a WASM module in one step
83
+ */
84
+ export async function loadAndInstantiate(
85
+ config: WasmModuleConfig
86
+ ): Promise<{ module: WebAssembly.Module; instance: WebAssembly.Instance }> {
87
+ const { source, imports = {}, memory } = config;
88
+
89
+ // Create memory if specified
90
+ const wasmImports: WebAssembly.Imports = { ...imports };
91
+ if (memory) {
92
+ wasmImports.env = {
93
+ ...wasmImports.env,
94
+ memory: new WebAssembly.Memory({
95
+ initial: memory.initial,
96
+ maximum: memory.maximum,
97
+ shared: memory.shared,
98
+ }),
99
+ };
100
+ }
101
+
102
+ const module = await loadWasmModule(source);
103
+ const instance = await instantiateWasm(module, wasmImports);
104
+
105
+ return { module, instance };
106
+ }
107
+
108
+ /**
109
+ * Create a WASM module from AssemblyScript-compiled bytes
110
+ */
111
+ export async function loadAssemblyScript(
112
+ source: string | ArrayBuffer,
113
+ imports: WebAssembly.Imports = {}
114
+ ): Promise<{
115
+ module: WebAssembly.Module;
116
+ instance: WebAssembly.Instance;
117
+ exports: Record<string, unknown>;
118
+ }> {
119
+ // Default AssemblyScript imports
120
+ const defaultImports: WebAssembly.Imports = {
121
+ env: {
122
+ abort: (_message: number, fileName: number, line: number, column: number) => {
123
+ console.error(`AssemblyScript abort at ${fileName}:${line}:${column}`);
124
+ },
125
+ seed: () => Date.now(),
126
+ ...((imports.env as object) || {}),
127
+ },
128
+ ...imports,
129
+ };
130
+
131
+ const { module, instance } = await loadAndInstantiate({
132
+ source,
133
+ imports: defaultImports,
134
+ });
135
+
136
+ return {
137
+ module,
138
+ instance,
139
+ exports: instance.exports as Record<string, unknown>,
140
+ };
141
+ }
142
+
143
+ /**
144
+ * Helper to wrap WASM exports for easier use
145
+ */
146
+ export function wrapWasmExports<T extends Record<string, unknown>>(
147
+ instance: WebAssembly.Instance
148
+ ): T {
149
+ return instance.exports as T;
150
+ }
151
+
152
+ /**
153
+ * Create a typed array view into WASM memory
154
+ */
155
+ export function getMemoryView<T extends ArrayBufferView>(
156
+ memory: WebAssembly.Memory,
157
+ ArrayType: new (buffer: ArrayBuffer, byteOffset?: number, length?: number) => T,
158
+ offset: number = 0,
159
+ length?: number
160
+ ): T {
161
+ return new ArrayType(memory.buffer, offset, length);
162
+ }
163
+
164
+ /**
165
+ * Copy data to WASM memory
166
+ */
167
+ export function copyToWasmMemory(
168
+ memory: WebAssembly.Memory,
169
+ data: ArrayBufferView,
170
+ offset: number
171
+ ): void {
172
+ const view = new Uint8Array(memory.buffer);
173
+ const source = new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
174
+ view.set(source, offset);
175
+ }
176
+
177
+ /**
178
+ * Copy data from WASM memory
179
+ */
180
+ export function copyFromWasmMemory(
181
+ memory: WebAssembly.Memory,
182
+ offset: number,
183
+ length: number
184
+ ): Uint8Array {
185
+ const view = new Uint8Array(memory.buffer, offset, length);
186
+ return new Uint8Array(view); // Copy to detach from WASM memory
187
+ }
188
+
189
+ /**
190
+ * Clear module caches
191
+ */
192
+ export function clearWasmCache(): void {
193
+ moduleCache.clear();
194
+ instanceCache.clear();
195
+ }
196
+
197
+ /**
198
+ * Get cache statistics
199
+ */
200
+ export function getWasmCacheStats(): { modules: number; instances: number } {
201
+ return {
202
+ modules: moduleCache.size,
203
+ instances: instanceCache.size,
204
+ };
205
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * ComputeKit Worker Entry Point
3
+ * Export worker utilities for custom worker implementations
4
+ */
5
+
6
+ export {
7
+ reportProgress,
8
+ registerFunction,
9
+ initWorkerRuntime,
10
+ functionRegistry,
11
+ } from './runtime';
@@ -0,0 +1,149 @@
1
+ /**
2
+ * ComputeKit Worker Runtime
3
+ * Code that runs inside Web Workers
4
+ */
5
+
6
+ import type {
7
+ WorkerMessage,
8
+ ExecutePayload,
9
+ ResultPayload,
10
+ ErrorPayload,
11
+ ComputeProgress,
12
+ } from '../types';
13
+
14
+ import { generateId, findTransferables } from '../utils';
15
+
16
+ /** Registry of compute functions available in the worker */
17
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
18
+ const functionRegistry = new Map<string, Function>();
19
+
20
+ /** Current task context for progress reporting */
21
+ let currentTaskId: string | null = null;
22
+
23
+ /**
24
+ * Report progress from within a compute function
25
+ */
26
+ export function reportProgress(progress: Partial<ComputeProgress>): void {
27
+ if (!currentTaskId) {
28
+ console.warn('reportProgress called outside of compute function');
29
+ return;
30
+ }
31
+
32
+ const message: WorkerMessage = {
33
+ id: generateId(),
34
+ type: 'progress',
35
+ payload: {
36
+ taskId: currentTaskId,
37
+ progress: {
38
+ percent: progress.percent ?? 0,
39
+ phase: progress.phase,
40
+ estimatedTimeRemaining: progress.estimatedTimeRemaining,
41
+ data: progress.data,
42
+ },
43
+ },
44
+ timestamp: Date.now(),
45
+ };
46
+
47
+ self.postMessage(message);
48
+ }
49
+
50
+ /**
51
+ * Register a compute function in the worker
52
+ */
53
+ export function registerFunction(
54
+ name: string,
55
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
56
+ fn: Function
57
+ ): void {
58
+ functionRegistry.set(name, fn);
59
+ }
60
+
61
+ /**
62
+ * Execute a registered function
63
+ */
64
+ async function executeFunction(functionName: string, input: unknown): Promise<unknown> {
65
+ const fn = functionRegistry.get(functionName);
66
+
67
+ if (!fn) {
68
+ throw new Error(`Function "${functionName}" not found in worker`);
69
+ }
70
+
71
+ return fn(input);
72
+ }
73
+
74
+ /**
75
+ * Handle incoming messages from the main thread
76
+ */
77
+ async function handleMessage(event: MessageEvent<WorkerMessage>): Promise<void> {
78
+ const { id, type, payload } = event.data;
79
+
80
+ if (type === 'execute') {
81
+ const { functionName, input } = payload as ExecutePayload;
82
+ const startTime = performance.now();
83
+
84
+ // Set current task for progress reporting
85
+ currentTaskId = id;
86
+
87
+ try {
88
+ const result = await executeFunction(functionName, input);
89
+ const duration = performance.now() - startTime;
90
+
91
+ // Find transferable objects in result
92
+ const transfer = findTransferables(result);
93
+
94
+ const response: WorkerMessage<ResultPayload> = {
95
+ id,
96
+ type: 'result',
97
+ payload: {
98
+ data: result,
99
+ duration,
100
+ },
101
+ timestamp: Date.now(),
102
+ };
103
+
104
+ self.postMessage(response, transfer as Transferable[]);
105
+ } catch (err) {
106
+ const error = err instanceof Error ? err : new Error(String(err));
107
+
108
+ const response: WorkerMessage<ErrorPayload> = {
109
+ id,
110
+ type: 'error',
111
+ payload: {
112
+ message: error.message,
113
+ stack: error.stack,
114
+ },
115
+ timestamp: Date.now(),
116
+ };
117
+
118
+ self.postMessage(response);
119
+ } finally {
120
+ currentTaskId = null;
121
+ }
122
+ } else if (type === 'init') {
123
+ // Handle initialization if needed
124
+ const response: WorkerMessage = {
125
+ id,
126
+ type: 'ready',
127
+ timestamp: Date.now(),
128
+ };
129
+ self.postMessage(response);
130
+ }
131
+ }
132
+
133
+ /**
134
+ * Initialize the worker runtime
135
+ */
136
+ export function initWorkerRuntime(): void {
137
+ self.onmessage = handleMessage;
138
+
139
+ // Signal that worker is ready
140
+ const readyMessage: WorkerMessage = {
141
+ id: generateId(),
142
+ type: 'ready',
143
+ timestamp: Date.now(),
144
+ };
145
+ self.postMessage(readyMessage);
146
+ }
147
+
148
+ // Export for use in worker entry point
149
+ export { functionRegistry };