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.

feat: non-atomic shared primitives (Int8-Uint64, Bool)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

+559
+8
src/index.ts
··· 5 5 export type { Runner } from './runner.ts'; 6 6 export type { Loadable } from './shared/index.ts'; 7 7 export { 8 + Int8, 9 + Uint8, 10 + Int16, 11 + Uint16, 8 12 Int32, 13 + Uint32, 14 + Int64, 15 + Uint64, 16 + Bool, 9 17 AtomicBool, 10 18 AtomicInt8, 11 19 AtomicUint8,
+28
src/shared/bool.ts
··· 1 + import { registerSync } from './reconstruct.ts'; 2 + import type { Loadable } from './loadable.ts'; 3 + 4 + export class Bool implements Loadable<boolean> { 5 + static readonly byteSize = 1; 6 + static readonly byteAlignment = 1; 7 + private readonly view: Uint8Array; 8 + 9 + constructor(buffer?: SharedArrayBuffer, byteOffset?: number) { 10 + const buf = buffer ?? new SharedArrayBuffer(1); 11 + const offset = byteOffset ?? 0; 12 + this.view = new Uint8Array(buf, offset, 1); 13 + } 14 + 15 + load(): boolean { 16 + return this.view[0] !== 0; 17 + } 18 + 19 + store(value: boolean): void { 20 + this.view[0] = value ? 1 : 0; 21 + } 22 + 23 + [Symbol.for('moroutine.shared')](): { tag: string; buffer: SharedArrayBuffer; byteOffset: number } { 24 + return { tag: 'Bool', buffer: this.view.buffer as SharedArrayBuffer, byteOffset: this.view.byteOffset }; 25 + } 26 + } 27 + 28 + registerSync('Bool', Bool);
+8
src/shared/index.ts
··· 1 1 export type { Loadable } from './loadable.ts'; 2 + export { Int8 } from './int8.ts'; 3 + export { Uint8 } from './uint8.ts'; 4 + export { Int16 } from './int16.ts'; 5 + export { Uint16 } from './uint16.ts'; 2 6 export { Int32 } from './int32.ts'; 7 + export { Uint32 } from './uint32.ts'; 8 + export { Int64 } from './int64.ts'; 9 + export { Uint64 } from './uint64.ts'; 10 + export { Bool } from './bool.ts'; 3 11 export { AtomicBool } from './atomic-bool.ts'; 4 12 export { AtomicInt8 } from './atomic-int8.ts'; 5 13 export { AtomicUint8 } from './atomic-uint8.ts';
+28
src/shared/int16.ts
··· 1 + import { registerSync } from './reconstruct.ts'; 2 + import type { Loadable } from './loadable.ts'; 3 + 4 + export class Int16 implements Loadable<number> { 5 + static readonly byteSize = 2; 6 + static readonly byteAlignment = 2; 7 + private readonly view: Int16Array; 8 + 9 + constructor(buffer?: SharedArrayBuffer, byteOffset?: number) { 10 + const buf = buffer ?? new SharedArrayBuffer(2); 11 + const offset = byteOffset ?? 0; 12 + this.view = new Int16Array(buf, offset, 1); 13 + } 14 + 15 + load(): number { 16 + return this.view[0]; 17 + } 18 + 19 + store(value: number): void { 20 + this.view[0] = value; 21 + } 22 + 23 + [Symbol.for('moroutine.shared')](): { tag: string; buffer: SharedArrayBuffer; byteOffset: number } { 24 + return { tag: 'Int16', buffer: this.view.buffer as SharedArrayBuffer, byteOffset: this.view.byteOffset }; 25 + } 26 + } 27 + 28 + registerSync('Int16', Int16);
+28
src/shared/int64.ts
··· 1 + import { registerSync } from './reconstruct.ts'; 2 + import type { Loadable } from './loadable.ts'; 3 + 4 + export class Int64 implements Loadable<bigint> { 5 + static readonly byteSize = 8; 6 + static readonly byteAlignment = 8; 7 + private readonly view: BigInt64Array; 8 + 9 + constructor(buffer?: SharedArrayBuffer, byteOffset?: number) { 10 + const buf = buffer ?? new SharedArrayBuffer(8); 11 + const offset = byteOffset ?? 0; 12 + this.view = new BigInt64Array(buf, offset, 1); 13 + } 14 + 15 + load(): bigint { 16 + return this.view[0]; 17 + } 18 + 19 + store(value: bigint): void { 20 + this.view[0] = value; 21 + } 22 + 23 + [Symbol.for('moroutine.shared')](): { tag: string; buffer: SharedArrayBuffer; byteOffset: number } { 24 + return { tag: 'Int64', buffer: this.view.buffer as SharedArrayBuffer, byteOffset: this.view.byteOffset }; 25 + } 26 + } 27 + 28 + registerSync('Int64', Int64);
+28
src/shared/int8.ts
··· 1 + import { registerSync } from './reconstruct.ts'; 2 + import type { Loadable } from './loadable.ts'; 3 + 4 + export class Int8 implements Loadable<number> { 5 + static readonly byteSize = 1; 6 + static readonly byteAlignment = 1; 7 + private readonly view: Int8Array; 8 + 9 + constructor(buffer?: SharedArrayBuffer, byteOffset?: number) { 10 + const buf = buffer ?? new SharedArrayBuffer(1); 11 + const offset = byteOffset ?? 0; 12 + this.view = new Int8Array(buf, offset, 1); 13 + } 14 + 15 + load(): number { 16 + return this.view[0]; 17 + } 18 + 19 + store(value: number): void { 20 + this.view[0] = value; 21 + } 22 + 23 + [Symbol.for('moroutine.shared')](): { tag: string; buffer: SharedArrayBuffer; byteOffset: number } { 24 + return { tag: 'Int8', buffer: this.view.buffer as SharedArrayBuffer, byteOffset: this.view.byteOffset }; 25 + } 26 + } 27 + 28 + registerSync('Int8', Int8);
+28
src/shared/uint16.ts
··· 1 + import { registerSync } from './reconstruct.ts'; 2 + import type { Loadable } from './loadable.ts'; 3 + 4 + export class Uint16 implements Loadable<number> { 5 + static readonly byteSize = 2; 6 + static readonly byteAlignment = 2; 7 + private readonly view: Uint16Array; 8 + 9 + constructor(buffer?: SharedArrayBuffer, byteOffset?: number) { 10 + const buf = buffer ?? new SharedArrayBuffer(2); 11 + const offset = byteOffset ?? 0; 12 + this.view = new Uint16Array(buf, offset, 1); 13 + } 14 + 15 + load(): number { 16 + return this.view[0]; 17 + } 18 + 19 + store(value: number): void { 20 + this.view[0] = value; 21 + } 22 + 23 + [Symbol.for('moroutine.shared')](): { tag: string; buffer: SharedArrayBuffer; byteOffset: number } { 24 + return { tag: 'Uint16', buffer: this.view.buffer as SharedArrayBuffer, byteOffset: this.view.byteOffset }; 25 + } 26 + } 27 + 28 + registerSync('Uint16', Uint16);
+28
src/shared/uint32.ts
··· 1 + import { registerSync } from './reconstruct.ts'; 2 + import type { Loadable } from './loadable.ts'; 3 + 4 + export class Uint32 implements Loadable<number> { 5 + static readonly byteSize = 4; 6 + static readonly byteAlignment = 4; 7 + private readonly view: Uint32Array; 8 + 9 + constructor(buffer?: SharedArrayBuffer, byteOffset?: number) { 10 + const buf = buffer ?? new SharedArrayBuffer(4); 11 + const offset = byteOffset ?? 0; 12 + this.view = new Uint32Array(buf, offset, 1); 13 + } 14 + 15 + load(): number { 16 + return this.view[0]; 17 + } 18 + 19 + store(value: number): void { 20 + this.view[0] = value; 21 + } 22 + 23 + [Symbol.for('moroutine.shared')](): { tag: string; buffer: SharedArrayBuffer; byteOffset: number } { 24 + return { tag: 'Uint32', buffer: this.view.buffer as SharedArrayBuffer, byteOffset: this.view.byteOffset }; 25 + } 26 + } 27 + 28 + registerSync('Uint32', Uint32);
+28
src/shared/uint64.ts
··· 1 + import { registerSync } from './reconstruct.ts'; 2 + import type { Loadable } from './loadable.ts'; 3 + 4 + export class Uint64 implements Loadable<bigint> { 5 + static readonly byteSize = 8; 6 + static readonly byteAlignment = 8; 7 + private readonly view: BigUint64Array; 8 + 9 + constructor(buffer?: SharedArrayBuffer, byteOffset?: number) { 10 + const buf = buffer ?? new SharedArrayBuffer(8); 11 + const offset = byteOffset ?? 0; 12 + this.view = new BigUint64Array(buf, offset, 1); 13 + } 14 + 15 + load(): bigint { 16 + return this.view[0]; 17 + } 18 + 19 + store(value: bigint): void { 20 + this.view[0] = value; 21 + } 22 + 23 + [Symbol.for('moroutine.shared')](): { tag: string; buffer: SharedArrayBuffer; byteOffset: number } { 24 + return { tag: 'Uint64', buffer: this.view.buffer as SharedArrayBuffer, byteOffset: this.view.byteOffset }; 25 + } 26 + } 27 + 28 + registerSync('Uint64', Uint64);
+28
src/shared/uint8.ts
··· 1 + import { registerSync } from './reconstruct.ts'; 2 + import type { Loadable } from './loadable.ts'; 3 + 4 + export class Uint8 implements Loadable<number> { 5 + static readonly byteSize = 1; 6 + static readonly byteAlignment = 1; 7 + private readonly view: Uint8Array; 8 + 9 + constructor(buffer?: SharedArrayBuffer, byteOffset?: number) { 10 + const buf = buffer ?? new SharedArrayBuffer(1); 11 + const offset = byteOffset ?? 0; 12 + this.view = new Uint8Array(buf, offset, 1); 13 + } 14 + 15 + load(): number { 16 + return this.view[0]; 17 + } 18 + 19 + store(value: number): void { 20 + this.view[0] = value; 21 + } 22 + 23 + [Symbol.for('moroutine.shared')](): { tag: string; buffer: SharedArrayBuffer; byteOffset: number } { 24 + return { tag: 'Uint8', buffer: this.view.buffer as SharedArrayBuffer, byteOffset: this.view.byteOffset }; 25 + } 26 + } 27 + 28 + registerSync('Uint8', Uint8);
+53
test/shared/bool.test.ts
··· 1 + import { describe, it } from 'node:test'; 2 + import assert from 'node:assert/strict'; 3 + import { Bool } from 'moroutine'; 4 + 5 + describe('Bool', () => { 6 + it('self-allocates and initializes to false', () => { 7 + const v = new Bool(); 8 + assert.equal(v.load(), false); 9 + }); 10 + 11 + it('stores and loads true', () => { 12 + const v = new Bool(); 13 + v.store(true); 14 + assert.equal(v.load(), true); 15 + }); 16 + 17 + it('stores and loads false', () => { 18 + const v = new Bool(); 19 + v.store(true); 20 + v.store(false); 21 + assert.equal(v.load(), false); 22 + }); 23 + 24 + it('round-trips true/false', () => { 25 + const v = new Bool(); 26 + v.store(true); 27 + assert.equal(v.load(), true); 28 + v.store(false); 29 + assert.equal(v.load(), false); 30 + }); 31 + 32 + it('accepts an external buffer and byteOffset', () => { 33 + const buffer = new SharedArrayBuffer(8); 34 + const v = new Bool(buffer, 1); 35 + v.store(true); 36 + assert.equal(new Uint8Array(buffer, 1, 1)[0], 1); 37 + }); 38 + 39 + it('defaults byteOffset to 0', () => { 40 + const buffer = new SharedArrayBuffer(1); 41 + const v = new Bool(buffer); 42 + v.store(true); 43 + assert.equal(new Uint8Array(buffer)[0], 1); 44 + }); 45 + 46 + it('exposes static byteSize of 1', () => { 47 + assert.equal(Bool.byteSize, 1); 48 + }); 49 + 50 + it('exposes static byteAlignment of 1', () => { 51 + assert.equal(Bool.byteAlignment, 1); 52 + }); 53 + });
+38
test/shared/int16.test.ts
··· 1 + import { describe, it } from 'node:test'; 2 + import assert from 'node:assert/strict'; 3 + import { Int16 } from 'moroutine'; 4 + 5 + describe('Int16', () => { 6 + it('self-allocates and initializes to zero', () => { 7 + const v = new Int16(); 8 + assert.equal(v.load(), 0); 9 + }); 10 + 11 + it('stores and loads a value', () => { 12 + const v = new Int16(); 13 + v.store(42); 14 + assert.equal(v.load(), 42); 15 + }); 16 + 17 + it('accepts an external buffer and byteOffset', () => { 18 + const buffer = new SharedArrayBuffer(16); 19 + const v = new Int16(buffer, 2); 20 + v.store(99); 21 + assert.equal(new Int16Array(buffer, 2, 1)[0], 99); 22 + }); 23 + 24 + it('defaults byteOffset to 0', () => { 25 + const buffer = new SharedArrayBuffer(2); 26 + const v = new Int16(buffer); 27 + v.store(7); 28 + assert.equal(new Int16Array(buffer)[0], 7); 29 + }); 30 + 31 + it('exposes static byteSize of 2', () => { 32 + assert.equal(Int16.byteSize, 2); 33 + }); 34 + 35 + it('exposes static byteAlignment of 2', () => { 36 + assert.equal(Int16.byteAlignment, 2); 37 + }); 38 + });
+38
test/shared/int64.test.ts
··· 1 + import { describe, it } from 'node:test'; 2 + import assert from 'node:assert/strict'; 3 + import { Int64 } from 'moroutine'; 4 + 5 + describe('Int64', () => { 6 + it('self-allocates and initializes to zero', () => { 7 + const v = new Int64(); 8 + assert.equal(v.load(), 0n); 9 + }); 10 + 11 + it('stores and loads a value', () => { 12 + const v = new Int64(); 13 + v.store(99n); 14 + assert.equal(v.load(), 99n); 15 + }); 16 + 17 + it('accepts an external buffer and byteOffset', () => { 18 + const buffer = new SharedArrayBuffer(16); 19 + const v = new Int64(buffer, 8); 20 + v.store(42n); 21 + assert.equal(new BigInt64Array(buffer, 8, 1)[0], 42n); 22 + }); 23 + 24 + it('defaults byteOffset to 0', () => { 25 + const buffer = new SharedArrayBuffer(8); 26 + const v = new Int64(buffer); 27 + v.store(7n); 28 + assert.equal(new BigInt64Array(buffer)[0], 7n); 29 + }); 30 + 31 + it('exposes static byteSize of 8', () => { 32 + assert.equal(Int64.byteSize, 8); 33 + }); 34 + 35 + it('exposes static byteAlignment of 8', () => { 36 + assert.equal(Int64.byteAlignment, 8); 37 + }); 38 + });
+38
test/shared/int8.test.ts
··· 1 + import { describe, it } from 'node:test'; 2 + import assert from 'node:assert/strict'; 3 + import { Int8 } from 'moroutine'; 4 + 5 + describe('Int8', () => { 6 + it('self-allocates and initializes to zero', () => { 7 + const v = new Int8(); 8 + assert.equal(v.load(), 0); 9 + }); 10 + 11 + it('stores and loads a value', () => { 12 + const v = new Int8(); 13 + v.store(42); 14 + assert.equal(v.load(), 42); 15 + }); 16 + 17 + it('accepts an external buffer and byteOffset', () => { 18 + const buffer = new SharedArrayBuffer(8); 19 + const v = new Int8(buffer, 1); 20 + v.store(99); 21 + assert.equal(new Int8Array(buffer, 1, 1)[0], 99); 22 + }); 23 + 24 + it('defaults byteOffset to 0', () => { 25 + const buffer = new SharedArrayBuffer(1); 26 + const v = new Int8(buffer); 27 + v.store(7); 28 + assert.equal(new Int8Array(buffer)[0], 7); 29 + }); 30 + 31 + it('exposes static byteSize of 1', () => { 32 + assert.equal(Int8.byteSize, 1); 33 + }); 34 + 35 + it('exposes static byteAlignment of 1', () => { 36 + assert.equal(Int8.byteAlignment, 1); 37 + }); 38 + });
+38
test/shared/uint16.test.ts
··· 1 + import { describe, it } from 'node:test'; 2 + import assert from 'node:assert/strict'; 3 + import { Uint16 } from 'moroutine'; 4 + 5 + describe('Uint16', () => { 6 + it('self-allocates and initializes to zero', () => { 7 + const v = new Uint16(); 8 + assert.equal(v.load(), 0); 9 + }); 10 + 11 + it('stores and loads a value', () => { 12 + const v = new Uint16(); 13 + v.store(42); 14 + assert.equal(v.load(), 42); 15 + }); 16 + 17 + it('accepts an external buffer and byteOffset', () => { 18 + const buffer = new SharedArrayBuffer(16); 19 + const v = new Uint16(buffer, 2); 20 + v.store(99); 21 + assert.equal(new Uint16Array(buffer, 2, 1)[0], 99); 22 + }); 23 + 24 + it('defaults byteOffset to 0', () => { 25 + const buffer = new SharedArrayBuffer(2); 26 + const v = new Uint16(buffer); 27 + v.store(7); 28 + assert.equal(new Uint16Array(buffer)[0], 7); 29 + }); 30 + 31 + it('exposes static byteSize of 2', () => { 32 + assert.equal(Uint16.byteSize, 2); 33 + }); 34 + 35 + it('exposes static byteAlignment of 2', () => { 36 + assert.equal(Uint16.byteAlignment, 2); 37 + }); 38 + });
+38
test/shared/uint32.test.ts
··· 1 + import { describe, it } from 'node:test'; 2 + import assert from 'node:assert/strict'; 3 + import { Uint32 } from 'moroutine'; 4 + 5 + describe('Uint32', () => { 6 + it('self-allocates and initializes to zero', () => { 7 + const v = new Uint32(); 8 + assert.equal(v.load(), 0); 9 + }); 10 + 11 + it('stores and loads a value', () => { 12 + const v = new Uint32(); 13 + v.store(42); 14 + assert.equal(v.load(), 42); 15 + }); 16 + 17 + it('accepts an external buffer and byteOffset', () => { 18 + const buffer = new SharedArrayBuffer(16); 19 + const v = new Uint32(buffer, 4); 20 + v.store(99); 21 + assert.equal(new Uint32Array(buffer, 4, 1)[0], 99); 22 + }); 23 + 24 + it('defaults byteOffset to 0', () => { 25 + const buffer = new SharedArrayBuffer(4); 26 + const v = new Uint32(buffer); 27 + v.store(7); 28 + assert.equal(new Uint32Array(buffer)[0], 7); 29 + }); 30 + 31 + it('exposes static byteSize of 4', () => { 32 + assert.equal(Uint32.byteSize, 4); 33 + }); 34 + 35 + it('exposes static byteAlignment of 4', () => { 36 + assert.equal(Uint32.byteAlignment, 4); 37 + }); 38 + });
+38
test/shared/uint64.test.ts
··· 1 + import { describe, it } from 'node:test'; 2 + import assert from 'node:assert/strict'; 3 + import { Uint64 } from 'moroutine'; 4 + 5 + describe('Uint64', () => { 6 + it('self-allocates and initializes to zero', () => { 7 + const v = new Uint64(); 8 + assert.equal(v.load(), 0n); 9 + }); 10 + 11 + it('stores and loads a value', () => { 12 + const v = new Uint64(); 13 + v.store(99n); 14 + assert.equal(v.load(), 99n); 15 + }); 16 + 17 + it('accepts an external buffer and byteOffset', () => { 18 + const buffer = new SharedArrayBuffer(16); 19 + const v = new Uint64(buffer, 8); 20 + v.store(42n); 21 + assert.equal(new BigUint64Array(buffer, 8, 1)[0], 42n); 22 + }); 23 + 24 + it('defaults byteOffset to 0', () => { 25 + const buffer = new SharedArrayBuffer(8); 26 + const v = new Uint64(buffer); 27 + v.store(7n); 28 + assert.equal(new BigUint64Array(buffer)[0], 7n); 29 + }); 30 + 31 + it('exposes static byteSize of 8', () => { 32 + assert.equal(Uint64.byteSize, 8); 33 + }); 34 + 35 + it('exposes static byteAlignment of 8', () => { 36 + assert.equal(Uint64.byteAlignment, 8); 37 + }); 38 + });
+38
test/shared/uint8.test.ts
··· 1 + import { describe, it } from 'node:test'; 2 + import assert from 'node:assert/strict'; 3 + import { Uint8 } from 'moroutine'; 4 + 5 + describe('Uint8', () => { 6 + it('self-allocates and initializes to zero', () => { 7 + const v = new Uint8(); 8 + assert.equal(v.load(), 0); 9 + }); 10 + 11 + it('stores and loads a value', () => { 12 + const v = new Uint8(); 13 + v.store(42); 14 + assert.equal(v.load(), 42); 15 + }); 16 + 17 + it('accepts an external buffer and byteOffset', () => { 18 + const buffer = new SharedArrayBuffer(8); 19 + const v = new Uint8(buffer, 1); 20 + v.store(99); 21 + assert.equal(new Uint8Array(buffer, 1, 1)[0], 99); 22 + }); 23 + 24 + it('defaults byteOffset to 0', () => { 25 + const buffer = new SharedArrayBuffer(1); 26 + const v = new Uint8(buffer); 27 + v.store(7); 28 + assert.equal(new Uint8Array(buffer)[0], 7); 29 + }); 30 + 31 + it('exposes static byteSize of 1', () => { 32 + assert.equal(Uint8.byteSize, 1); 33 + }); 34 + 35 + it('exposes static byteAlignment of 1', () => { 36 + assert.equal(Uint8.byteAlignment, 1); 37 + }); 38 + });