@gjsify/web-worker 0.0.0-stage → 0.64.16
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 +58 -2
- package/globals.mjs +9 -0
- package/lib/esm/_virtual/_rolldown/runtime.js +1 -0
- package/lib/esm/index.js +1 -0
- package/lib/esm/register.js +1 -0
- package/lib/esm/worker-scope.js +47 -0
- package/lib/esm/worker.js +1 -0
- package/lib/types/comlink.spec.d.ts +2 -0
- package/lib/types/index.d.ts +2 -0
- package/lib/types/register.d.ts +1 -0
- package/lib/types/register.gjs.spec.d.ts +2 -0
- package/lib/types/worker-scope.d.ts +5 -0
- package/lib/types/worker.d.ts +25 -0
- package/lib/types/worker.spec.d.ts +2 -0
- package/package.json +80 -5
package/README.md
CHANGED
|
@@ -1,3 +1,59 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @gjsify/web-worker
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
WHATWG `Worker` for GJS (and Node) over `worker_threads`. `new Worker(url)`, `postMessage`, `terminate`, `onmessage` / `onerror` / `onmessageerror`, and a `DedicatedWorkerGlobalScope`-shaped `self` inside the worker. [Comlink](https://github.com/GoogleChromeLabs/comlink) works unmodified.
|
|
4
|
+
|
|
5
|
+
Part of the [gjsify](https://github.com/gjsify/gjsify) project — Node.js and Web APIs for GJS (GNOME JavaScript).
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
gjsify install @gjsify/web-worker
|
|
11
|
+
|
|
12
|
+
# npm or yarn also work (e.g. adding it to an existing project):
|
|
13
|
+
npm install @gjsify/web-worker
|
|
14
|
+
yarn add @gjsify/web-worker
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Usage
|
|
18
|
+
|
|
19
|
+
```typescript
|
|
20
|
+
import { Worker } from '@gjsify/web-worker';
|
|
21
|
+
|
|
22
|
+
const worker = new Worker(new URL('./echo.worker.mjs', import.meta.url), { type: 'module', name: 'echo' });
|
|
23
|
+
worker.onmessage = (event) => console.log(event.data);
|
|
24
|
+
worker.postMessage({ hello: 'world' });
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
```javascript
|
|
28
|
+
// echo.worker.mjs
|
|
29
|
+
self.onmessage = (event) => self.postMessage({ echo: event.data, name: self.name });
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
With `gjsify build --globals auto`, a bare `Worker` reference is routed here on GJS and Node; browsers keep their own.
|
|
33
|
+
|
|
34
|
+
### Comlink
|
|
35
|
+
|
|
36
|
+
```typescript
|
|
37
|
+
import * as Comlink from 'comlink';
|
|
38
|
+
|
|
39
|
+
const api = Comlink.wrap<Api>(new Worker(new URL('./api.worker.mjs', import.meta.url)));
|
|
40
|
+
console.log(await api.add(2, 3));
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Behaviour
|
|
44
|
+
|
|
45
|
+
- Host: `postMessage(value, transfer | { transfer })`, `terminate()` (idempotent, silences further events), `addEventListener`, and the three `on…` attributes.
|
|
46
|
+
- An uncaught error in the worker is a cancellable `ErrorEvent`; if nothing handles it, it goes to `console.error`.
|
|
47
|
+
- Worker scope: `self`, `name`, `postMessage`, `addEventListener` / `removeEventListener` (`message`, `messageerror`; function or `handleEvent` listeners), `onmessage`, `onmessageerror`, `close()`.
|
|
48
|
+
- Relative script strings resolve against the working directory.
|
|
49
|
+
- Scripts load as ES modules; `type` is accepted but not distinguished.
|
|
50
|
+
|
|
51
|
+
## Limits
|
|
52
|
+
|
|
53
|
+
- On GJS the child is a bare `gjs` with no module resolution: the worker file must be self-contained. Bundle it with `gjsify build --app gjs`.
|
|
54
|
+
- Parent-side globals do not exist in the child; its global surface is the child's own.
|
|
55
|
+
- Detecting `new Worker(new URL(…, import.meta.url))` at build time and bundling the worker file is not part of this package yet (#2338).
|
|
56
|
+
|
|
57
|
+
## License
|
|
58
|
+
|
|
59
|
+
MIT
|
package/globals.mjs
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Re-exports the host's `Worker` global for browser and NativeScript builds.
|
|
3
|
+
*
|
|
4
|
+
* Node has no WHATWG `Worker`, so unlike its sibling `message-channel` this
|
|
5
|
+
* package is NOT native there: a Node build keeps the adapter, which runs over
|
|
6
|
+
* the native `node:worker_threads`. Only runtimes whose slot is `native` are
|
|
7
|
+
* routed here.
|
|
8
|
+
*/
|
|
9
|
+
export const Worker = globalThis.Worker;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var e=Object.defineProperty,__name=(t,n)=>e(t,`name`,{value:n,configurable:!0});export{__name};
|
package/lib/esm/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{Worker as e}from"./worker.js";export{e as Worker};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{Worker as e}from"./worker.js";import"./index.js";globalThis.Worker===void 0&&(globalThis.Worker=e);
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import"./_virtual/_rolldown/runtime.js";function buildWorkerSource(e,t,n){let r=`
|
|
2
|
+
${n?``:`const { parentPort } = require('node:worker_threads');`}
|
|
3
|
+
${`
|
|
4
|
+
const scope = globalThis;
|
|
5
|
+
const wrapped = { message: new Map(), messageerror: new Map() };
|
|
6
|
+
const event = (type, data) => ({ type, data, target: scope, currentTarget: scope, ports: [], origin: '', lastEventId: '', source: null });
|
|
7
|
+
|
|
8
|
+
scope.self = scope;
|
|
9
|
+
scope.name = %NAME%;
|
|
10
|
+
scope.postMessage = (value, options) => {
|
|
11
|
+
const transfer = Array.isArray(options) ? options : options && options.transfer ? options.transfer : undefined;
|
|
12
|
+
parentPort.postMessage(value, transfer);
|
|
13
|
+
};
|
|
14
|
+
scope.addEventListener = (type, listener) => {
|
|
15
|
+
const registry = wrapped[type];
|
|
16
|
+
if (!registry || registry.has(listener)) return;
|
|
17
|
+
const fn = typeof listener === 'function' ? listener : listener && listener.handleEvent ? (e) => listener.handleEvent(e) : null;
|
|
18
|
+
if (!fn) return;
|
|
19
|
+
const forward = type === 'message' ? (data) => fn(event(type, data)) : () => fn(event(type, null));
|
|
20
|
+
registry.set(listener, forward);
|
|
21
|
+
parentPort.on(type, forward);
|
|
22
|
+
};
|
|
23
|
+
scope.removeEventListener = (type, listener) => {
|
|
24
|
+
const registry = wrapped[type];
|
|
25
|
+
const forward = registry && registry.get(listener);
|
|
26
|
+
if (forward) { registry.delete(listener); parentPort.off(type, forward); }
|
|
27
|
+
};
|
|
28
|
+
scope.close = () => parentPort.close();
|
|
29
|
+
for (const type of ['message', 'messageerror']) {
|
|
30
|
+
let holder = null;
|
|
31
|
+
Object.defineProperty(scope, 'on' + type, {
|
|
32
|
+
configurable: true,
|
|
33
|
+
get: () => (holder ? holder.fn : null),
|
|
34
|
+
set: (fn) => {
|
|
35
|
+
if (holder) scope.removeEventListener(type, holder);
|
|
36
|
+
holder = typeof fn === 'function' ? { fn, handleEvent: (e) => fn.call(scope, e) } : null;
|
|
37
|
+
if (holder) scope.addEventListener(type, holder);
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
`.replace(`%NAME%`,JSON.stringify(t))}
|
|
42
|
+
const boot = async () => { await import(${JSON.stringify(e)}); };
|
|
43
|
+
`;return n?`${r}\nreturn boot();`:`${r}\nboot().catch((error) => {
|
|
44
|
+
const context = globalThis.__gjsify_worker_context;
|
|
45
|
+
if (context && context.crash) context.crash(error);
|
|
46
|
+
else queueMicrotask(() => { throw error; });
|
|
47
|
+
});`}export{buildWorkerSource};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import"./_virtual/_rolldown/runtime.js";import{buildWorkerSource as e}from"./worker-scope.js";import{ErrorEvent as t,EventTarget as n,MessageEvent as r}from"@gjsify/dom-events";import{cwd as i,versions as a}from"node:process";import{pathToFileURL as o}from"node:url";import{Worker as s}from"node:worker_threads";const c=typeof a.gjs==`string`;function resolveScript(e){if(e instanceof URL)return e.href;let t=String(e);return/^[a-zA-Z][a-zA-Z\d+.-]*:/.test(t)?new URL(t).href:new URL(t,o(`${i()}/`)).href}var Worker=class extends n{#e;#t=!1;#n={message:null,messageerror:null,error:null};constructor(n,i={}){super();let a=new s(e(resolveScript(n),i.name??``,c),{eval:!0});this.#e=a,a.on(`message`,e=>{this.#t||this.dispatchEvent(new r(`message`,{data:e}))}),a.on(`messageerror`,()=>{this.#t||this.dispatchEvent(new r(`messageerror`,{data:null}))}),a.on(`error`,e=>{if(this.#t)return;let n=new t(`error`,{cancelable:!0,message:e?.message??String(e),error:e});this.dispatchEvent(n),!n.defaultPrevented&&this.#n.error===null&&console.error(e)})}postMessage(e,t){if(this.#t)return;let n=Array.isArray(t)?t:t?.transfer;this.#e.postMessage(e,n)}terminate(){this.#t||(this.#t=!0,this.#e.terminate().catch(()=>{}))}#r(e,t){let n=this.#n[e];n&&this.removeEventListener(e,n),this.#n[e]=typeof t==`function`?t:null,this.#n[e]&&this.addEventListener(e,this.#n[e])}get onmessage(){return this.#n.message}set onmessage(e){this.#r(`message`,e)}get onmessageerror(){return this.#n.messageerror}set onmessageerror(e){this.#r(`messageerror`,e)}get onerror(){return this.#n.error}set onerror(e){this.#r(`error`,e)}get[Symbol.toStringTag](){return`Worker`}};export{Worker};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { EventTarget } from '@gjsify/dom-events';
|
|
2
|
+
export interface WorkerOptions {
|
|
3
|
+
/** Only `'module'` is meaningful: the script is always loaded as an ES module. */
|
|
4
|
+
type?: 'classic' | 'module';
|
|
5
|
+
/** Becomes `self.name` inside the worker. */
|
|
6
|
+
name?: string;
|
|
7
|
+
credentials?: 'omit' | 'same-origin' | 'include';
|
|
8
|
+
}
|
|
9
|
+
type Handler = ((event: any) => unknown) | null;
|
|
10
|
+
export declare class Worker extends EventTarget {
|
|
11
|
+
#private;
|
|
12
|
+
constructor(scriptURL: string | URL, options?: WorkerOptions);
|
|
13
|
+
postMessage(message: unknown, transfer?: readonly unknown[] | {
|
|
14
|
+
transfer?: readonly unknown[];
|
|
15
|
+
}): void;
|
|
16
|
+
terminate(): void;
|
|
17
|
+
get onmessage(): Handler;
|
|
18
|
+
set onmessage(fn: Handler);
|
|
19
|
+
get onmessageerror(): Handler;
|
|
20
|
+
set onmessageerror(fn: Handler);
|
|
21
|
+
get onerror(): Handler;
|
|
22
|
+
set onerror(fn: Handler);
|
|
23
|
+
get [Symbol.toStringTag](): string;
|
|
24
|
+
}
|
|
25
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,81 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
2
|
+
"name": "@gjsify/web-worker",
|
|
3
|
+
"version": "0.64.16",
|
|
4
|
+
"description": "WHATWG Worker for GJS (and Node) over worker_threads — new Worker(url), postMessage, terminate, onmessage/onerror, with a DedicatedWorkerGlobalScope-shaped scope inside the worker. Works with Comlink unmodified.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"module": "lib/esm/index.js",
|
|
7
|
+
"types": "lib/types/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./lib/types/index.d.ts",
|
|
11
|
+
"default": "./lib/esm/index.js"
|
|
12
|
+
},
|
|
13
|
+
"./register": {
|
|
14
|
+
"types": "./lib/types/register.d.ts",
|
|
15
|
+
"default": "./lib/esm/register.js"
|
|
16
|
+
},
|
|
17
|
+
"./globals": "./globals.mjs"
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"lib",
|
|
21
|
+
"globals.mjs"
|
|
22
|
+
],
|
|
23
|
+
"sideEffects": [
|
|
24
|
+
"./lib/esm/register.js",
|
|
25
|
+
"./globals.mjs"
|
|
26
|
+
],
|
|
27
|
+
"scripts": {
|
|
28
|
+
"clear": "gjsify clear lib tsconfig.tsbuildinfo tsconfig.types.tsbuildinfo fixtures test.gjs.mjs test.node.mjs",
|
|
29
|
+
"check": "gjsify tsc --noEmit",
|
|
30
|
+
"build": "gjsify run build:gjsify && gjsify run build:types",
|
|
31
|
+
"build:gjsify": "gjsify build --library 'src/**/*.{ts,js}' --exclude 'src/**/*.spec.{mts,ts}' 'src/test.{mts,ts}'",
|
|
32
|
+
"build:types": "gjsify tsc",
|
|
33
|
+
"build:test": "gjsify run build:test:gjs && gjsify run build:test:node",
|
|
34
|
+
"prebuild:test:fixtures": "gjsify copy src/fixtures/*.mjs fixtures/",
|
|
35
|
+
"build:test:gjs": "gjsify run prebuild:test:fixtures && gjsify build src/bundled-fixtures/comlink.worker.mjs --app gjs --outfile fixtures/comlink.worker.bundle.mjs && gjsify build src/test.mts --app gjs --outfile test.gjs.mjs",
|
|
36
|
+
"build:test:browser": "gjsify build src/test.browser.mts --app browser --outfile dist/test.browser.mjs",
|
|
37
|
+
"build:test:node": "gjsify run prebuild:test:fixtures && gjsify build src/bundled-fixtures/comlink.worker.mjs --app node --outfile fixtures/comlink.worker.bundle.mjs && gjsify build src/test.mts --app node --outfile test.node.mjs",
|
|
38
|
+
"test": "gjsify run build:gjsify && gjsify run test:node && gjsify run test:gjs",
|
|
39
|
+
"test:gjs": "gjsify run build:test:gjs && gjsify run test.gjs.mjs",
|
|
40
|
+
"test:node": "gjsify run build:test:node && node test.node.mjs"
|
|
41
|
+
},
|
|
42
|
+
"keywords": [
|
|
43
|
+
"gjs",
|
|
44
|
+
"web",
|
|
45
|
+
"worker",
|
|
46
|
+
"web-worker",
|
|
47
|
+
"comlink"
|
|
48
|
+
],
|
|
49
|
+
"devDependencies": {
|
|
50
|
+
"@gjsify/cli": "^0.64.16",
|
|
51
|
+
"@gjsify/node-globals": "^0.64.16",
|
|
52
|
+
"@gjsify/unit": "^0.64.16",
|
|
53
|
+
"@types/node": "^25.9.2",
|
|
54
|
+
"comlink": "^4.4.2",
|
|
55
|
+
"typescript": "^6.0.3"
|
|
56
|
+
},
|
|
57
|
+
"dependencies": {
|
|
58
|
+
"@gjsify/dom-events": "^0.64.16",
|
|
59
|
+
"@gjsify/worker_threads": "^0.64.16"
|
|
60
|
+
},
|
|
61
|
+
"license": "MIT",
|
|
62
|
+
"author": "Pascal Garber <pascal@artandcode.studio>",
|
|
63
|
+
"gjsify": {
|
|
64
|
+
"runtimes": {
|
|
65
|
+
"gjs": "polyfill",
|
|
66
|
+
"node": "polyfill",
|
|
67
|
+
"browser": "native",
|
|
68
|
+
"nativescript": "native"
|
|
69
|
+
},
|
|
70
|
+
"tier": 1
|
|
71
|
+
},
|
|
72
|
+
"repository": {
|
|
73
|
+
"type": "git",
|
|
74
|
+
"url": "git+https://github.com/gjsify/gjsify.git",
|
|
75
|
+
"directory": "packages/web/web-worker"
|
|
76
|
+
},
|
|
77
|
+
"bugs": {
|
|
78
|
+
"url": "https://github.com/gjsify/gjsify/issues"
|
|
79
|
+
},
|
|
80
|
+
"homepage": "https://github.com/gjsify/gjsify/tree/main/packages/web/web-worker#readme"
|
|
81
|
+
}
|