@b9g/node-webworker 0.1.2 → 0.1.3
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 +22 -7
- package/package.json +3 -3
- package/src/index.d.ts +14 -14
- package/src/index.js +94 -38
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@ Minimal Web Worker shim for Node.js until native support arrives.
|
|
|
6
6
|
|
|
7
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
8
|
|
|
9
|
-
**🔗 Canonical Issue:** https://github.com/nodejs/node/issues/43583
|
|
9
|
+
**🔗 Canonical Issue:** https://github.com/nodejs/node/issues/43583
|
|
10
10
|
**Please 👍 and comment** on the issue to show demand for native Web Worker support!
|
|
11
11
|
|
|
12
12
|
## Installation
|
|
@@ -35,13 +35,28 @@ worker.postMessage({ hello: 'world' });
|
|
|
35
35
|
await worker.terminate();
|
|
36
36
|
```
|
|
37
37
|
|
|
38
|
+
## Exports
|
|
39
|
+
|
|
40
|
+
### Classes
|
|
41
|
+
|
|
42
|
+
- `Worker` - Web Worker implementation using Node.js worker_threads
|
|
43
|
+
|
|
44
|
+
### Types
|
|
45
|
+
|
|
46
|
+
- `MessageEvent` - Event type for worker messages
|
|
47
|
+
- `ErrorEvent` - Event type for worker errors
|
|
48
|
+
|
|
49
|
+
### Default Export
|
|
50
|
+
|
|
51
|
+
- `Worker` - The Worker class
|
|
52
|
+
|
|
38
53
|
## Features
|
|
39
54
|
|
|
40
|
-
-
|
|
41
|
-
-
|
|
42
|
-
-
|
|
43
|
-
-
|
|
44
|
-
-
|
|
55
|
+
- **Standards-compliant API** - Drop-in replacement for Web Workers
|
|
56
|
+
- **ES Module support** - Works with modern JavaScript
|
|
57
|
+
- **Minimal overhead** - Thin wrapper around `worker_threads`
|
|
58
|
+
- **Error handling** - Proper event forwarding
|
|
59
|
+
- **Clean termination** - Resource cleanup
|
|
45
60
|
|
|
46
61
|
## Limitations
|
|
47
62
|
|
|
@@ -57,4 +72,4 @@ We maintain this as a temporary workaround. Please help push for native support
|
|
|
57
72
|
|
|
58
73
|
## License
|
|
59
74
|
|
|
60
|
-
MIT
|
|
75
|
+
MIT
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@b9g/node-webworker",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Minimal Web Worker shim for Node.js until native support arrives",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"worker",
|
|
@@ -36,11 +36,11 @@
|
|
|
36
36
|
"import": "./src/index.js"
|
|
37
37
|
},
|
|
38
38
|
"./worker-wrapper": {
|
|
39
|
-
"types": "./src/
|
|
39
|
+
"types": "./src/undefined.d.ts",
|
|
40
40
|
"import": "./src/worker-wrapper.js"
|
|
41
41
|
},
|
|
42
42
|
"./worker-wrapper.js": {
|
|
43
|
-
"types": "./src/
|
|
43
|
+
"types": "./src/undefined.d.ts",
|
|
44
44
|
"import": "./src/worker-wrapper.js"
|
|
45
45
|
}
|
|
46
46
|
}
|
package/src/index.d.ts
CHANGED
|
@@ -6,20 +6,20 @@
|
|
|
6
6
|
*
|
|
7
7
|
* @see https://github.com/nodejs/node/issues/43583
|
|
8
8
|
*/
|
|
9
|
-
import { Worker as NodeWorker } from
|
|
9
|
+
import { Worker as NodeWorker } from "worker_threads";
|
|
10
10
|
/**
|
|
11
11
|
* Event-like object for message events
|
|
12
12
|
*/
|
|
13
13
|
export interface MessageEvent {
|
|
14
14
|
readonly data: any;
|
|
15
|
-
readonly type:
|
|
15
|
+
readonly type: "message";
|
|
16
16
|
}
|
|
17
17
|
/**
|
|
18
18
|
* Error event object
|
|
19
19
|
*/
|
|
20
20
|
export interface ErrorEvent {
|
|
21
21
|
readonly error: Error;
|
|
22
|
-
readonly type:
|
|
22
|
+
readonly type: "error";
|
|
23
23
|
}
|
|
24
24
|
/**
|
|
25
25
|
* Web Worker API implementation using Node.js worker_threads
|
|
@@ -28,11 +28,11 @@ export interface ErrorEvent {
|
|
|
28
28
|
* to Node.js worker_threads underneath.
|
|
29
29
|
*/
|
|
30
30
|
export declare class Worker {
|
|
31
|
-
private
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
constructor(scriptURL: string,
|
|
35
|
-
type?:
|
|
31
|
+
#private;
|
|
32
|
+
onmessage: ((event: MessageEvent) => void) | null;
|
|
33
|
+
onerror: ((event: ErrorEvent) => void) | null;
|
|
34
|
+
constructor(scriptURL: string | URL, _options?: {
|
|
35
|
+
type?: "classic" | "module";
|
|
36
36
|
});
|
|
37
37
|
/**
|
|
38
38
|
* Send a message to the worker
|
|
@@ -41,17 +41,17 @@ export declare class Worker {
|
|
|
41
41
|
/**
|
|
42
42
|
* Add an event listener (Web Worker API)
|
|
43
43
|
*/
|
|
44
|
-
addEventListener(type:
|
|
45
|
-
addEventListener(type:
|
|
44
|
+
addEventListener(type: "message", listener: (event: MessageEvent) => void): void;
|
|
45
|
+
addEventListener(type: "error", listener: (event: ErrorEvent) => void): void;
|
|
46
46
|
/**
|
|
47
47
|
* Remove an event listener
|
|
48
48
|
*/
|
|
49
|
-
removeEventListener(type:
|
|
50
|
-
removeEventListener(type:
|
|
49
|
+
removeEventListener(type: "message", listener: (event: MessageEvent) => void): void;
|
|
50
|
+
removeEventListener(type: "error", listener: (event: ErrorEvent) => void): void;
|
|
51
51
|
/**
|
|
52
|
-
* Terminate the worker
|
|
52
|
+
* Terminate the worker (Web Worker standard - returns void, not a promise)
|
|
53
53
|
*/
|
|
54
|
-
terminate():
|
|
54
|
+
terminate(): void;
|
|
55
55
|
/**
|
|
56
56
|
* Get the underlying Node.js Worker (for advanced usage)
|
|
57
57
|
*/
|
package/src/index.js
CHANGED
|
@@ -1,83 +1,139 @@
|
|
|
1
1
|
/// <reference types="./index.d.ts" />
|
|
2
2
|
// src/index.ts
|
|
3
3
|
import { Worker as NodeWorker } from "worker_threads";
|
|
4
|
-
|
|
5
|
-
import {
|
|
4
|
+
var WORKER_WRAPPER_CODE = `
|
|
5
|
+
import {parentPort} from "worker_threads";
|
|
6
|
+
|
|
7
|
+
// Provide Web Worker globals
|
|
8
|
+
globalThis.onmessage = null;
|
|
9
|
+
globalThis.postMessage = (data) => parentPort.postMessage(data);
|
|
10
|
+
|
|
11
|
+
// Set up message forwarding
|
|
12
|
+
parentPort.on("message", (data) => {
|
|
13
|
+
if (globalThis.onmessage) {
|
|
14
|
+
globalThis.onmessage({data, type: "message"});
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
// Import the actual worker script URL from environment variable
|
|
19
|
+
const WORKER_SCRIPT_URL = process.env.WORKER_SCRIPT_URL;
|
|
20
|
+
if (WORKER_SCRIPT_URL) {
|
|
21
|
+
await import(WORKER_SCRIPT_URL);
|
|
22
|
+
} else {
|
|
23
|
+
throw new Error("WORKER_SCRIPT_URL environment variable not set");
|
|
24
|
+
}
|
|
25
|
+
`;
|
|
26
|
+
var WORKER_WRAPPER_DATA_URL = new URL(
|
|
27
|
+
`data:text/javascript,${encodeURIComponent(WORKER_WRAPPER_CODE)}`
|
|
28
|
+
);
|
|
6
29
|
var Worker = class {
|
|
7
|
-
nodeWorker;
|
|
8
|
-
messageListeners
|
|
9
|
-
errorListeners
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
30
|
+
#nodeWorker;
|
|
31
|
+
#messageListeners;
|
|
32
|
+
#errorListeners;
|
|
33
|
+
// Web Worker standard properties
|
|
34
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
35
|
+
onmessage = null;
|
|
36
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
37
|
+
onerror = null;
|
|
38
|
+
constructor(scriptURL, _options) {
|
|
39
|
+
this.#messageListeners = /* @__PURE__ */ new Set();
|
|
40
|
+
this.#errorListeners = /* @__PURE__ */ new Set();
|
|
41
|
+
const scriptURLString = scriptURL.toString();
|
|
42
|
+
let workerScriptURL = scriptURLString;
|
|
43
|
+
if (!scriptURLString.startsWith("file://") && !scriptURLString.startsWith("data:")) {
|
|
44
|
+
if (scriptURLString.startsWith("./") || scriptURLString.startsWith("../")) {
|
|
45
|
+
throw new Error(
|
|
46
|
+
"Relative paths are not supported. Use new Worker(new URL('./worker.js', import.meta.url)) instead."
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
workerScriptURL = `file://${scriptURLString}`;
|
|
50
|
+
}
|
|
51
|
+
this.#nodeWorker = new NodeWorker(WORKER_WRAPPER_DATA_URL, {
|
|
52
|
+
...{ type: "module" },
|
|
16
53
|
env: {
|
|
17
54
|
...process.env,
|
|
18
|
-
WORKER_SCRIPT_URL:
|
|
55
|
+
WORKER_SCRIPT_URL: workerScriptURL
|
|
19
56
|
}
|
|
20
57
|
});
|
|
21
|
-
this
|
|
58
|
+
this.#setupEventForwarding();
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Report an error through the error event mechanism
|
|
62
|
+
*/
|
|
63
|
+
#reportError(error) {
|
|
64
|
+
const event = { error, type: "error" };
|
|
65
|
+
if (this.onerror) {
|
|
66
|
+
this.onerror(event);
|
|
67
|
+
}
|
|
68
|
+
this.#errorListeners.forEach((listener) => {
|
|
69
|
+
listener(event);
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Set up event forwarding from Node.js Worker to Web Worker API
|
|
74
|
+
*/
|
|
75
|
+
#setupEventForwarding() {
|
|
76
|
+
this.#nodeWorker.on("message", (data) => {
|
|
22
77
|
const event = { data, type: "message" };
|
|
23
|
-
this.
|
|
78
|
+
if (this.onmessage) {
|
|
24
79
|
try {
|
|
25
|
-
|
|
80
|
+
this.onmessage(event);
|
|
26
81
|
} catch (error) {
|
|
27
|
-
|
|
82
|
+
this.#reportError(error);
|
|
28
83
|
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
this.nodeWorker.on("error", (error) => {
|
|
32
|
-
const event = { error, type: "error" };
|
|
33
|
-
this.errorListeners.forEach((listener) => {
|
|
84
|
+
}
|
|
85
|
+
this.#messageListeners.forEach((listener) => {
|
|
34
86
|
try {
|
|
35
87
|
listener(event);
|
|
36
|
-
} catch (
|
|
37
|
-
|
|
88
|
+
} catch (error) {
|
|
89
|
+
this.#reportError(error);
|
|
38
90
|
}
|
|
39
91
|
});
|
|
40
92
|
});
|
|
93
|
+
this.#nodeWorker.on("error", (error) => {
|
|
94
|
+
this.#reportError(error);
|
|
95
|
+
});
|
|
41
96
|
}
|
|
42
97
|
/**
|
|
43
98
|
* Send a message to the worker
|
|
44
99
|
*/
|
|
45
100
|
postMessage(message, transfer) {
|
|
46
101
|
if (transfer && transfer.length > 0) {
|
|
47
|
-
|
|
102
|
+
this.#nodeWorker.postMessage(message, transfer);
|
|
103
|
+
} else {
|
|
104
|
+
this.#nodeWorker.postMessage(message);
|
|
48
105
|
}
|
|
49
|
-
this.nodeWorker.postMessage(message);
|
|
50
106
|
}
|
|
51
107
|
addEventListener(type, listener) {
|
|
52
108
|
if (type === "message") {
|
|
53
|
-
this
|
|
109
|
+
this.#messageListeners.add(listener);
|
|
54
110
|
} else if (type === "error") {
|
|
55
|
-
this
|
|
56
|
-
} else {
|
|
57
|
-
console.warn(`[node-webworker] Unsupported event type: ${type}`);
|
|
111
|
+
this.#errorListeners.add(listener);
|
|
58
112
|
}
|
|
59
113
|
}
|
|
60
114
|
removeEventListener(type, listener) {
|
|
61
115
|
if (type === "message") {
|
|
62
|
-
this
|
|
116
|
+
this.#messageListeners.delete(listener);
|
|
63
117
|
} else if (type === "error") {
|
|
64
|
-
this
|
|
118
|
+
this.#errorListeners.delete(listener);
|
|
65
119
|
}
|
|
66
120
|
}
|
|
67
121
|
/**
|
|
68
|
-
* Terminate the worker
|
|
122
|
+
* Terminate the worker (Web Worker standard - returns void, not a promise)
|
|
69
123
|
*/
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
this.
|
|
74
|
-
|
|
124
|
+
terminate() {
|
|
125
|
+
this.#nodeWorker.terminate().catch(() => {
|
|
126
|
+
});
|
|
127
|
+
this.#messageListeners.clear();
|
|
128
|
+
this.#errorListeners.clear();
|
|
129
|
+
this.onmessage = null;
|
|
130
|
+
this.onerror = null;
|
|
75
131
|
}
|
|
76
132
|
/**
|
|
77
133
|
* Get the underlying Node.js Worker (for advanced usage)
|
|
78
134
|
*/
|
|
79
135
|
get nodeWorker_() {
|
|
80
|
-
return this
|
|
136
|
+
return this.#nodeWorker;
|
|
81
137
|
}
|
|
82
138
|
};
|
|
83
139
|
var src_default = Worker;
|