@b9g/node-webworker 0.1.3 → 0.2.0
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 +7 -10
- package/package.json +2 -11
- package/src/index.d.ts +21 -11
- package/src/index.js +81 -34
- package/src/worker-wrapper.js +0 -15
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
|
-
|
|
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
|
|
@@ -18,7 +18,7 @@ npm install @b9g/node-webworker
|
|
|
18
18
|
## Usage
|
|
19
19
|
|
|
20
20
|
```typescript
|
|
21
|
-
import {
|
|
21
|
+
import {Worker} from '@b9g/node-webworker';
|
|
22
22
|
|
|
23
23
|
// Create a worker (same API as Web Workers)
|
|
24
24
|
const worker = new Worker('./worker.js', { type: 'module' });
|
|
@@ -32,7 +32,7 @@ worker.addEventListener('message', (event) => {
|
|
|
32
32
|
worker.postMessage({ hello: 'world' });
|
|
33
33
|
|
|
34
34
|
// Terminate when done
|
|
35
|
-
|
|
35
|
+
worker.terminate();
|
|
36
36
|
```
|
|
37
37
|
|
|
38
38
|
## Exports
|
|
@@ -40,11 +40,8 @@ await worker.terminate();
|
|
|
40
40
|
### Classes
|
|
41
41
|
|
|
42
42
|
- `Worker` - Web Worker implementation using Node.js worker_threads
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
- `MessageEvent` - Event type for worker messages
|
|
47
|
-
- `ErrorEvent` - Event type for worker errors
|
|
43
|
+
- `MessageEvent` - Event class for worker messages
|
|
44
|
+
- `ErrorEvent` - Event class for worker errors
|
|
48
45
|
|
|
49
46
|
### Default Export
|
|
50
47
|
|
|
@@ -60,9 +57,9 @@ await worker.terminate();
|
|
|
60
57
|
|
|
61
58
|
## Limitations
|
|
62
59
|
|
|
63
|
-
- **Transferable objects** - Limited support (logs warning)
|
|
64
60
|
- **Node.js only** - Don't use this in browsers (they have native Web Workers)
|
|
65
|
-
- **
|
|
61
|
+
- **Module workers only** - Classic workers with `importScripts()` not supported
|
|
62
|
+
- **Serialization differences** - Uses Node.js structured clone, not web's algorithm
|
|
66
63
|
|
|
67
64
|
## Deprecation Notice
|
|
68
65
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@b9g/node-webworker",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Minimal Web Worker shim for Node.js until native support arrives",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"worker",
|
|
@@ -11,8 +11,7 @@
|
|
|
11
11
|
],
|
|
12
12
|
"dependencies": {},
|
|
13
13
|
"devDependencies": {
|
|
14
|
-
"@b9g/libuild": "^0.1.
|
|
15
|
-
"bun-types": "latest",
|
|
14
|
+
"@b9g/libuild": "^0.1.18",
|
|
16
15
|
"@types/node": "^18.0.0"
|
|
17
16
|
},
|
|
18
17
|
"engines": {
|
|
@@ -34,14 +33,6 @@
|
|
|
34
33
|
"./index.js": {
|
|
35
34
|
"types": "./src/index.d.ts",
|
|
36
35
|
"import": "./src/index.js"
|
|
37
|
-
},
|
|
38
|
-
"./worker-wrapper": {
|
|
39
|
-
"types": "./src/undefined.d.ts",
|
|
40
|
-
"import": "./src/worker-wrapper.js"
|
|
41
|
-
},
|
|
42
|
-
"./worker-wrapper.js": {
|
|
43
|
-
"types": "./src/undefined.d.ts",
|
|
44
|
-
"import": "./src/worker-wrapper.js"
|
|
45
36
|
}
|
|
46
37
|
}
|
|
47
38
|
}
|
package/src/index.d.ts
CHANGED
|
@@ -6,20 +6,27 @@
|
|
|
6
6
|
*
|
|
7
7
|
* @see https://github.com/nodejs/node/issues/43583
|
|
8
8
|
*/
|
|
9
|
-
import { Worker as NodeWorker } from "worker_threads";
|
|
10
9
|
/**
|
|
11
|
-
*
|
|
10
|
+
* Message event for worker communication
|
|
12
11
|
*/
|
|
13
|
-
export
|
|
12
|
+
export declare class MessageEvent extends Event {
|
|
14
13
|
readonly data: any;
|
|
15
|
-
|
|
14
|
+
constructor(data: any);
|
|
16
15
|
}
|
|
17
16
|
/**
|
|
18
|
-
* Error event
|
|
17
|
+
* Error event for worker errors
|
|
19
18
|
*/
|
|
20
|
-
export
|
|
19
|
+
export declare class ErrorEvent extends Event {
|
|
21
20
|
readonly error: Error;
|
|
22
|
-
|
|
21
|
+
constructor(error: Error);
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Close event for worker termination
|
|
25
|
+
* Includes exit code for crash detection
|
|
26
|
+
*/
|
|
27
|
+
export declare class CloseEvent extends Event {
|
|
28
|
+
readonly code: number;
|
|
29
|
+
constructor(code: number);
|
|
23
30
|
}
|
|
24
31
|
/**
|
|
25
32
|
* Web Worker API implementation using Node.js worker_threads
|
|
@@ -31,8 +38,11 @@ export declare class Worker {
|
|
|
31
38
|
#private;
|
|
32
39
|
onmessage: ((event: MessageEvent) => void) | null;
|
|
33
40
|
onerror: ((event: ErrorEvent) => void) | null;
|
|
41
|
+
onmessageerror: ((event: MessageEvent) => void) | null;
|
|
42
|
+
onclose: ((event: CloseEvent) => void) | null;
|
|
34
43
|
constructor(scriptURL: string | URL, _options?: {
|
|
35
44
|
type?: "classic" | "module";
|
|
45
|
+
env?: Record<string, string>;
|
|
36
46
|
});
|
|
37
47
|
/**
|
|
38
48
|
* Send a message to the worker
|
|
@@ -43,18 +53,18 @@ export declare class Worker {
|
|
|
43
53
|
*/
|
|
44
54
|
addEventListener(type: "message", listener: (event: MessageEvent) => void): void;
|
|
45
55
|
addEventListener(type: "error", listener: (event: ErrorEvent) => void): void;
|
|
56
|
+
addEventListener(type: "messageerror", listener: (event: MessageEvent) => void): void;
|
|
57
|
+
addEventListener(type: "close", listener: (event: CloseEvent) => void): void;
|
|
46
58
|
/**
|
|
47
59
|
* Remove an event listener
|
|
48
60
|
*/
|
|
49
61
|
removeEventListener(type: "message", listener: (event: MessageEvent) => void): void;
|
|
50
62
|
removeEventListener(type: "error", listener: (event: ErrorEvent) => void): void;
|
|
63
|
+
removeEventListener(type: "messageerror", listener: (event: MessageEvent) => void): void;
|
|
64
|
+
removeEventListener(type: "close", listener: (event: CloseEvent) => void): void;
|
|
51
65
|
/**
|
|
52
66
|
* Terminate the worker (Web Worker standard - returns void, not a promise)
|
|
53
67
|
*/
|
|
54
68
|
terminate(): void;
|
|
55
|
-
/**
|
|
56
|
-
* Get the underlying Node.js Worker (for advanced usage)
|
|
57
|
-
*/
|
|
58
|
-
get nodeWorker_(): NodeWorker;
|
|
59
69
|
}
|
|
60
70
|
export default Worker;
|
package/src/index.js
CHANGED
|
@@ -1,28 +1,28 @@
|
|
|
1
1
|
/// <reference types="./index.d.ts" />
|
|
2
2
|
// src/index.ts
|
|
3
3
|
import { Worker as NodeWorker } from "worker_threads";
|
|
4
|
-
var
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
}
|
|
25
|
-
|
|
4
|
+
var MessageEvent = class extends Event {
|
|
5
|
+
data;
|
|
6
|
+
constructor(data) {
|
|
7
|
+
super("message");
|
|
8
|
+
this.data = data;
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
var ErrorEvent = class extends Event {
|
|
12
|
+
error;
|
|
13
|
+
constructor(error) {
|
|
14
|
+
super("error");
|
|
15
|
+
this.error = error;
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
var CloseEvent = class extends Event {
|
|
19
|
+
code;
|
|
20
|
+
constructor(code) {
|
|
21
|
+
super("close");
|
|
22
|
+
this.code = code;
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
var WORKER_WRAPPER_CODE = `import{parentPort as p}from"worker_threads";const l=new Set();globalThis.onmessage=null;globalThis.onmessageerror=null;globalThis.postMessage=(d,t)=>t?.length?p.postMessage(d,t):p.postMessage(d);globalThis.self=globalThis;globalThis.addEventListener=(t,f)=>t==="message"&&l.add(f);globalThis.removeEventListener=(t,f)=>t==="message"&&l.delete(f);p.on("message",d=>{const e={data:d,type:"message"};globalThis.onmessage?.(e);l.forEach(f=>f(e))});const u=process.env.WORKER_SCRIPT_URL;if(u)await import(u);else throw Error("WORKER_SCRIPT_URL not set");`;
|
|
26
26
|
var WORKER_WRAPPER_DATA_URL = new URL(
|
|
27
27
|
`data:text/javascript,${encodeURIComponent(WORKER_WRAPPER_CODE)}`
|
|
28
28
|
);
|
|
@@ -30,14 +30,22 @@ var Worker = class {
|
|
|
30
30
|
#nodeWorker;
|
|
31
31
|
#messageListeners;
|
|
32
32
|
#errorListeners;
|
|
33
|
+
#messageerrorListeners;
|
|
34
|
+
#closeListeners;
|
|
33
35
|
// Web Worker standard properties
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
onmessage;
|
|
37
|
+
onerror;
|
|
38
|
+
onmessageerror;
|
|
39
|
+
onclose;
|
|
38
40
|
constructor(scriptURL, _options) {
|
|
39
41
|
this.#messageListeners = /* @__PURE__ */ new Set();
|
|
40
42
|
this.#errorListeners = /* @__PURE__ */ new Set();
|
|
43
|
+
this.#messageerrorListeners = /* @__PURE__ */ new Set();
|
|
44
|
+
this.#closeListeners = /* @__PURE__ */ new Set();
|
|
45
|
+
this.onmessage = null;
|
|
46
|
+
this.onerror = null;
|
|
47
|
+
this.onmessageerror = null;
|
|
48
|
+
this.onclose = null;
|
|
41
49
|
const scriptURLString = scriptURL.toString();
|
|
42
50
|
let workerScriptURL = scriptURLString;
|
|
43
51
|
if (!scriptURLString.startsWith("file://") && !scriptURLString.startsWith("data:")) {
|
|
@@ -51,17 +59,31 @@ var Worker = class {
|
|
|
51
59
|
this.#nodeWorker = new NodeWorker(WORKER_WRAPPER_DATA_URL, {
|
|
52
60
|
...{ type: "module" },
|
|
53
61
|
env: {
|
|
62
|
+
// eslint-disable-next-line no-restricted-properties -- Workers inherit parent env
|
|
54
63
|
...process.env,
|
|
64
|
+
..._options?.env,
|
|
55
65
|
WORKER_SCRIPT_URL: workerScriptURL
|
|
56
66
|
}
|
|
57
67
|
});
|
|
58
68
|
this.#setupEventForwarding();
|
|
59
69
|
}
|
|
70
|
+
/**
|
|
71
|
+
* Report a close event when the worker exits
|
|
72
|
+
*/
|
|
73
|
+
#reportClose(code) {
|
|
74
|
+
const event = new CloseEvent(code);
|
|
75
|
+
if (this.onclose) {
|
|
76
|
+
this.onclose(event);
|
|
77
|
+
}
|
|
78
|
+
this.#closeListeners.forEach((listener) => {
|
|
79
|
+
listener(event);
|
|
80
|
+
});
|
|
81
|
+
}
|
|
60
82
|
/**
|
|
61
83
|
* Report an error through the error event mechanism
|
|
62
84
|
*/
|
|
63
85
|
#reportError(error) {
|
|
64
|
-
const event =
|
|
86
|
+
const event = new ErrorEvent(error);
|
|
65
87
|
if (this.onerror) {
|
|
66
88
|
this.onerror(event);
|
|
67
89
|
}
|
|
@@ -74,7 +96,7 @@ var Worker = class {
|
|
|
74
96
|
*/
|
|
75
97
|
#setupEventForwarding() {
|
|
76
98
|
this.#nodeWorker.on("message", (data) => {
|
|
77
|
-
const event =
|
|
99
|
+
const event = new MessageEvent(data);
|
|
78
100
|
if (this.onmessage) {
|
|
79
101
|
try {
|
|
80
102
|
this.onmessage(event);
|
|
@@ -93,6 +115,18 @@ var Worker = class {
|
|
|
93
115
|
this.#nodeWorker.on("error", (error) => {
|
|
94
116
|
this.#reportError(error);
|
|
95
117
|
});
|
|
118
|
+
this.#nodeWorker.on("messageerror", (data) => {
|
|
119
|
+
const event = new MessageEvent(data);
|
|
120
|
+
if (this.onmessageerror) {
|
|
121
|
+
this.onmessageerror(event);
|
|
122
|
+
}
|
|
123
|
+
this.#messageerrorListeners.forEach((listener) => {
|
|
124
|
+
listener(event);
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
this.#nodeWorker.on("exit", (code) => {
|
|
128
|
+
this.#reportClose(code);
|
|
129
|
+
});
|
|
96
130
|
}
|
|
97
131
|
/**
|
|
98
132
|
* Send a message to the worker
|
|
@@ -109,6 +143,12 @@ var Worker = class {
|
|
|
109
143
|
this.#messageListeners.add(listener);
|
|
110
144
|
} else if (type === "error") {
|
|
111
145
|
this.#errorListeners.add(listener);
|
|
146
|
+
} else if (type === "messageerror") {
|
|
147
|
+
this.#messageerrorListeners.add(
|
|
148
|
+
listener
|
|
149
|
+
);
|
|
150
|
+
} else if (type === "close") {
|
|
151
|
+
this.#closeListeners.add(listener);
|
|
112
152
|
}
|
|
113
153
|
}
|
|
114
154
|
removeEventListener(type, listener) {
|
|
@@ -116,6 +156,12 @@ var Worker = class {
|
|
|
116
156
|
this.#messageListeners.delete(listener);
|
|
117
157
|
} else if (type === "error") {
|
|
118
158
|
this.#errorListeners.delete(listener);
|
|
159
|
+
} else if (type === "messageerror") {
|
|
160
|
+
this.#messageerrorListeners.delete(
|
|
161
|
+
listener
|
|
162
|
+
);
|
|
163
|
+
} else if (type === "close") {
|
|
164
|
+
this.#closeListeners.delete(listener);
|
|
119
165
|
}
|
|
120
166
|
}
|
|
121
167
|
/**
|
|
@@ -126,18 +172,19 @@ var Worker = class {
|
|
|
126
172
|
});
|
|
127
173
|
this.#messageListeners.clear();
|
|
128
174
|
this.#errorListeners.clear();
|
|
175
|
+
this.#messageerrorListeners.clear();
|
|
176
|
+
this.#closeListeners.clear();
|
|
129
177
|
this.onmessage = null;
|
|
130
178
|
this.onerror = null;
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
* Get the underlying Node.js Worker (for advanced usage)
|
|
134
|
-
*/
|
|
135
|
-
get nodeWorker_() {
|
|
136
|
-
return this.#nodeWorker;
|
|
179
|
+
this.onmessageerror = null;
|
|
180
|
+
this.onclose = null;
|
|
137
181
|
}
|
|
138
182
|
};
|
|
139
183
|
var src_default = Worker;
|
|
140
184
|
export {
|
|
185
|
+
CloseEvent,
|
|
186
|
+
ErrorEvent,
|
|
187
|
+
MessageEvent,
|
|
141
188
|
Worker,
|
|
142
189
|
src_default as default
|
|
143
190
|
};
|
package/src/worker-wrapper.js
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
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
|
-
}
|