promise-worker-bi 6.0.0-beta.0 → 6.0.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 CHANGED
@@ -119,8 +119,8 @@ For a Web Worker, we could just use [the normal `error` event](https://developer
119
119
  Therefore, promise-worker-bi includes a unified API that works in Web Workers and Shared Workers. Include some code like this in your host:
120
120
 
121
121
  ```js
122
- promiseWorker.registerError((error) => {
123
- console.log("Error inside worker!", err);
122
+ promiseWorker.addEventListener("error", (errorError) => {
123
+ console.log("Error inside worker!", errorError.error);
124
124
  });
125
125
  ```
126
126
 
@@ -140,7 +140,7 @@ const response1 = await promiseWorker.postMessage({
140
140
  });
141
141
 
142
142
  const response2 = await promiseWorker.postMessage({
143
- type: "en",
143
+ type: "fr",
144
144
  });
145
145
  ```
146
146
 
@@ -285,13 +285,17 @@ Create a new instance of `PWBHost`, using the given worker.
285
285
 
286
286
  - `worker` - the `Worker`, `SharedWorker` or [PseudoWorker](https://github.com/nolanlawson/pseudo-worker) to use.
287
287
 
288
- #### `promiseWorker.registerError((error: Error) => void)`
288
+ #### `promiseWorker.addEventListener("error", (event: ErrorEvent) => void)`
289
+
290
+ When an error in your web/shared worker process occurs that is _not_ directly in response to a `promiseWorker.postMessage` call, you can see it by listening to the "error" event here.
291
+
292
+ `promiseWorker` is EventTarget so you can use `addEventListener`, `removeEventListener`, and all their normal options.
289
293
 
290
- This should only be called in the browser, not in a worker.
294
+ #### `promiseWorker.addEventListener("close", (event: Event) => void)`
291
295
 
292
- When an error in your web/shared worker process occurs that is _not_ directly in response to a `promiseWorker.postMessage` call, it will be sent to the callback you provide here to `promiseWorker.registerError`.
296
+ There is no built-in API to listen for when a web/shared worker closes, which can happen in various situations such as the OS killing it to free up memory. promise-worker-bi includes a "close" event you can use for this purpose.
293
297
 
294
- Although [normally stack traces are not sent from the worker to the main thread](https://github.com/mknichel/javascript-errors/blob/master/README.md#dedicated-workers), promise-worker-bi magically works around this problem so you will see a stack trace in `error`.
298
+ `promiseWorker` is EventTarget so you can use `addEventListener`, `removeEventListener`, and all their normal options.
295
299
 
296
300
  ### Worker bundle
297
301
 
@@ -301,17 +305,17 @@ Create a new instance of `PWBWorker`.
301
305
 
302
306
  ### Both bundles
303
307
 
304
- #### `promiseWorker.register((message: any, hostID?: number) => any)`
308
+ #### `promiseWorker.register((message: unknown, hostID?: number) => unknown)`
305
309
 
306
310
  Register a message handler wherever you will be receiving messages: in the worker, in the browser, or both. Your handler consumes a message and returns a promise or value.
307
311
 
308
312
  The `hostID` parameter is only defined inside a shared worker, in which case it is a unique number identifying the host that the message came from.
309
313
 
310
- The return value of the callback function (shown above as `any`) must resolve to something [structured cloneable](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm).
314
+ The return value of the callback function (shown above as `unknown`) must resolve to something [structured cloneable](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm).
311
315
 
312
- If you want to [transfer an object](https://developer.mozilla.org/en-US/docs/Glossary/Transferable_objects) in the return value of the callback, then the return type should be `{ message: any, _PWB_TRANSFER: Transferable[] }` rather than just `any`.
316
+ If you want to [transfer an object](https://developer.mozilla.org/en-US/docs/Glossary/Transferable_objects) in the return value of the callback, then the return type should be `{ message: unknown, _PWB_TRANSFER: Transferable[] }` rather than just `unknown`.
313
317
 
314
- #### `promiseWorker.postMessage(message: any, hostID?: number, transfer?: Transferable[]): Promise<any>`
318
+ #### `promiseWorker.postMessage(message: unknown, hostID?: number, transfer?: Transferable[]): Promise<unknown>`
315
319
 
316
320
  Send a message to the browser or worker and return a Promise. `message` must resolve to something [structured cloneable](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm).
317
321
 
package/dist/index.d.ts CHANGED
@@ -1,57 +1,64 @@
1
- //#region src/fakeError.d.ts
2
- type FakeError = {
3
- name: string;
4
- message: string;
5
- stack?: string;
6
- };
7
- //#endregion
8
1
  //#region src/message.d.ts
9
2
  declare const MSGTYPE_QUERY = 0;
10
3
  declare const MSGTYPE_RESPONSE = 1;
11
4
  declare const MSGTYPE_HOST_ID = 2;
12
5
  declare const MSGTYPE_HOST_LOCK = 3;
13
- declare const MSGTYPE_WORKER_ERROR = 4;
14
- type QueryMessage = [typeof MSGTYPE_QUERY, number, unknown] | [typeof MSGTYPE_QUERY, number, unknown, number | undefined];
15
- type ResponseMessage = [typeof MSGTYPE_RESPONSE, number, FakeError] | [typeof MSGTYPE_RESPONSE, number, null, unknown];
16
- type HostIdMessage = [typeof MSGTYPE_HOST_ID, number];
17
- type HostLockMessage = [typeof MSGTYPE_HOST_LOCK, number, string];
18
- type WorkerErrorMessage = [typeof MSGTYPE_WORKER_ERROR, FakeError];
19
- type Message = QueryMessage | ResponseMessage | HostIdMessage | HostLockMessage | WorkerErrorMessage;
6
+ declare const MSGTYPE_WORKER_LOCK = 4;
7
+ declare const MSGTYPE_WORKER_ERROR = 5;
8
+ type QueryMessage = [type: typeof MSGTYPE_QUERY, messageId: number, query: unknown] | [type: typeof MSGTYPE_QUERY, messageId: number, query: unknown, hostId: number | undefined];
9
+ type ResponseMessage = [type: typeof MSGTYPE_RESPONSE, messageId: number, error: Error] | [type: typeof MSGTYPE_RESPONSE, messageId: number, error: null, result: unknown];
10
+ type HostIdMessage = [type: typeof MSGTYPE_HOST_ID, hostId: number] | [type: typeof MSGTYPE_HOST_ID, hostId: number, workerLockId: string];
11
+ type HostLockMessage = [type: typeof MSGTYPE_HOST_LOCK, hostID: number, hostLockId: string];
12
+ type WorkerLockMessage = [type: typeof MSGTYPE_WORKER_LOCK, workerLockId: string];
13
+ type WorkerErrorMessage = [type: typeof MSGTYPE_WORKER_ERROR, error: Error];
14
+ type Message = QueryMessage | ResponseMessage | HostIdMessage | HostLockMessage | WorkerLockMessage | WorkerErrorMessage;
20
15
  //#endregion
21
16
  //#region src/PWBBase.d.ts
22
- type QueryCallback = (a: unknown, b: number | undefined) => any;
23
- declare abstract class PWBBase {
17
+ type QueryCallback = (a: unknown, b: number | undefined) => unknown;
18
+ type EventMap = Record<string, Event>;
19
+ declare abstract class PWBBase<Events extends EventMap> extends EventTarget {
24
20
  protected _callbacks: Map<number, (a: Error | null, b: unknown) => void>;
25
21
  protected _queryCallback: QueryCallback;
26
22
  constructor();
23
+ addEventListener<K extends keyof Events>(type: K, listener: ((event: Events[K]) => void) | {
24
+ handleEvent(object: Events[K]): void;
25
+ } | null, options?: AddEventListenerOptions | boolean): void;
26
+ dispatchEvent<K extends keyof Events>(event: Events[K]): boolean;
27
+ removeEventListener<K extends keyof Events>(type: K, listener: ((event: Events[K]) => void) | {
28
+ handleEvent(object: Events[K]): void;
29
+ } | null, options?: EventListenerOptions | boolean): void;
27
30
  register(cb: QueryCallback): void;
28
31
  protected abstract _postMessage(obj: Message, hostID?: number | undefined, transfer?: Transferable[] | undefined): void;
29
32
  private _postResponse;
30
33
  private _handleQuery;
31
- protected _onMessageCommon(e: MessageEvent): HostIdMessage | HostLockMessage | WorkerErrorMessage | undefined;
34
+ protected _onMessageCommon(e: MessageEvent): [type: 2, hostId: number] | [type: 2, hostId: number, workerLockId: string] | HostLockMessage | WorkerLockMessage | WorkerErrorMessage | undefined;
32
35
  }
33
36
  //#endregion
34
37
  //#region src/PWBHost.d.ts
35
- type ErrorCallback = (a: Error) => void;
36
- export declare class PWBHost extends PWBBase {
37
- private _errorCallback;
38
+ type HostEvents = {
39
+ close: Event;
40
+ error: ErrorEvent;
41
+ };
42
+ export declare class PWBHost extends PWBBase<HostEvents> {
38
43
  private _hostID;
39
44
  private _hostIDQueue;
40
45
  private _worker;
41
46
  constructor(worker: SharedWorker | Worker);
42
- registerError(cb: ErrorCallback): void;
43
47
  protected _postMessage(obj: QueryMessage | ResponseMessage | HostLockMessage, _hostID?: unknown, transfer?: Transferable[] | undefined): void;
44
48
  postMessage(userMessage: unknown, _hostID?: undefined, transfer?: Transferable[] | undefined): Promise<any>;
49
+ private handleWorkerLock;
45
50
  private _onMessage;
46
51
  }
47
52
  //#endregion
48
53
  //#region src/PWBWorker.d.ts
49
- export declare class PWBWorker extends PWBBase {
54
+ type WorkerEvents = Record<string, Event>;
55
+ export declare class PWBWorker extends PWBBase<WorkerEvents> {
50
56
  private _hosts;
51
57
  private _maxHostID;
52
58
  private _sharedWorker;
59
+ private _workerLockAcquired;
53
60
  constructor();
54
- protected _postMessage(message: QueryMessage | ResponseMessage | HostIdMessage | WorkerErrorMessage, targetHostID?: number | undefined, transfer?: Transferable[] | undefined): void;
61
+ protected _postMessage(message: QueryMessage | ResponseMessage | HostIdMessage | WorkerLockMessage | WorkerErrorMessage, targetHostID?: number | undefined, transfer?: Transferable[] | undefined): void;
55
62
  postMessage(userMessage: unknown, targetHostID?: number | undefined, transfer?: Transferable[] | undefined): Promise<unknown>;
56
63
  private _onMessage;
57
64
  }
package/dist/index.js CHANGED
@@ -1,46 +1,36 @@
1
- //#region src/fakeError.ts
2
- const isFakeError = (x) => {
3
- if (typeof x !== "object" || x === null) return false;
4
- const candidate = x;
5
- if (typeof candidate.name !== "string" || typeof candidate.message !== "string") return false;
6
- if (candidate.stack !== void 0 && typeof candidate.stack !== "string") return false;
1
+ const isError = (error) => {
2
+ if (Error.isError) return Error.isError(error);
7
3
  return true;
8
4
  };
9
- const toFakeError = (error) => {
10
- const fakeError = {
11
- name: error.name,
12
- message: error.message
13
- };
14
- if (typeof error.stack === "string") fakeError.stack = error.stack;
15
- return fakeError;
16
- };
17
- const fromFakeError = (fakeError) => {
18
- return Object.assign(/* @__PURE__ */ new Error(), fakeError);
19
- };
20
5
  const parseMessage = (message) => {
21
6
  if (!Array.isArray(message) || message.length < 2 || message.length > 4) return;
22
7
  const type = message[0];
23
8
  if (type === 0) {
24
9
  if (typeof message[1] !== "number") throw new Error("Invalid messageID");
25
- if (typeof message[3] !== "number" && message[3] !== void 0) throw new Error("Invalid hostID");
10
+ if (message[3] !== void 0 && typeof message[3] !== "number") throw new Error("Invalid hostID");
26
11
  return message;
27
12
  }
28
13
  if (type === 1) {
29
14
  if (typeof message[1] !== "number") throw new Error("Invalid messageID");
30
- if (message[2] !== null && !isFakeError(message[2])) throw new Error("Invalid error");
15
+ if (message[2] !== null && !isError(message[2])) throw new Error("Invalid error");
31
16
  return message;
32
17
  }
33
18
  if (type === 2) {
34
19
  if (typeof message[1] !== "number") throw new Error("Invalid hostID");
20
+ if (message[2] !== void 0 && typeof message[2] !== "string") throw new Error("Invalid workerLockId");
35
21
  return message;
36
22
  }
37
23
  if (type === 3) {
38
24
  if (typeof message[1] !== "number") throw new Error("Invalid hostID");
39
- if (typeof message[2] !== "string") throw new Error("Invalid lockId");
25
+ if (typeof message[2] !== "string") throw new Error("Invalid hostLockId");
40
26
  return message;
41
27
  }
42
28
  if (type === 4) {
43
- if (!isFakeError(message[1])) throw new Error("Invalid error");
29
+ if (typeof message[1] !== "string") throw new Error("Invalid workerLockId");
30
+ return message;
31
+ }
32
+ if (type === 5) {
33
+ if (!isError(message[1])) throw new Error("Invalid error");
44
34
  return message;
45
35
  }
46
36
  throw new Error("Invalid message type");
@@ -49,20 +39,30 @@ const parseMessage = (message) => {
49
39
  //#region src/PWBBase.ts
50
40
  const isPromise = (obj) => !!obj && (typeof obj === "object" || typeof obj === "function") && typeof obj.then === "function";
51
41
  const logError = (err) => {
52
- console.error("Error in Worker:");
42
+ console.error("Error in worker:");
53
43
  console.error(err);
54
44
  };
55
45
  const isSharedWorker = (worker) => {
56
46
  return worker.port !== void 0;
57
47
  };
58
- var PWBBase = class {
48
+ var PWBBase = class extends EventTarget {
59
49
  _callbacks;
60
50
  _queryCallback;
61
51
  constructor() {
52
+ super();
62
53
  this._callbacks = /* @__PURE__ */ new Map();
63
54
  this._queryCallback = () => {};
64
55
  this._onMessage = this._onMessage.bind(this);
65
56
  }
57
+ addEventListener(type, listener, options) {
58
+ super.addEventListener(type, listener, options);
59
+ }
60
+ dispatchEvent(event) {
61
+ return super.dispatchEvent(event);
62
+ }
63
+ removeEventListener(type, listener, options) {
64
+ super.removeEventListener(type, listener, options);
65
+ }
66
66
  register(cb) {
67
67
  this._queryCallback = cb;
68
68
  }
@@ -72,7 +72,7 @@ var PWBBase = class {
72
72
  this._postMessage([
73
73
  1,
74
74
  messageID,
75
- toFakeError(error)
75
+ error
76
76
  ], hostID);
77
77
  } else if (typeof result === "object" && result !== null && Object.hasOwn(result, "message") && Object.hasOwn(result, "_PWB_TRANSFER")) this._postMessage([
78
78
  1,
@@ -97,7 +97,7 @@ var PWBBase = class {
97
97
  else result.then((finalResult) => {
98
98
  this._postResponse(messageID, null, finalResult, hostID);
99
99
  }, (finalError) => {
100
- this._postResponse(messageID, finalError, hostID);
100
+ this._postResponse(messageID, finalError, void 0, hostID);
101
101
  });
102
102
  } catch (err) {
103
103
  this._postResponse(messageID, err);
@@ -112,7 +112,7 @@ var PWBBase = class {
112
112
  }
113
113
  if (message[0] === 1) {
114
114
  const messageID = message[1];
115
- const error = message[2] === null ? null : fromFakeError(message[2]);
115
+ const error = message[2] === null ? null : message[2];
116
116
  const callback = this._callbacks.get(messageID);
117
117
  if (callback === void 0) return;
118
118
  this._callbacks.delete(messageID);
@@ -126,7 +126,6 @@ var PWBBase = class {
126
126
  //#region src/PWBHost.ts
127
127
  let nextMessageID$1 = 0;
128
128
  var PWBHost = class extends PWBBase {
129
- _errorCallback;
130
129
  _hostID;
131
130
  _hostIDQueue;
132
131
  _worker;
@@ -140,13 +139,6 @@ var PWBHost = class extends PWBBase {
140
139
  this._worker = worker;
141
140
  this._hostIDQueue = [];
142
141
  }
143
- registerError(cb) {
144
- this._errorCallback = cb;
145
- this._worker.addEventListener("error", (e) => {
146
- e.preventDefault();
147
- e.stopPropagation();
148
- });
149
- }
150
142
  _postMessage(obj, _hostID, transfer) {
151
143
  if (isSharedWorker(this._worker)) {
152
144
  if (transfer) this._worker.port.postMessage(obj, transfer);
@@ -177,6 +169,11 @@ var PWBHost = class extends PWBBase {
177
169
  else actuallyPostMessage(resolve, reject);
178
170
  });
179
171
  }
172
+ handleWorkerLock(workerLockId) {
173
+ navigator.locks.request(workerLockId, { mode: "shared" }, async () => {
174
+ this.dispatchEvent(new Event("close"));
175
+ });
176
+ }
180
177
  _onMessage(e) {
181
178
  const message = this._onMessageCommon(e);
182
179
  if (!message) return;
@@ -190,21 +187,20 @@ var PWBHost = class extends PWBBase {
190
187
  }, 0);
191
188
  this._hostIDQueue = void 0;
192
189
  }
193
- const lockId = `pwb-${Math.random()}`;
194
- navigator.locks.request(lockId, async () => {
190
+ if (message[2] !== void 0) this.handleWorkerLock(message[2]);
191
+ const hostLockId = `pwb-host-${Math.random()}`;
192
+ navigator.locks.request(hostLockId, async () => {
195
193
  this._postMessage([
196
194
  3,
197
195
  hostID,
198
- lockId
196
+ hostLockId
199
197
  ]);
200
198
  return new Promise(() => {});
201
199
  });
202
- } else if (message[0] === 4) {
203
- if (message[1] !== null) {
204
- const error = fromFakeError(message[1]);
205
- if (this._errorCallback !== void 0) this._errorCallback(error);
206
- }
207
- }
200
+ } else if (message[0] === 5) {
201
+ const error = message[1];
202
+ this.dispatchEvent(new ErrorEvent("error", { error }));
203
+ } else if (message[0] === 4) this.handleWorkerLock(message[1]);
208
204
  }
209
205
  };
210
206
  //#endregion
@@ -214,31 +210,49 @@ var PWBWorker = class extends PWBBase {
214
210
  _hosts = /* @__PURE__ */ new Map();
215
211
  _maxHostID = -1;
216
212
  _sharedWorker;
213
+ _workerLockAcquired = false;
217
214
  constructor() {
218
215
  super();
216
+ const workerLockId = `pwb-worker-${Math.random()}`;
217
+ navigator.locks.request(workerLockId, async () => {
218
+ this._workerLockAcquired = true;
219
+ this._postMessage([4, workerLockId]);
220
+ return new Promise(() => {});
221
+ });
219
222
  if (typeof SharedWorkerGlobalScope !== "undefined" && self instanceof SharedWorkerGlobalScope) {
220
223
  this._sharedWorker = true;
221
- self.addEventListener("connect", (e) => {
224
+ const _self = self;
225
+ _self.addEventListener("connect", (e) => {
222
226
  const port = e.ports[0];
223
227
  port.addEventListener("message", (e2) => this._onMessage(e2));
224
228
  port.start();
225
229
  this._maxHostID += 1;
226
230
  const hostID = this._maxHostID;
227
231
  this._hosts.set(hostID, { port });
228
- this._postMessage([2, hostID], hostID);
232
+ let message;
233
+ if (this._workerLockAcquired) message = [
234
+ 2,
235
+ hostID,
236
+ workerLockId
237
+ ];
238
+ else message = [2, hostID];
239
+ this._postMessage(message, hostID);
229
240
  });
230
- self.addEventListener("error", (e) => {
241
+ _self.addEventListener("error", (e) => {
231
242
  logError(e.error);
243
+ e.preventDefault();
232
244
  const hostID = this._hosts.keys().next().value;
233
- if (hostID !== void 0) this._postMessage([4, toFakeError(e.error)], hostID);
245
+ if (hostID !== void 0) this._postMessage([5, e.error], hostID);
234
246
  });
235
247
  } else {
236
248
  this._sharedWorker = false;
237
- self.addEventListener("message", this._onMessage);
249
+ const _self = self;
250
+ _self.addEventListener("message", this._onMessage);
238
251
  this._postMessage([2, 0], 0);
239
- self.addEventListener("error", (e) => {
252
+ _self.addEventListener("error", (e) => {
240
253
  logError(e.error);
241
- this._postMessage([4, toFakeError(e.error)]);
254
+ e.preventDefault();
255
+ this._postMessage([5, e.error]);
242
256
  });
243
257
  }
244
258
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "promise-worker-bi",
3
- "version": "6.0.0-beta.0",
3
+ "version": "6.0.0",
4
4
  "description": "Promise-based messaging for Web Workers and Shared Workers",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -14,8 +14,8 @@
14
14
  "scripts": {
15
15
  "prepack": "pnpm run build",
16
16
  "build": "tsdown",
17
- "lint": "pnpm run build && tsc && eslint .",
18
- "test": "pnpm run build && vitest run",
17
+ "lint": "tsc && eslint .",
18
+ "test": "vitest run",
19
19
  "prepare": "husky"
20
20
  },
21
21
  "repository": {