@gjsify/web-worker 0.0.0-stage → 0.64.14

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
@@ -1,3 +1,59 @@
1
- # Temporary Holding Version
1
+ # @gjsify/web-worker
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
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};
@@ -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,2 @@
1
+ declare const _default: () => Promise<void>;
2
+ export default _default;
@@ -0,0 +1,2 @@
1
+ export { Worker } from './worker.js';
2
+ export type { WorkerOptions } from './worker.js';
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,2 @@
1
+ declare const _default: () => Promise<void>;
2
+ export default _default;
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Eval-mode source for a worker: install the scope, then load `href`.
3
+ * `gjs` selects the tail that RETURNS the import so the bootstrap awaits it.
4
+ */
5
+ export declare function buildWorkerSource(href: string, name: string, gjs: boolean): string;
@@ -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 {};
@@ -0,0 +1,2 @@
1
+ declare const _default: () => Promise<void>;
2
+ export default _default;
package/package.json CHANGED
@@ -1,6 +1,81 @@
1
1
  {
2
- "name": "@gjsify/web-worker",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
2
+ "name": "@gjsify/web-worker",
3
+ "version": "0.64.14",
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.14",
51
+ "@gjsify/node-globals": "^0.64.14",
52
+ "@gjsify/unit": "^0.64.14",
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.14",
59
+ "@gjsify/worker_threads": "^0.64.14"
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
+ }