@b9g/node-webworker 0.1.3 → 0.2.0-beta.1

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 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
@@ -18,7 +18,7 @@ npm install @b9g/node-webworker
18
18
  ## Usage
19
19
 
20
20
  ```typescript
21
- import { Worker } from '@b9g/node-webworker';
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
- await worker.terminate();
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
- ### Types
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
- - **Basic API** - Only core Worker features, not full spec
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.1.3",
3
+ "version": "0.2.0-beta.1",
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.11",
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
- * Event-like object for message events
10
+ * Message event for worker communication
12
11
  */
13
- export interface MessageEvent {
12
+ export declare class MessageEvent extends Event {
14
13
  readonly data: any;
15
- readonly type: "message";
14
+ constructor(data: any);
16
15
  }
17
16
  /**
18
- * Error event object
17
+ * Error event for worker errors
19
18
  */
20
- export interface ErrorEvent {
19
+ export declare class ErrorEvent extends Event {
21
20
  readonly error: Error;
22
- readonly type: "error";
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 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
- `;
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
- // eslint-disable-next-line no-restricted-syntax
35
- onmessage = null;
36
- // eslint-disable-next-line no-restricted-syntax
37
- onerror = null;
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 = { error, type: "error" };
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 = { data, type: "message" };
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
  };
@@ -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
- }