Skip to main content

Migration from v1 → v2

hurried v2 is a complete TypeScript rewrite. The v1 public API still works where it makes sense — but you'll want to migrate to the new primitives for the type safety and the bus.

Quick diff

- const { Thread, makeExecutable } = require('hurried');
+ import { Thread, defineWorker, workerBus } from 'hurried';

- module.exports.slow = slow;
- makeExecutable(slow, 'slow');
+ export default defineWorker({ slow });

What changed

Areav1v2
LanguageJavaScriptTypeScript with .d.ts published
Module formatCommonJSESM + CJS (dual)
Min Node version10.518.17
Inline functionsvia fromScript(code) (untyped)fromFunction(fn) (typed)
Poolsnot built-innew Pool({ task, size })
Parallel helpersnot built-inparallel, mapParallel
Pub/sub eventsnot built-intyped Bus<TEvents> on every primitive
Cancellationnot built-inAbortSignal + timeout on every call
Errorsplain Errortyped hierarchy (TaskError, TaskTimeoutError, …)
Named handlersmakeExecutable(fn, name)defineWorker({ name: fn, ... }) (legacy makeExecutable kept)
Testsjest snapshotsvitest, 97% coverage, matrix CI

Migration steps

1. Update imports

- const { Thread, makeExecutable } = require('hurried');
+ import { Thread, defineWorker, workerBus } from 'hurried';

2. Replace inline fromScript usage with fromFunction

- const thread = Thread.fromScript(`
- const { makeExecutable } = require('hurried');
- makeExecutable((n) => n * 2, 'double');
- `);
- await thread.run('double', 21);

+ const thread = Thread.fromFunction((n: number) => n * 2);
+ await thread.run(21);

3. Migrate handler files to defineWorker

- // worker.js
- const { makeExecutable } = require('hurried');
- function process(items) { /* ... */ return items.length; }
- module.exports.process = process;
- makeExecutable(process, 'process');

+ // worker.ts
+ import { defineWorker } from 'hurried';
+ export default defineWorker({
+ process(items: string[]) { /* ... */ return items.length; },
+ });

makeExecutable is still exported, so you don't have to migrate — but defineWorker gives you better type ergonomics.

4. Add typed events with workerBus

For workers that report progress or state, add a typed event map and reach for workerBus<Events>():

// shared.ts
export type Events = {
progress: { done: number; total: number };
};

// worker.ts
import { defineWorker, workerBus } from 'hurried';
import type { Events } from './shared.js';

const bus = workerBus<Events>();

export default defineWorker({
process(items: string[]) {
items.forEach((it, i) =>
bus.emit('progress', { done: i + 1, total: items.length }),
);
return items.length;
},
});

// main.ts
import type { Events } from './shared.js';
const thread = Thread.fromFile<Events>(new URL('./worker.js', import.meta.url));
thread.on('progress', (p) => render(p));

5. Adopt pools where it makes sense

Anywhere you had multiple workers running the same task, Pool cleans up the lifecycle:

- const threads = Array.from({ length: 4 }, () => Thread.fromFile('./worker.js'));
- const results = await Promise.all(inputs.map((x, i) => threads[i % 4].run('process', x)));
- threads.forEach((t) => t.terminate());

+ const pool = new Pool({ size: 4, task: processInput });
+ const results = await pool.map(inputs);
+ await pool.terminate();