···2233> Offload functions to worker threads with shared memory primitives for Node.js.
4455+## Installation
66+77+```sh
88+npm install moroutine
99+```
1010+1111+Requires Node.js v24+.
1212+513## Quick Start
614715```ts
1616+// main.ts
1717+import { isPrime } from './is-prime.ts';
1818+1919+const result = await isPrime(999_999_937);
2020+console.log(result); // true
2121+```
2222+2323+```ts
824// is-prime.ts
925import { mo } from 'moroutine';
1026···1733});
1834```
19352020-```ts
2121-// main.ts
2222-import { workers } from 'moroutine';
2323-import { isPrime } from './is-prime.ts';
2424-2525-using run = workers(4);
2626-const results = await run([isPrime(999_999_937), isPrime(1_000_000_007)]);
2727-console.log(results); // [true, true]
2828-```
2929-3036Define a function with `mo()` in its own module, then import and run it on a worker pool. Moroutine modules must be side-effect free — workers import them to find the registered functions.
31373238## Core API
···46524753### `workers(size)`
48544949-Creates a pool of worker threads. Returns a `Runner` that dispatches tasks with round-robin scheduling. Disposable via `using` or `[Symbol.dispose]()`.
5555+Creates a pool of worker threads. Returns a `Runner` that dispatches tasks with round-robin scheduling. Disposable via `using` or `[Symbol.dispose]()`. Defaults to `os.availableParallelism()` workers when `size` is omitted.
50565157```ts
5258import { workers } from 'moroutine';
···6874const result = await add(3, 4); // runs on a dedicated worker for `add`
6975```
70767777+### Task-Args
7878+7979+Pass a task as an argument to another task. The result is resolved on the worker and cached, so it never crosses back to the main thread. This is useful for non-transferable context like a database connection.
8080+8181+```ts
8282+// db.ts
8383+import { DatabaseSync } from 'node:sqlite';
8484+import { mo } from 'moroutine';
8585+8686+export const openDb = mo(import.meta, (filename: string): DatabaseSync => {
8787+ return new DatabaseSync(filename);
8888+});
8989+9090+export const exec = mo(import.meta, (db: DatabaseSync, sql: string): void => {
9191+ db.exec(sql);
9292+});
9393+9494+export const query = mo(import.meta, (db: DatabaseSync, sql: string): unknown[] => {
9595+ return db.prepare(sql).all();
9696+});
9797+```
9898+9999+```ts
100100+import { workers } from 'moroutine';
101101+import { openDb, exec, query } from './db.ts';
102102+103103+const db = openDb(':memory:');
104104+105105+{
106106+ using run = workers(1);
107107+ await run(exec(db, `CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)`));
108108+ await run(exec(db, `INSERT INTO users (name) VALUES ('Alice')`));
109109+ const rows = await run(query(db, 'SELECT * FROM users')); // [{ id: 1, name: 'Alice' }]
110110+}
111111+```
112112+113113+`openDb()` returns a `Task<DatabaseSync>`, and `exec()`/`query()` accept it in place of `DatabaseSync`. The database is opened once on the worker and reused for every subsequent call — the main thread never touches it.
114114+71115## Shared Memory
7211673117### Descriptors and `shared()`
···136180const buf = bytes(32); // fixed 32-byte buffer
137181buf.store(new Uint8Array(32)); // exact length required
138182buf.load(); // Readonly<Uint8Array> view
139139-buf.view[0] = 0xff; // direct mutable access
183183+buf.view[0] = 0xff; // direct mutable access
140184141185const name = string(64); // UTF-8, max 64 bytes
142186name.store('hello');
···151195shared(0) // Int32 initialized to 0
152196shared(true) // Bool initialized to true
153197shared(0n) // Int64 initialized to 0n
154154-shared({ x: 10, y: 20 }) // struct with Int32 fields
198198+shared({ x: 10, y: 20 }) // struct with Int32 fields
155199```
156200157201### Locks
···275319```
276320277321```ts
278278-const data = channel(generate(100));
322322+const ch = channel(generate(100));
279323280324{
281325 using run = workers(4);
282326 const [a, b, c, d] = await run([
283283- process(data),
284284- process(data),
285285- process(data),
286286- process(data),
327327+ process(ch),
328328+ process(ch),
329329+ process(ch),
330330+ process(ch),
287331 ]);
288332 // Items distributed across workers — no duplicates, no gaps
289333}