@b9g/node-webworker 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +60 -0
- package/package.json +47 -0
- package/src/index.d.ts +60 -0
- package/src/index.js +87 -0
- package/src/worker-wrapper.js +15 -0
package/README.md
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# @b9g/node-webworker
|
|
2
|
+
|
|
3
|
+
Minimal Web Worker shim for Node.js until native support arrives.
|
|
4
|
+
|
|
5
|
+
## Why This Package Exists
|
|
6
|
+
|
|
7
|
+
Node.js lacks native Web Worker support, despite being a web standard since 2009. This package provides a minimal, reliable shim using Node.js `worker_threads` until native support is added.
|
|
8
|
+
|
|
9
|
+
**🔗 Canonical Issue:** https://github.com/nodejs/node/issues/43583
|
|
10
|
+
**Please 👍 and comment** on the issue to show demand for native Web Worker support!
|
|
11
|
+
|
|
12
|
+
## Installation
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm install @b9g/node-webworker
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Usage
|
|
19
|
+
|
|
20
|
+
```typescript
|
|
21
|
+
import { Worker } from '@b9g/node-webworker';
|
|
22
|
+
|
|
23
|
+
// Create a worker (same API as Web Workers)
|
|
24
|
+
const worker = new Worker('./worker.js', { type: 'module' });
|
|
25
|
+
|
|
26
|
+
// Listen for messages
|
|
27
|
+
worker.addEventListener('message', (event) => {
|
|
28
|
+
console.log('Received:', event.data);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
// Send a message
|
|
32
|
+
worker.postMessage({ hello: 'world' });
|
|
33
|
+
|
|
34
|
+
// Terminate when done
|
|
35
|
+
await worker.terminate();
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Features
|
|
39
|
+
|
|
40
|
+
- ✅ **Standards-compliant API** - Drop-in replacement for Web Workers
|
|
41
|
+
- ✅ **ES Module support** - Works with modern JavaScript
|
|
42
|
+
- ✅ **Minimal overhead** - Thin wrapper around `worker_threads`
|
|
43
|
+
- ✅ **Error handling** - Proper event forwarding
|
|
44
|
+
- ✅ **Clean termination** - Resource cleanup
|
|
45
|
+
|
|
46
|
+
## Limitations
|
|
47
|
+
|
|
48
|
+
- **Transferable objects** - Limited support (logs warning)
|
|
49
|
+
- **Node.js only** - Don't use this in browsers (they have native Web Workers)
|
|
50
|
+
- **Basic API** - Only core Worker features, not full spec
|
|
51
|
+
|
|
52
|
+
## Deprecation Notice
|
|
53
|
+
|
|
54
|
+
⚠️ **This package will be deprecated** once Node.js implements native Web Workers.
|
|
55
|
+
|
|
56
|
+
We maintain this as a temporary workaround. Please help push for native support by engaging with [nodejs/node#43583](https://github.com/nodejs/node/issues/43583).
|
|
57
|
+
|
|
58
|
+
## License
|
|
59
|
+
|
|
60
|
+
MIT
|
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@b9g/node-webworker",
|
|
3
|
+
"version": "0.1.2",
|
|
4
|
+
"description": "Minimal Web Worker shim for Node.js until native support arrives",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"worker",
|
|
7
|
+
"web-worker",
|
|
8
|
+
"node",
|
|
9
|
+
"shim",
|
|
10
|
+
"temporary"
|
|
11
|
+
],
|
|
12
|
+
"dependencies": {},
|
|
13
|
+
"devDependencies": {
|
|
14
|
+
"@b9g/libuild": "^0.1.11",
|
|
15
|
+
"bun-types": "latest",
|
|
16
|
+
"@types/node": "^18.0.0"
|
|
17
|
+
},
|
|
18
|
+
"engines": {
|
|
19
|
+
"node": ">=18.0.0"
|
|
20
|
+
},
|
|
21
|
+
"type": "module",
|
|
22
|
+
"types": "src/index.d.ts",
|
|
23
|
+
"module": "src/index.js",
|
|
24
|
+
"exports": {
|
|
25
|
+
".": {
|
|
26
|
+
"types": "./src/index.d.ts",
|
|
27
|
+
"import": "./src/index.js"
|
|
28
|
+
},
|
|
29
|
+
"./package.json": "./package.json",
|
|
30
|
+
"./index": {
|
|
31
|
+
"types": "./src/index.d.ts",
|
|
32
|
+
"import": "./src/index.js"
|
|
33
|
+
},
|
|
34
|
+
"./index.js": {
|
|
35
|
+
"types": "./src/index.d.ts",
|
|
36
|
+
"import": "./src/index.js"
|
|
37
|
+
},
|
|
38
|
+
"./worker-wrapper": {
|
|
39
|
+
"types": "./src/worker-wrapper.d.ts",
|
|
40
|
+
"import": "./src/worker-wrapper.js"
|
|
41
|
+
},
|
|
42
|
+
"./worker-wrapper.js": {
|
|
43
|
+
"types": "./src/worker-wrapper.d.ts",
|
|
44
|
+
"import": "./src/worker-wrapper.js"
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
package/src/index.d.ts
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @b9g/node-webworker - Minimal Web Worker shim for Node.js
|
|
3
|
+
*
|
|
4
|
+
* This package provides a minimal Web Worker API implementation for Node.js
|
|
5
|
+
* until native Web Worker support is added to Node.js core.
|
|
6
|
+
*
|
|
7
|
+
* @see https://github.com/nodejs/node/issues/43583
|
|
8
|
+
*/
|
|
9
|
+
import { Worker as NodeWorker } from 'worker_threads';
|
|
10
|
+
/**
|
|
11
|
+
* Event-like object for message events
|
|
12
|
+
*/
|
|
13
|
+
export interface MessageEvent {
|
|
14
|
+
readonly data: any;
|
|
15
|
+
readonly type: 'message';
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Error event object
|
|
19
|
+
*/
|
|
20
|
+
export interface ErrorEvent {
|
|
21
|
+
readonly error: Error;
|
|
22
|
+
readonly type: 'error';
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Web Worker API implementation using Node.js worker_threads
|
|
26
|
+
*
|
|
27
|
+
* This provides a minimal, standards-compliant interface that maps
|
|
28
|
+
* to Node.js worker_threads underneath.
|
|
29
|
+
*/
|
|
30
|
+
export declare class Worker {
|
|
31
|
+
private nodeWorker;
|
|
32
|
+
private messageListeners;
|
|
33
|
+
private errorListeners;
|
|
34
|
+
constructor(scriptURL: string, options?: {
|
|
35
|
+
type?: 'classic' | 'module';
|
|
36
|
+
});
|
|
37
|
+
/**
|
|
38
|
+
* Send a message to the worker
|
|
39
|
+
*/
|
|
40
|
+
postMessage(message: any, transfer?: Transferable[]): void;
|
|
41
|
+
/**
|
|
42
|
+
* Add an event listener (Web Worker API)
|
|
43
|
+
*/
|
|
44
|
+
addEventListener(type: 'message', listener: (event: MessageEvent) => void): void;
|
|
45
|
+
addEventListener(type: 'error', listener: (event: ErrorEvent) => void): void;
|
|
46
|
+
/**
|
|
47
|
+
* Remove an event listener
|
|
48
|
+
*/
|
|
49
|
+
removeEventListener(type: 'message', listener: (event: MessageEvent) => void): void;
|
|
50
|
+
removeEventListener(type: 'error', listener: (event: ErrorEvent) => void): void;
|
|
51
|
+
/**
|
|
52
|
+
* Terminate the worker
|
|
53
|
+
*/
|
|
54
|
+
terminate(): Promise<number>;
|
|
55
|
+
/**
|
|
56
|
+
* Get the underlying Node.js Worker (for advanced usage)
|
|
57
|
+
*/
|
|
58
|
+
get nodeWorker_(): NodeWorker;
|
|
59
|
+
}
|
|
60
|
+
export default Worker;
|
package/src/index.js
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/// <reference types="./index.d.ts" />
|
|
2
|
+
// src/index.ts
|
|
3
|
+
import { Worker as NodeWorker } from "worker_threads";
|
|
4
|
+
import { fileURLToPath } from "url";
|
|
5
|
+
import { dirname, join } from "path";
|
|
6
|
+
var Worker = class {
|
|
7
|
+
nodeWorker;
|
|
8
|
+
messageListeners = /* @__PURE__ */ new Set();
|
|
9
|
+
errorListeners = /* @__PURE__ */ new Set();
|
|
10
|
+
constructor(scriptURL, options) {
|
|
11
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
12
|
+
const __dirname = dirname(__filename);
|
|
13
|
+
const wrapperScript = join(__dirname, "worker-wrapper.js");
|
|
14
|
+
this.nodeWorker = new NodeWorker(wrapperScript, {
|
|
15
|
+
type: "module",
|
|
16
|
+
env: {
|
|
17
|
+
...process.env,
|
|
18
|
+
WORKER_SCRIPT_URL: scriptURL
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
this.nodeWorker.on("message", (data) => {
|
|
22
|
+
const event = { data, type: "message" };
|
|
23
|
+
this.messageListeners.forEach((listener) => {
|
|
24
|
+
try {
|
|
25
|
+
listener(event);
|
|
26
|
+
} catch (error) {
|
|
27
|
+
console.error("[node-webworker] Error in message listener:", error);
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
this.nodeWorker.on("error", (error) => {
|
|
32
|
+
const event = { error, type: "error" };
|
|
33
|
+
this.errorListeners.forEach((listener) => {
|
|
34
|
+
try {
|
|
35
|
+
listener(event);
|
|
36
|
+
} catch (listenerError) {
|
|
37
|
+
console.error("[node-webworker] Error in error listener:", listenerError);
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Send a message to the worker
|
|
44
|
+
*/
|
|
45
|
+
postMessage(message, transfer) {
|
|
46
|
+
if (transfer && transfer.length > 0) {
|
|
47
|
+
console.warn("[node-webworker] Transferable objects not fully supported");
|
|
48
|
+
}
|
|
49
|
+
this.nodeWorker.postMessage(message);
|
|
50
|
+
}
|
|
51
|
+
addEventListener(type, listener) {
|
|
52
|
+
if (type === "message") {
|
|
53
|
+
this.messageListeners.add(listener);
|
|
54
|
+
} else if (type === "error") {
|
|
55
|
+
this.errorListeners.add(listener);
|
|
56
|
+
} else {
|
|
57
|
+
console.warn(`[node-webworker] Unsupported event type: ${type}`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
removeEventListener(type, listener) {
|
|
61
|
+
if (type === "message") {
|
|
62
|
+
this.messageListeners.delete(listener);
|
|
63
|
+
} else if (type === "error") {
|
|
64
|
+
this.errorListeners.delete(listener);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Terminate the worker
|
|
69
|
+
*/
|
|
70
|
+
async terminate() {
|
|
71
|
+
const exitCode = await this.nodeWorker.terminate();
|
|
72
|
+
this.messageListeners.clear();
|
|
73
|
+
this.errorListeners.clear();
|
|
74
|
+
return exitCode;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Get the underlying Node.js Worker (for advanced usage)
|
|
78
|
+
*/
|
|
79
|
+
get nodeWorker_() {
|
|
80
|
+
return this.nodeWorker;
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
var src_default = Worker;
|
|
84
|
+
export {
|
|
85
|
+
Worker,
|
|
86
|
+
src_default as default
|
|
87
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// src/worker-wrapper.js
|
|
2
|
+
import { parentPort } from "worker_threads";
|
|
3
|
+
globalThis.onmessage = null;
|
|
4
|
+
globalThis.postMessage = (data) => parentPort.postMessage(data);
|
|
5
|
+
parentPort.on("message", (data) => {
|
|
6
|
+
if (globalThis.onmessage) {
|
|
7
|
+
globalThis.onmessage({ data, type: "message" });
|
|
8
|
+
}
|
|
9
|
+
});
|
|
10
|
+
var WORKER_SCRIPT_URL = process.env.WORKER_SCRIPT_URL;
|
|
11
|
+
if (WORKER_SCRIPT_URL) {
|
|
12
|
+
await import(WORKER_SCRIPT_URL);
|
|
13
|
+
} else {
|
|
14
|
+
throw new Error("WORKER_SCRIPT_URL environment variable not set");
|
|
15
|
+
}
|