@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 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
- - **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
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.2",
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/worker-wrapper.d.ts",
39
+ "types": "./src/undefined.d.ts",
40
40
  "import": "./src/worker-wrapper.js"
41
41
  },
42
42
  "./worker-wrapper.js": {
43
- "types": "./src/worker-wrapper.d.ts",
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 'worker_threads';
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: 'message';
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: 'error';
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 nodeWorker;
32
- private messageListeners;
33
- private errorListeners;
34
- constructor(scriptURL: string, options?: {
35
- type?: 'classic' | 'module';
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: 'message', listener: (event: MessageEvent) => void): void;
45
- addEventListener(type: 'error', listener: (event: ErrorEvent) => void): void;
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: 'message', listener: (event: MessageEvent) => void): void;
50
- removeEventListener(type: 'error', listener: (event: ErrorEvent) => void): void;
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(): Promise<number>;
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
- import { fileURLToPath } from "url";
5
- import { dirname, join } from "path";
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 = /* @__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",
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: scriptURL
55
+ WORKER_SCRIPT_URL: workerScriptURL
19
56
  }
20
57
  });
21
- this.nodeWorker.on("message", (data) => {
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.messageListeners.forEach((listener) => {
78
+ if (this.onmessage) {
24
79
  try {
25
- listener(event);
80
+ this.onmessage(event);
26
81
  } catch (error) {
27
- console.error("[node-webworker] Error in message listener:", error);
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 (listenerError) {
37
- console.error("[node-webworker] Error in error listener:", listenerError);
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
- console.warn("[node-webworker] Transferable objects not fully supported");
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.messageListeners.add(listener);
109
+ this.#messageListeners.add(listener);
54
110
  } else if (type === "error") {
55
- this.errorListeners.add(listener);
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.messageListeners.delete(listener);
116
+ this.#messageListeners.delete(listener);
63
117
  } else if (type === "error") {
64
- this.errorListeners.delete(listener);
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
- async terminate() {
71
- const exitCode = await this.nodeWorker.terminate();
72
- this.messageListeners.clear();
73
- this.errorListeners.clear();
74
- return exitCode;
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.nodeWorker;
136
+ return this.#nodeWorker;
81
137
  }
82
138
  };
83
139
  var src_default = Worker;