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: SharedStruct composable Loadable facade

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

Devin Ivy b1733ff5 21c8fcec

+118
+1
src/index.ts
··· 29 29 ReadGuard, 30 30 WriteGuard, 31 31 slab, 32 + SharedStruct, 32 33 } from './shared/index.ts';
+1
src/shared/index.ts
··· 20 20 export { Mutex, MutexGuard } from './mutex.ts'; 21 21 export { RwLock, ReadGuard, WriteGuard } from './rwlock.ts'; 22 22 export { slab } from './slab.ts'; 23 + export { SharedStruct } from './shared-struct.ts';
+40
src/shared/shared-struct.ts
··· 1 + import type { Loadable } from './loadable.ts'; 2 + 3 + type FieldValues<T extends Record<string, Loadable<any>>> = { 4 + [K in keyof T]: T[K] extends Loadable<infer V> ? V : never; 5 + }; 6 + 7 + const SHARED = Symbol.for('moroutine.shared'); 8 + 9 + export class SharedStruct<T extends Record<string, Loadable<any>>> implements Loadable<FieldValues<T>> { 10 + readonly fields: T; 11 + 12 + constructor(fields: T) { 13 + this.fields = fields; 14 + } 15 + 16 + load(): FieldValues<T> { 17 + const result = {} as FieldValues<T>; 18 + for (const key in this.fields) { 19 + (result as any)[key] = this.fields[key].load(); 20 + } 21 + return result; 22 + } 23 + 24 + store(values: FieldValues<T>): void { 25 + for (const key in this.fields) { 26 + this.fields[key].store((values as any)[key]); 27 + } 28 + } 29 + 30 + [SHARED](): { tag: string; fields: Record<string, unknown> } { 31 + const serializedFields: Record<string, unknown> = {}; 32 + for (const key in this.fields) { 33 + const field = this.fields[key]; 34 + if (typeof field === 'object' && field !== null && SHARED in field) { 35 + serializedFields[key] = (field as any)[SHARED](); 36 + } 37 + } 38 + return { tag: 'SharedStruct', fields: serializedFields }; 39 + } 40 + }
+76
test/shared/shared-struct.test.ts
··· 1 + import { describe, it } from 'node:test'; 2 + import assert from 'node:assert/strict'; 3 + import { SharedStruct, Int32, Uint8, Bool, slab } from 'moroutine'; 4 + 5 + describe('SharedStruct', () => { 6 + it('load returns all field values', () => { 7 + const [x, y] = slab(Int32, Int32); 8 + const point = new SharedStruct({ x, y }); 9 + assert.deepEqual(point.load(), { x: 0, y: 0 }); 10 + }); 11 + 12 + it('store writes all field values', () => { 13 + const [x, y] = slab(Int32, Int32); 14 + const point = new SharedStruct({ x, y }); 15 + point.store({ x: 10, y: 20 }); 16 + assert.deepEqual(point.load(), { x: 10, y: 20 }); 17 + }); 18 + 19 + it('works with mixed types', () => { 20 + const [x, y, health, alive] = slab(Int32, Int32, Uint8, Bool); 21 + const state = new SharedStruct({ x, y, health, alive }); 22 + state.store({ x: 1, y: 2, health: 100, alive: true }); 23 + assert.deepEqual(state.load(), { x: 1, y: 2, health: 100, alive: true }); 24 + }); 25 + 26 + it('works with independently created fields', () => { 27 + const pos = new SharedStruct({ x: new Int32(), y: new Int32() }); 28 + pos.store({ x: 42, y: 99 }); 29 + assert.deepEqual(pos.load(), { x: 42, y: 99 }); 30 + }); 31 + 32 + it('supports nested structs', () => { 33 + const [sx, sy, sw, sh] = slab(Int32, Int32, Int32, Int32); 34 + const position = new SharedStruct({ x: sx, y: sy }); 35 + const size = new SharedStruct({ w: sw, h: sh }); 36 + const rect = new SharedStruct({ position, size }); 37 + 38 + rect.store({ position: { x: 1, y: 2 }, size: { w: 100, h: 50 } }); 39 + assert.deepEqual(rect.load(), { position: { x: 1, y: 2 }, size: { w: 100, h: 50 } }); 40 + }); 41 + 42 + it('inner struct mutation is visible through outer struct', () => { 43 + const [x, y] = slab(Int32, Int32); 44 + const pos = new SharedStruct({ x, y }); 45 + const entity = new SharedStruct({ pos, alive: new Bool() }); 46 + 47 + pos.store({ x: 10, y: 20 }); 48 + assert.deepEqual(entity.load(), { pos: { x: 10, y: 20 }, alive: false }); 49 + }); 50 + 51 + it('exposes fields for direct access', () => { 52 + const [x, y] = slab(Int32, Int32); 53 + const point = new SharedStruct({ x, y }); 54 + point.fields.x.store(42); 55 + assert.equal(point.fields.y.load(), 0); 56 + assert.deepEqual(point.load(), { x: 42, y: 0 }); 57 + }); 58 + 59 + it('nested fields accessible via fields property', () => { 60 + const [x, y, w, h] = slab(Int32, Int32, Int32, Int32); 61 + const pos = new SharedStruct({ x, y }); 62 + const rect = new SharedStruct({ pos, w, h }); 63 + 64 + rect.fields.pos.fields.x.store(99); 65 + rect.fields.w.store(200); 66 + assert.deepEqual(rect.load(), { pos: { x: 99, y: 0 }, w: 200, h: 0 }); 67 + }); 68 + 69 + it('is itself Loadable (can be nested)', () => { 70 + const inner = new SharedStruct({ v: new Int32() }); 71 + const outer = new SharedStruct({ inner, flag: new Bool() }); 72 + 73 + outer.store({ inner: { v: 42 }, flag: true }); 74 + assert.deepEqual(outer.load(), { inner: { v: 42 }, flag: true }); 75 + }); 76 + });