Offload functions to worker threads with shared memory primitives for Node.js.
8
fork

Configure Feed

Select the types of activity you want to include in your feed.

chore: add prettier config and format codebase

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

+311 -108
+7
.prettierrc
··· 1 + { 2 + "singleQuote": true, 3 + "trailingComma": "all", 4 + "tabWidth": 2, 5 + "semi": true, 6 + "printWidth": 120 7 + }
+1 -3
examples/atomics/main.ts
··· 9 9 const pool = workerPool(4); 10 10 11 11 // Fire off 100 increments across 4 workers 12 - await Promise.all( 13 - Array.from({ length: 100 }, () => pool(increment(counter))), 14 - ); 12 + await Promise.all(Array.from({ length: 100 }, () => pool(increment(counter)))); 15 13 16 14 pool[Symbol.dispose](); 17 15
+1 -4
examples/non-blocking/main.ts
··· 18 18 19 19 const pool = workerPool(2); 20 20 const start = performance.now(); 21 - const [a, b] = await Promise.all([ 22 - pool(fibonacci(42)), 23 - pool(fibonacci(41)), 24 - ]); 21 + const [a, b] = await Promise.all([pool(fibonacci(42)), pool(fibonacci(41))]); 25 22 const elapsed = (performance.now() - start).toFixed(0); 26 23 pool[Symbol.dispose](); 27 24
+3 -1
examples/parallel-batch/heavy-work.ts
··· 3 3 export const heavyWork = mo(import.meta, (item: number): number => { 4 4 // Simulate CPU-bound work (~50ms per item) 5 5 const start = Date.now(); 6 - while (Date.now() - start < 50) { /* busy wait */ } 6 + while (Date.now() - start < 50) { 7 + /* busy wait */ 8 + } 7 9 return item * item; 8 10 });
+1 -8
examples/primes/main.ts
··· 4 4 5 5 import { isPrime } from './is-prime.ts'; 6 6 7 - const candidates = [ 8 - 999_999_937, 9 - 1_000_000_007, 10 - 1_000_000_009, 11 - 1_000_000_021, 12 - 1_000_000_033, 13 - 999_999_938, 14 - ]; 7 + const candidates = [999_999_937, 1_000_000_007, 1_000_000_009, 1_000_000_021, 1_000_000_033, 999_999_938]; 15 8 16 9 for (const n of candidates) { 17 10 const prime = await isPrime(n);
+49 -8
src/index.ts
··· 5 5 export type { Runner } from './runner.ts'; 6 6 export { 7 7 shared, 8 - int8, uint8, int16, uint16, int32, uint32, int64, uint64, bool, 9 - int8atomic, uint8atomic, int16atomic, uint16atomic, int32atomic, uint32atomic, int64atomic, uint64atomic, boolatomic, 10 - mutex, rwlock, 11 - bytes, string, 8 + int8, 9 + uint8, 10 + int16, 11 + uint16, 12 + int32, 13 + uint32, 14 + int64, 15 + uint64, 16 + bool, 17 + int8atomic, 18 + uint8atomic, 19 + int16atomic, 20 + uint16atomic, 21 + int32atomic, 22 + uint32atomic, 23 + int64atomic, 24 + uint64atomic, 25 + boolatomic, 26 + mutex, 27 + rwlock, 28 + bytes, 29 + string, 12 30 } from './shared/index.ts'; 13 31 export type { Loadable, Descriptor } from './shared/index.ts'; 14 32 export type { 15 - Int8, Uint8, Int16, Uint16, Int32, Uint32, Int64, Uint64, Bool, 16 - AtomicInt8, AtomicUint8, AtomicInt16, AtomicUint16, AtomicInt32, AtomicUint32, AtomicInt64, AtomicUint64, AtomicBool, 17 - Mutex, MutexGuard, RwLock, ReadGuard, WriteGuard, 18 - SharedStruct, Bytes, SharedString, Tuple, 33 + Int8, 34 + Uint8, 35 + Int16, 36 + Uint16, 37 + Int32, 38 + Uint32, 39 + Int64, 40 + Uint64, 41 + Bool, 42 + AtomicInt8, 43 + AtomicUint8, 44 + AtomicInt16, 45 + AtomicUint16, 46 + AtomicInt32, 47 + AtomicUint32, 48 + AtomicInt64, 49 + AtomicUint64, 50 + AtomicBool, 51 + Mutex, 52 + MutexGuard, 53 + RwLock, 54 + ReadGuard, 55 + WriteGuard, 56 + SharedStruct, 57 + Bytes, 58 + SharedString, 59 + Tuple, 19 60 } from './shared/index.ts';
+2 -5
src/mo.ts
··· 3 3 4 4 const counters = new Map<string, number>(); 5 5 6 - export function mo<A extends unknown[], R>( 7 - importMeta: ImportMeta, 8 - fn: (...args: A) => R, 9 - ): (...args: A) => Task<R> { 6 + export function mo<A extends unknown[], R>(importMeta: ImportMeta, fn: (...args: A) => R): (...args: A) => Task<R> { 10 7 const url = importMeta.url; 11 8 12 9 if (isModuleFrozen(url)) { 13 10 throw new Error( 14 11 `Cannot call mo() for ${url} after a task from this module has been dispatched. ` + 15 - 'All mo() calls must happen at module load time.', 12 + 'All mo() calls must happen at module load time.', 16 13 ); 17 14 } 18 15
+6 -1
src/shared/bytes.ts
··· 27 27 } 28 28 29 29 [SHARED](): { tag: string; buffer: SharedArrayBuffer; byteOffset: number; size: number } { 30 - return { tag: 'Bytes', buffer: this.view.buffer as SharedArrayBuffer, byteOffset: this.view.byteOffset, size: this.size }; 30 + return { 31 + tag: 'Bytes', 32 + buffer: this.view.buffer as SharedArrayBuffer, 33 + byteOffset: this.view.byteOffset, 34 + size: this.size, 35 + }; 31 36 } 32 37 }
+78 -13
src/shared/descriptors.ts
··· 40 40 export const int8: Descriptor<Int8> = makeDescriptor(() => new Int8(), Int8.byteSize, Int8.byteAlignment, Int8); 41 41 export const uint8: Descriptor<Uint8> = makeDescriptor(() => new Uint8(), Uint8.byteSize, Uint8.byteAlignment, Uint8); 42 42 export const int16: Descriptor<Int16> = makeDescriptor(() => new Int16(), Int16.byteSize, Int16.byteAlignment, Int16); 43 - export const uint16: Descriptor<Uint16> = makeDescriptor(() => new Uint16(), Uint16.byteSize, Uint16.byteAlignment, Uint16); 43 + export const uint16: Descriptor<Uint16> = makeDescriptor( 44 + () => new Uint16(), 45 + Uint16.byteSize, 46 + Uint16.byteAlignment, 47 + Uint16, 48 + ); 44 49 export const int32: Descriptor<Int32> = makeDescriptor(() => new Int32(), Int32.byteSize, Int32.byteAlignment, Int32); 45 - export const uint32: Descriptor<Uint32> = makeDescriptor(() => new Uint32(), Uint32.byteSize, Uint32.byteAlignment, Uint32); 50 + export const uint32: Descriptor<Uint32> = makeDescriptor( 51 + () => new Uint32(), 52 + Uint32.byteSize, 53 + Uint32.byteAlignment, 54 + Uint32, 55 + ); 46 56 export const int64: Descriptor<Int64> = makeDescriptor(() => new Int64(), Int64.byteSize, Int64.byteAlignment, Int64); 47 - export const uint64: Descriptor<Uint64> = makeDescriptor(() => new Uint64(), Uint64.byteSize, Uint64.byteAlignment, Uint64); 57 + export const uint64: Descriptor<Uint64> = makeDescriptor( 58 + () => new Uint64(), 59 + Uint64.byteSize, 60 + Uint64.byteAlignment, 61 + Uint64, 62 + ); 48 63 export const bool: Descriptor<Bool> = makeDescriptor(() => new Bool(), Bool.byteSize, Bool.byteAlignment, Bool); 49 64 50 - export const int8atomic: Descriptor<AtomicInt8> = makeDescriptor(() => new AtomicInt8(), AtomicInt8.byteSize, AtomicInt8.byteAlignment, AtomicInt8); 51 - export const uint8atomic: Descriptor<AtomicUint8> = makeDescriptor(() => new AtomicUint8(), AtomicUint8.byteSize, AtomicUint8.byteAlignment, AtomicUint8); 52 - export const int16atomic: Descriptor<AtomicInt16> = makeDescriptor(() => new AtomicInt16(), AtomicInt16.byteSize, AtomicInt16.byteAlignment, AtomicInt16); 53 - export const uint16atomic: Descriptor<AtomicUint16> = makeDescriptor(() => new AtomicUint16(), AtomicUint16.byteSize, AtomicUint16.byteAlignment, AtomicUint16); 54 - export const int32atomic: Descriptor<AtomicInt32> = makeDescriptor(() => new AtomicInt32(), AtomicInt32.byteSize, AtomicInt32.byteAlignment, AtomicInt32); 55 - export const uint32atomic: Descriptor<AtomicUint32> = makeDescriptor(() => new AtomicUint32(), AtomicUint32.byteSize, AtomicUint32.byteAlignment, AtomicUint32); 56 - export const int64atomic: Descriptor<AtomicInt64> = makeDescriptor(() => new AtomicInt64(), AtomicInt64.byteSize, AtomicInt64.byteAlignment, AtomicInt64); 57 - export const uint64atomic: Descriptor<AtomicUint64> = makeDescriptor(() => new AtomicUint64(), AtomicUint64.byteSize, AtomicUint64.byteAlignment, AtomicUint64); 58 - export const boolatomic: Descriptor<AtomicBool> = makeDescriptor(() => new AtomicBool(), AtomicBool.byteSize, AtomicBool.byteAlignment, AtomicBool); 65 + export const int8atomic: Descriptor<AtomicInt8> = makeDescriptor( 66 + () => new AtomicInt8(), 67 + AtomicInt8.byteSize, 68 + AtomicInt8.byteAlignment, 69 + AtomicInt8, 70 + ); 71 + export const uint8atomic: Descriptor<AtomicUint8> = makeDescriptor( 72 + () => new AtomicUint8(), 73 + AtomicUint8.byteSize, 74 + AtomicUint8.byteAlignment, 75 + AtomicUint8, 76 + ); 77 + export const int16atomic: Descriptor<AtomicInt16> = makeDescriptor( 78 + () => new AtomicInt16(), 79 + AtomicInt16.byteSize, 80 + AtomicInt16.byteAlignment, 81 + AtomicInt16, 82 + ); 83 + export const uint16atomic: Descriptor<AtomicUint16> = makeDescriptor( 84 + () => new AtomicUint16(), 85 + AtomicUint16.byteSize, 86 + AtomicUint16.byteAlignment, 87 + AtomicUint16, 88 + ); 89 + export const int32atomic: Descriptor<AtomicInt32> = makeDescriptor( 90 + () => new AtomicInt32(), 91 + AtomicInt32.byteSize, 92 + AtomicInt32.byteAlignment, 93 + AtomicInt32, 94 + ); 95 + export const uint32atomic: Descriptor<AtomicUint32> = makeDescriptor( 96 + () => new AtomicUint32(), 97 + AtomicUint32.byteSize, 98 + AtomicUint32.byteAlignment, 99 + AtomicUint32, 100 + ); 101 + export const int64atomic: Descriptor<AtomicInt64> = makeDescriptor( 102 + () => new AtomicInt64(), 103 + AtomicInt64.byteSize, 104 + AtomicInt64.byteAlignment, 105 + AtomicInt64, 106 + ); 107 + export const uint64atomic: Descriptor<AtomicUint64> = makeDescriptor( 108 + () => new AtomicUint64(), 109 + AtomicUint64.byteSize, 110 + AtomicUint64.byteAlignment, 111 + AtomicUint64, 112 + ); 113 + export const boolatomic: Descriptor<AtomicBool> = makeDescriptor( 114 + () => new AtomicBool(), 115 + AtomicBool.byteSize, 116 + AtomicBool.byteAlignment, 117 + AtomicBool, 118 + ); 59 119 60 120 export const mutex: Descriptor<Mutex> = makeDescriptor(() => new Mutex(), Mutex.byteSize, Mutex.byteAlignment, Mutex); 61 - export const rwlock: Descriptor<RwLock> = makeDescriptor(() => new RwLock(), RwLock.byteSize, RwLock.byteAlignment, RwLock); 121 + export const rwlock: Descriptor<RwLock> = makeDescriptor( 122 + () => new RwLock(), 123 + RwLock.byteSize, 124 + RwLock.byteAlignment, 125 + RwLock, 126 + ); 62 127 63 128 export interface BytesDescriptor extends Bytes { 64 129 byteSize: number;
+22 -4
src/shared/index.ts
··· 1 1 export type { Loadable } from './loadable.ts'; 2 2 export { 3 - int8, uint8, int16, uint16, int32, uint32, int64, uint64, bool, 4 - int8atomic, uint8atomic, int16atomic, uint16atomic, int32atomic, uint32atomic, int64atomic, uint64atomic, boolatomic, 5 - mutex, rwlock, 6 - bytes, string, 3 + int8, 4 + uint8, 5 + int16, 6 + uint16, 7 + int32, 8 + uint32, 9 + int64, 10 + uint64, 11 + bool, 12 + int8atomic, 13 + uint8atomic, 14 + int16atomic, 15 + uint16atomic, 16 + int32atomic, 17 + uint32atomic, 18 + int64atomic, 19 + uint64atomic, 20 + boolatomic, 21 + mutex, 22 + rwlock, 23 + bytes, 24 + string, 7 25 } from './descriptors.ts'; 8 26 export type { Descriptor, BytesDescriptor, StringDescriptor } from './descriptors.ts'; 9 27 export { shared } from './shared.ts';
+14 -2
src/shared/reconstruct.ts
··· 25 25 const serializedElements = (data.elements as any[]).map((el: any) => serializeStructField(el)); 26 26 return { __shared__: 'Tuple', elements: serializedElements }; 27 27 } 28 - return { __shared__: data.tag, buffer: data.buffer, byteOffset: data.byteOffset, ...(data.size !== undefined && { size: data.size }), ...(data.maxBytes !== undefined && { maxBytes: data.maxBytes }) }; 28 + return { 29 + __shared__: data.tag, 30 + buffer: data.buffer, 31 + byteOffset: data.byteOffset, 32 + ...(data.size !== undefined && { size: data.size }), 33 + ...(data.maxBytes !== undefined && { maxBytes: data.maxBytes }), 34 + }; 29 35 } 30 36 return arg; 31 37 } ··· 38 44 } 39 45 return { __shared__: 'SharedStruct', fields: serializedFields }; 40 46 } 41 - return { __shared__: data.tag, buffer: data.buffer, byteOffset: data.byteOffset, ...(data.size !== undefined && { size: data.size }), ...(data.maxBytes !== undefined && { maxBytes: data.maxBytes }) }; 47 + return { 48 + __shared__: data.tag, 49 + buffer: data.buffer, 50 + byteOffset: data.byteOffset, 51 + ...(data.size !== undefined && { size: data.size }), 52 + ...(data.maxBytes !== undefined && { maxBytes: data.maxBytes }), 53 + }; 42 54 } 43 55 44 56 export function deserializeArg(arg: unknown): unknown {
+71 -18
src/shared/shared.ts
··· 77 77 78 78 type ArrayEntry = ArrayLeafEntry | ArrayStructEntry | ArrayTupleEntry; 79 79 80 - function collectLeaves(schema: Record<string, unknown>, path: string[], leaves: LeafEntry[], cursor: { offset: number }): void { 80 + function collectLeaves( 81 + schema: Record<string, unknown>, 82 + path: string[], 83 + leaves: LeafEntry[], 84 + cursor: { offset: number }, 85 + ): void { 81 86 for (const key in schema) { 82 87 const value = schema[key]; 83 88 if (isStringDescriptor(value)) { 84 89 cursor.offset = align(cursor.offset, value.byteAlignment); 85 - leaves.push({ path: [...path, key], descriptor: value as unknown as Descriptor<unknown>, offset: cursor.offset, stringMaxBytes: value._maxBytes }); 90 + leaves.push({ 91 + path: [...path, key], 92 + descriptor: value as unknown as Descriptor<unknown>, 93 + offset: cursor.offset, 94 + stringMaxBytes: value._maxBytes, 95 + }); 86 96 cursor.offset += value.byteSize; 87 97 } else if (isBytesDescriptor(value)) { 88 98 cursor.offset = align(cursor.offset, value.byteAlignment); 89 - leaves.push({ path: [...path, key], descriptor: value as unknown as Descriptor<unknown>, offset: cursor.offset, bytesSize: value._size }); 99 + leaves.push({ 100 + path: [...path, key], 101 + descriptor: value as unknown as Descriptor<unknown>, 102 + offset: cursor.offset, 103 + bytesSize: value._size, 104 + }); 90 105 cursor.offset += value.byteSize; 91 106 } else if (isDescriptor(value)) { 92 107 cursor.offset = align(cursor.offset, value.byteAlignment); ··· 112 127 for (const element of schema) { 113 128 if (isStringDescriptor(element)) { 114 129 cursor.offset = align(cursor.offset, element.byteAlignment); 115 - leaves.push({ path: [], descriptor: element as unknown as Descriptor<unknown>, offset: cursor.offset, stringMaxBytes: element._maxBytes }); 130 + leaves.push({ 131 + path: [], 132 + descriptor: element as unknown as Descriptor<unknown>, 133 + offset: cursor.offset, 134 + stringMaxBytes: element._maxBytes, 135 + }); 116 136 cursor.offset += element.byteSize; 117 137 } else if (isBytesDescriptor(element)) { 118 138 cursor.offset = align(cursor.offset, element.byteAlignment); 119 - leaves.push({ path: [], descriptor: element as unknown as Descriptor<unknown>, offset: cursor.offset, bytesSize: element._size }); 139 + leaves.push({ 140 + path: [], 141 + descriptor: element as unknown as Descriptor<unknown>, 142 + offset: cursor.offset, 143 + bytesSize: element._size, 144 + }); 120 145 cursor.offset += element.byteSize; 121 146 } else if (isDescriptor(element)) { 122 147 cursor.offset = align(cursor.offset, element.byteAlignment); ··· 143 168 for (const element of schema) { 144 169 if (isStringDescriptor(element)) { 145 170 cursor.offset = align(cursor.offset, element.byteAlignment); 146 - entries.push({ type: 'leaf', descriptor: element as unknown as Descriptor<unknown>, offset: cursor.offset, stringMaxBytes: element._maxBytes }); 171 + entries.push({ 172 + type: 'leaf', 173 + descriptor: element as unknown as Descriptor<unknown>, 174 + offset: cursor.offset, 175 + stringMaxBytes: element._maxBytes, 176 + }); 147 177 cursor.offset += element.byteSize; 148 178 } else if (isBytesDescriptor(element)) { 149 179 cursor.offset = align(cursor.offset, element.byteAlignment); 150 - entries.push({ type: 'leaf', descriptor: element as unknown as Descriptor<unknown>, offset: cursor.offset, bytesSize: element._size }); 180 + entries.push({ 181 + type: 'leaf', 182 + descriptor: element as unknown as Descriptor<unknown>, 183 + offset: cursor.offset, 184 + bytesSize: element._size, 185 + }); 151 186 cursor.offset += element.byteSize; 152 187 } else if (isDescriptor(element)) { 153 188 cursor.offset = align(cursor.offset, element.byteAlignment); ··· 199 234 return new Tuple(elements as any); 200 235 } 201 236 202 - function buildStructTree(schema: Record<string, unknown>, leaves: LeafEntry[], buffer: SharedArrayBuffer, leafIndex: { i: number }): SharedStruct<any> { 237 + function buildStructTree( 238 + schema: Record<string, unknown>, 239 + leaves: LeafEntry[], 240 + buffer: SharedArrayBuffer, 241 + leafIndex: { i: number }, 242 + ): SharedStruct<any> { 203 243 const fields: Record<string, any> = {}; 204 244 for (const key in schema) { 205 245 const value = schema[key]; ··· 234 274 for (const element of schema) { 235 275 if (isStringDescriptor(element)) { 236 276 const leaf = leaves[leafIndex.i++]; 237 - entries.push({ type: 'leaf', descriptor: leaf.descriptor, offset: leaf.offset, stringMaxBytes: leaf.stringMaxBytes }); 277 + entries.push({ 278 + type: 'leaf', 279 + descriptor: leaf.descriptor, 280 + offset: leaf.offset, 281 + stringMaxBytes: leaf.stringMaxBytes, 282 + }); 238 283 } else if (isBytesDescriptor(element)) { 239 284 const leaf = leaves[leafIndex.i++]; 240 285 entries.push({ type: 'leaf', descriptor: leaf.descriptor, offset: leaf.offset, bytesSize: leaf.bytesSize }); ··· 294 339 295 340 // Maps a schema value to its resolved instance type 296 341 type ResolveField<T> = 297 - T extends Descriptor<infer R> ? R : 298 - T extends BytesDescriptor ? Bytes : 299 - T extends StringDescriptor ? SharedString : 300 - T extends readonly unknown[] ? ResolveTuple<T> : 301 - T extends Record<string, unknown> ? ResolveStruct<T> : 302 - T extends number ? Int32 : 303 - T extends bigint ? Int64 : 304 - T extends boolean ? Bool : 305 - never; 342 + T extends Descriptor<infer R> 343 + ? R 344 + : T extends BytesDescriptor 345 + ? Bytes 346 + : T extends StringDescriptor 347 + ? SharedString 348 + : T extends readonly unknown[] 349 + ? ResolveTuple<T> 350 + : T extends Record<string, unknown> 351 + ? ResolveStruct<T> 352 + : T extends number 353 + ? Int32 354 + : T extends bigint 355 + ? Int64 356 + : T extends boolean 357 + ? Bool 358 + : never; 306 359 307 360 type ResolveStruct<T extends Record<string, unknown>> = SharedStruct<{ [K in keyof T]: ResolveField<T[K]> }>; 308 361 type ResolveTuple<T extends readonly unknown[]> = Tuple<ResolveTupleElements<T>>;
+6 -9
test/fixtures/sync.ts
··· 17 17 }, 18 18 ); 19 19 20 - export const rwlockRead = mo( 21 - import.meta, 22 - async (rwlock: RwLock, counter: AtomicInt32): Promise<number> => { 23 - const guard = await rwlock.readLock(); 24 - const val = counter.load(); 25 - rwlock.readUnlock(); 26 - return val; 27 - }, 28 - ); 20 + export const rwlockRead = mo(import.meta, async (rwlock: RwLock, counter: AtomicInt32): Promise<number> => { 21 + const guard = await rwlock.readLock(); 22 + const val = counter.load(); 23 + rwlock.readUnlock(); 24 + return val; 25 + });
+1 -6
test/pool.test.ts
··· 17 17 it('handles concurrent calls across pool workers', async () => { 18 18 const run = workerPool(2); 19 19 try { 20 - const results = await Promise.all([ 21 - run(double(1)), 22 - run(double(2)), 23 - run(double(3)), 24 - run(double(4)), 25 - ]); 20 + const results = await Promise.all([run(double(1)), run(double(2)), run(double(3)), run(double(4))]); 26 21 assert.deepEqual(results, [2, 4, 6, 8]); 27 22 } finally { 28 23 run[Symbol.dispose]();
+4 -13
test/shared/cross-worker.test.ts
··· 9 9 const counter = int32atomic(); 10 10 const pool = workerPool(2); 11 11 try { 12 - await Promise.all([ 13 - pool(atomicAdd(counter, 10)), 14 - pool(atomicAdd(counter, 20)), 15 - ]); 12 + await Promise.all([pool(atomicAdd(counter, 10)), pool(atomicAdd(counter, 20))]); 16 13 assert.equal(counter.load(), 30); 17 14 } finally { 18 15 pool[Symbol.dispose](); ··· 24 21 const counter = int32atomic(); 25 22 const pool = workerPool(2); 26 23 try { 27 - await Promise.all([ 28 - pool(mutexIncrement(m, counter, 100)), 29 - pool(mutexIncrement(m, counter, 100)), 30 - ]); 24 + await Promise.all([pool(mutexIncrement(m, counter, 100)), pool(mutexIncrement(m, counter, 100))]); 31 25 assert.equal(counter.load(), 200); 32 26 } finally { 33 27 pool[Symbol.dispose](); ··· 40 34 counter.store(42); 41 35 const pool = workerPool(2); 42 36 try { 43 - const [a, b] = await Promise.all([ 44 - pool(rwlockRead(rw, counter)), 45 - pool(rwlockRead(rw, counter)), 46 - ]); 37 + const [a, b] = await Promise.all([pool(rwlockRead(rw, counter)), pool(rwlockRead(rw, counter))]); 47 38 assert.equal(a, 42); 48 39 assert.equal(b, 42); 49 40 } finally { ··· 108 99 109 100 const pool = workerPool(1); 110 101 try { 111 - const result = await pool(readValue(b)) as Uint8Array; 102 + const result = (await pool(readValue(b))) as Uint8Array; 112 103 assert.deepEqual([...result], [1, 2, 3, 4]); 113 104 } finally { 114 105 pool[Symbol.dispose]();
+20 -3
test/shared/descriptors.test.ts
··· 1 1 import { describe, it } from 'node:test'; 2 2 import assert from 'node:assert/strict'; 3 3 import { 4 - int8, uint8, int16, uint16, int32, uint32, int64, uint64, bool, 5 - int8atomic, uint8atomic, int16atomic, uint16atomic, int32atomic, uint32atomic, int64atomic, uint64atomic, boolatomic, 6 - mutex, rwlock, 4 + int8, 5 + uint8, 6 + int16, 7 + uint16, 8 + int32, 9 + uint32, 10 + int64, 11 + uint64, 12 + bool, 13 + int8atomic, 14 + uint8atomic, 15 + int16atomic, 16 + uint16atomic, 17 + int32atomic, 18 + uint32atomic, 19 + int64atomic, 20 + uint64atomic, 21 + boolatomic, 22 + mutex, 23 + rwlock, 7 24 } from 'moroutine'; 8 25 9 26 describe('descriptors', () => {
+1 -4
test/shared/mutex.test.ts
··· 39 39 40 40 await Promise.all([task(1), task(2)]); 41 41 // Each task should push its id twice in a row (not interleaved) 42 - assert.ok( 43 - (order[0] === order[1] && order[2] === order[3]), 44 - `Expected non-interleaved order, got: ${order}`, 45 - ); 42 + assert.ok(order[0] === order[1] && order[2] === order[3], `Expected non-interleaved order, got: ${order}`); 46 43 }); 47 44 48 45 it('self-allocates', () => {
+4 -1
test/shared/shared.test.ts
··· 36 36 size: { w: int32, h: int32 }, 37 37 }); 38 38 rect.store({ pos: { x: 1, y: 2 }, size: { w: 100, h: 50 } }); 39 - assert.deepEqual(rect.load(), { pos: { x: 1, y: 2 }, size: { w: 100, h: 50 } }); 39 + assert.deepEqual(rect.load(), { 40 + pos: { x: 1, y: 2 }, 41 + size: { w: 100, h: 50 }, 42 + }); 40 43 }); 41 44 42 45 it('nested struct fields accessible', () => {
+12 -3
test/shared/tuple.test.ts
··· 41 41 }); 42 42 43 43 it('tuple with struct elements', () => { 44 - const t = shared([{ x: int32, y: int32 }, { x: int32, y: int32 }]); 45 - t.store([{ x: 1, y: 2 }, { x: 3, y: 4 }]); 46 - assert.deepEqual(t.load(), [{ x: 1, y: 2 }, { x: 3, y: 4 }]); 44 + const t = shared([ 45 + { x: int32, y: int32 }, 46 + { x: int32, y: int32 }, 47 + ]); 48 + t.store([ 49 + { x: 1, y: 2 }, 50 + { x: 3, y: 4 }, 51 + ]); 52 + assert.deepEqual(t.load(), [ 53 + { x: 1, y: 2 }, 54 + { x: 3, y: 4 }, 55 + ]); 47 56 }); 48 57 49 58 it('struct containing tuple field', () => {
+8 -2
test/transfer.test.ts
··· 7 7 it('transfers an ArrayBuffer to a worker (zero-copy)', async () => { 8 8 const buf = new ArrayBuffer(4); 9 9 const view = new Uint8Array(buf); 10 - view[0] = 1; view[1] = 2; view[2] = 3; view[3] = 4; 10 + view[0] = 1; 11 + view[1] = 2; 12 + view[2] = 3; 13 + view[3] = 4; 11 14 12 15 const pool = workerPool(1); 13 16 try { ··· 50 53 it('without transfer, buffer is copied (not detached)', async () => { 51 54 const buf = new ArrayBuffer(4); 52 55 const view = new Uint8Array(buf); 53 - view[0] = 1; view[1] = 2; view[2] = 3; view[3] = 4; 56 + view[0] = 1; 57 + view[1] = 2; 58 + view[2] = 3; 59 + view[3] = 4; 54 60 55 61 const pool = workerPool(1); 56 62 try {