@gjsify/ws 0.4.29 → 0.4.31
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/lib/esm/stream.js +1 -1
- package/lib/esm/websocket.js +1 -1
- package/lib/types/stream.d.ts +23 -1
- package/lib/types/websocket-server.d.ts +25 -1
- package/lib/types/websocket.d.ts +21 -5
- package/package.json +8 -8
package/lib/esm/stream.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import"./_virtual/_rolldown/runtime.js";import{Duplex as e}from"node:stream";function emitClose(e){e.emit(`close`)}function duplexOnEnd(){!this.destroyed&&this._writableState?.finished&&this.destroy()}function duplexOnError(e){this.removeListener(`error`,duplexOnError),this.destroy(),this.listenerCount(`error`)===0&&this.emit(`error`,e)}function createWebSocketStream(t,n={}){let r=!0,i=new e({...n,autoDestroy:!1,emitClose:!1,objectMode:!1,writableObjectMode:!1});
|
|
1
|
+
import"./_virtual/_rolldown/runtime.js";import{Duplex as e}from"node:stream";function emitClose(e){e.emit(`close`)}function duplexOnEnd(){!this.destroyed&&this._writableState?.finished&&this.destroy()}function duplexOnError(e){this.removeListener(`error`,duplexOnError),this.destroy(),this.listenerCount(`error`)===0&&this.emit(`error`,e)}function createWebSocketStream(t,n={}){let r=!0,i=t,a=new e({...n,autoDestroy:!1,emitClose:!1,objectMode:!1,writableObjectMode:!1});i.on(`message`,(...e)=>{let t=e[0],n=e[1],r;r=n||a.readableObjectMode?t:typeof t==`string`?Buffer.from(t):t,!a.push(r)&&typeof i.pause==`function`&&i.pause()}),i.once(`error`,(...e)=>{let t=e[0];a.destroyed||(r=!1,a.destroy(t))}),i.once(`close`,()=>{a.destroyed||a.push(null)});let o=a;return o._destroy=function(e,t){if(i.readyState===i.CLOSED){t(e),process.nextTick(emitClose,a);return}let n=!1;i.once(`error`,(...e)=>{let r=e[0];n=!0,t(r)}),i.once(`close`,()=>{n||t(e),process.nextTick(emitClose,a)}),r&&i.terminate()},o._final=function(e){if(i.readyState===i.CONNECTING){i.once(`open`,()=>o._final(e));return}if(i.readyState===i.CLOSED||i.readyState===i.CLOSING){e();return}i.once(`close`,()=>e()),i.close()},a._read=function(){typeof i.resume==`function`&&i.resume()},a._write=function(e,t,n){if(i.readyState===i.CONNECTING){i.once(`open`,()=>a._write(e,t,n));return}i.send(e,n)},a.on(`end`,duplexOnEnd),a.on(`error`,duplexOnError),a}export{createWebSocketStream};
|
package/lib/esm/websocket.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import"./_virtual/_rolldown/runtime.js";import{BINARY_TYPES as e}from"./constants.js";import{EventEmitter as t}from"@gjsify/events";import{Buffer as n}from"@gjsify/buffer";import{WebSocket as r}from"@gjsify/websocket";var WebSocket=class extends t{static CONNECTING=0;static OPEN=1;static CLOSING=2;static CLOSED=3;CONNECTING=0;OPEN=1;CLOSING=2;CLOSED=3;readyState=0;url=``;protocol=``;extensions=``;bufferedAmount=0;binaryType=`nodebuffer`;_native=null;constructor(e,t,n={}){if(super(),e===null){queueMicrotask(()=>this._fail(`Constructing ws.WebSocket with null address is not supported on Gjs`));return}this.url=typeof e==`string`?e:String(e);let r=this._resolveProtocols(t,n);this._openNative(this.url,r,n)}_resolveProtocols(e,t){if(e!==void 0)return Array.isArray(e)?e:[e];if(t.protocols!==void 0)return Array.isArray(t.protocols)?t.protocols:[t.protocols];if(t.protocol!==void 0)return[t.protocol]}_openNative(e,t,n){if(typeof r!=`function`){queueMicrotask(()=>this._fail("@gjsify/websocket provided no WebSocket constructor. On Node.js 22+ globalThis.WebSocket is native; on older Node install `ws` directly, or ensure globalThis.WebSocket is set before @gjsify/ws is imported."));return}let i={perMessageDeflate:n.perMessageDeflate!==!1,headers:n.headers,origin:n.origin,handshakeTimeout:n.handshakeTimeout};try{this._native=new r(e,t,i)}catch(e){queueMicrotask(()=>this._fail(e instanceof Error?e:Error(String(e))));return}this._native.binaryType=`arraybuffer`,this._native.addEventListener(`open`,()=>this._onOpen()),this._native.addEventListener(`message`,e=>this._onMessage(e)),this._native.addEventListener(`close`,e=>this._onClose(e)),this._native.addEventListener(`error`,e=>this._onError(e))}_fail(e){let t=typeof e==`string`?Error(e):e;this.readyState=3,this.emit(`error`,t),this._dispatchEvent(`error`,{error:t,message:t.message}),this.emit(`close`,1006,n.from(t.message)),this._dispatchEvent(`close`,{code:1006,reason:t.message,wasClean:!1})}_onOpen(){this.readyState=1,typeof this._native
|
|
1
|
+
import"./_virtual/_rolldown/runtime.js";import{BINARY_TYPES as e}from"./constants.js";import{EventEmitter as t}from"@gjsify/events";import{Buffer as n}from"@gjsify/buffer";import{WebSocket as r}from"@gjsify/websocket";var WebSocket=class extends t{static CONNECTING=0;static OPEN=1;static CLOSING=2;static CLOSED=3;CONNECTING=0;OPEN=1;CLOSING=2;CLOSED=3;readyState=0;url=``;protocol=``;extensions=``;bufferedAmount=0;binaryType=`nodebuffer`;_native=null;constructor(e,t,n={}){if(super(),e===null){queueMicrotask(()=>this._fail(`Constructing ws.WebSocket with null address is not supported on Gjs`));return}this.url=typeof e==`string`?e:String(e);let r=this._resolveProtocols(t,n);this._openNative(this.url,r,n)}_resolveProtocols(e,t){if(e!==void 0)return Array.isArray(e)?e:[e];if(t.protocols!==void 0)return Array.isArray(t.protocols)?t.protocols:[t.protocols];if(t.protocol!==void 0)return[t.protocol]}_openNative(e,t,n){if(typeof r!=`function`){queueMicrotask(()=>this._fail("@gjsify/websocket provided no WebSocket constructor. On Node.js 22+ globalThis.WebSocket is native; on older Node install `ws` directly, or ensure globalThis.WebSocket is set before @gjsify/ws is imported."));return}let i={perMessageDeflate:n.perMessageDeflate!==!1,headers:n.headers,origin:n.origin,handshakeTimeout:n.handshakeTimeout};try{this._native=new r(e,t,i)}catch(e){queueMicrotask(()=>this._fail(e instanceof Error?e:Error(String(e))));return}this._native.binaryType=`arraybuffer`,this._native.addEventListener(`open`,()=>this._onOpen()),this._native.addEventListener(`message`,e=>this._onMessage(e)),this._native.addEventListener(`close`,e=>this._onClose(e)),this._native.addEventListener(`error`,e=>this._onError(e))}_fail(e){let t=typeof e==`string`?Error(e):e;this.readyState=3,this.emit(`error`,t),this._dispatchEvent(`error`,{error:t,message:t.message}),this.emit(`close`,1006,n.from(t.message)),this._dispatchEvent(`close`,{code:1006,reason:t.message,wasClean:!1})}_onOpen(){this.readyState=1,typeof this._native?.protocol==`string`&&(this.protocol=this._native.protocol),typeof this._native?.extensions==`string`&&(this.extensions=this._native.extensions),this.emit(`open`),this._dispatchEvent(`open`,{})}_onMessage(e){let t=e?.data,n,r=!1;typeof t==`string`?(n=t,r=!1):t instanceof ArrayBuffer?(r=!0,n=this._decodeBinary(t)):ArrayBuffer.isView(t)?(r=!0,n=this._decodeBinary(t.buffer)):(n=t,r=!1),this.emit(`message`,n,r),this._dispatchEvent(`message`,{data:n,type:r?`binary`:`text`})}_decodeBinary(e){switch(this.binaryType){case`arraybuffer`:return e;case`fragments`:return[n.from(e)];case`blob`:{let t=globalThis.Blob;return t?new t([new Uint8Array(e)]):n.from(e)}default:return n.from(e)}}_onClose(e){let t=typeof e?.code==`number`?e.code:1006,r=typeof e?.reason==`string`?e.reason:``;this.readyState=3,this.emit(`close`,t,n.from(r)),this._dispatchEvent(`close`,{code:t,reason:r,wasClean:!!e?.wasClean})}_onError(e){let t=typeof e?.message==`string`&&e.message||`WebSocket error`,n=e?.error instanceof Error?e.error:Error(t);this.emit(`error`,n),this._dispatchEvent(`error`,{error:n,message:t})}send(e,t,n){let r=typeof t==`function`?t:n;if(this.readyState===0)throw Error(`WebSocket is not open: readyState 0 (CONNECTING)`);if(this.readyState!==1){let e=Error(`WebSocket is not open: readyState `+this.readyState);queueMicrotask(r?()=>r(e):()=>this.emit(`error`,e));return}this._nativeSend(e,r)}_nativeSend(e,t){try{let r;r=typeof e==`string`?e:typeof e==`number`||typeof e==`boolean`?String(e):n.isBuffer(e)?e.buffer.slice(e.byteOffset,e.byteOffset+e.byteLength):e instanceof ArrayBuffer||ArrayBuffer.isView(e)?e:String(e),this._native?.send(r),t&&queueMicrotask(()=>t())}catch(e){let n=e instanceof Error?e:Error(String(e));queueMicrotask(t?()=>t(n):()=>this.emit(`error`,n))}}_sizeOf(e){return typeof e==`string`?e.length:e instanceof ArrayBuffer||ArrayBuffer.isView(e)?e.byteLength:0}close(e,t){if(this.readyState!==3&&this.readyState!==2){this.readyState=2;try{let r=t===void 0?void 0:n.isBuffer(t)?t.toString(`utf8`):String(t);e===void 0?this._native?.close():r===void 0?this._native?.close(e):this._native?.close(e,r)}catch(e){this.emit(`error`,e instanceof Error?e:Error(String(e)))}}}terminate(){if(this.readyState!==3){this.readyState=2;try{this._native?.close(1006,`terminated`)}catch{}}}get isPaused(){return this.readyState===2||this.readyState===3}_eventTargetListeners=new Map;addEventListener(e,t){let n=this._eventTargetListeners.get(e);n||(n=new Set,this._eventTargetListeners.set(e,n)),n.add(t)}removeEventListener(e,t){this._eventTargetListeners.get(e)?.delete(t)}_dispatchEvent(e,t){let n=this._eventTargetListeners.get(e);if(!n||n.size===0)return;let r=Object.assign({type:e,target:this},t);for(let e of n)try{e(r)}catch(e){queueMicrotask(()=>this.emit(`error`,e))}}static get BINARY_TYPES(){return e}};WebSocket.WebSocket=WebSocket;export{WebSocket};
|
package/lib/types/stream.d.ts
CHANGED
|
@@ -1,2 +1,24 @@
|
|
|
1
1
|
import { Duplex } from 'node:stream';
|
|
2
|
-
|
|
2
|
+
import type { WebSocket } from './websocket.js';
|
|
3
|
+
/** Structural subset of a `ws.WebSocket` we read in the Duplex bridge. Includes
|
|
4
|
+
* the two server-side wrappers in this package (the public `WebSocket` client
|
|
5
|
+
* and the private `ServerSideWebSocket` from `websocket-server.ts`) plus any
|
|
6
|
+
* other ws-compatible class — typed structurally so the bridge stays drop-in
|
|
7
|
+
* for the npm `ws` semantics (`ws.on(event, …)`/`ws.send(…)`/`ws.close()`/`ws.terminate()`
|
|
8
|
+
* + the readyState constants both as instance and static fields). */
|
|
9
|
+
interface WSLike {
|
|
10
|
+
readyState: number;
|
|
11
|
+
readonly CONNECTING: number;
|
|
12
|
+
readonly OPEN: number;
|
|
13
|
+
readonly CLOSING: number;
|
|
14
|
+
readonly CLOSED: number;
|
|
15
|
+
on(event: string, listener: (...args: unknown[]) => void): unknown;
|
|
16
|
+
once(event: string, listener: (...args: unknown[]) => void): unknown;
|
|
17
|
+
send(data: string | Buffer, cb?: (err?: Error) => void): void;
|
|
18
|
+
close(): void;
|
|
19
|
+
terminate(): void;
|
|
20
|
+
pause?(): void;
|
|
21
|
+
resume?(): void;
|
|
22
|
+
}
|
|
23
|
+
export declare function createWebSocketStream(ws: WebSocket | WSLike, options?: Record<string, unknown>): Duplex;
|
|
24
|
+
export {};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { EventEmitter } from '@gjsify/events';
|
|
2
2
|
import { Buffer } from '@gjsify/buffer';
|
|
3
3
|
import Soup from '@girs/soup-3.0';
|
|
4
|
+
import Gio from '@girs/gio-2.0';
|
|
4
5
|
/** Structural duck-type for @gjsify/http Server — avoids a hard dep on @gjsify/http. */
|
|
5
6
|
interface HttpServer {
|
|
6
7
|
soupServer: Soup.Server | null;
|
|
@@ -10,6 +11,29 @@ interface HttpServer {
|
|
|
10
11
|
port: number;
|
|
11
12
|
} | null;
|
|
12
13
|
}
|
|
14
|
+
/** Structural duck-type for an HTTP upgrade request (Node `IncomingMessage`-shaped).
|
|
15
|
+
* ws's `handleUpgrade(req, socket, head, cb)` accepts the IncomingMessage produced
|
|
16
|
+
* by `http.Server`'s 'upgrade' event — modelled structurally so this file does not
|
|
17
|
+
* hard-depend on `@gjsify/http`'s exact request class. Members read defensively. */
|
|
18
|
+
interface UpgradeRequest {
|
|
19
|
+
method?: string;
|
|
20
|
+
url?: string;
|
|
21
|
+
headers?: Record<string, string | string[] | undefined>;
|
|
22
|
+
socket?: {
|
|
23
|
+
remoteAddress?: string;
|
|
24
|
+
remotePort?: number;
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
/** Structural duck-type for the upgrade socket (Node `Duplex`/`net.Socket`-shaped).
|
|
28
|
+
* We only need `write`/`destroy` to send the 101/4xx response, plus the optional
|
|
29
|
+
* `_releaseIOStream` hook that `@gjsify/net`'s Socket exposes so we can hand its
|
|
30
|
+
* underlying `Gio.IOStream` to `Soup.WebsocketConnection.new`. The hook stays
|
|
31
|
+
* optional so a plain net.Socket can still pass through `_abortHandshake`. */
|
|
32
|
+
interface UpgradeSocket {
|
|
33
|
+
write(chunk: string | Uint8Array, cb?: (err?: Error) => void): boolean;
|
|
34
|
+
destroy?(err?: Error): void;
|
|
35
|
+
_releaseIOStream?(): Gio.IOStream | null;
|
|
36
|
+
}
|
|
13
37
|
export interface VerifyClientInfo {
|
|
14
38
|
/** Value of the HTTP Origin request header (empty string if absent). */
|
|
15
39
|
origin: string;
|
|
@@ -107,7 +131,7 @@ export declare class WebSocketServer extends EventEmitter {
|
|
|
107
131
|
* Sec-WebSocket-Accept, emits 'headers' (mutable array), writes the 101
|
|
108
132
|
* response via socket.write(), then creates Soup.WebsocketConnection from
|
|
109
133
|
* the underlying IOStream and calls cb(ws, req). */
|
|
110
|
-
handleUpgrade(req:
|
|
134
|
+
handleUpgrade(req: UpgradeRequest, socket: UpgradeSocket, _head: Buffer, cb: (ws: ServerSideWebSocket, req: UpgradeRequest) => void): void;
|
|
111
135
|
shouldHandle(req: {
|
|
112
136
|
url?: string;
|
|
113
137
|
}): boolean;
|
package/lib/types/websocket.d.ts
CHANGED
|
@@ -1,6 +1,21 @@
|
|
|
1
1
|
import { EventEmitter } from '@gjsify/events';
|
|
2
2
|
import { Buffer } from '@gjsify/buffer';
|
|
3
3
|
export type BinaryType = 'nodebuffer' | 'arraybuffer' | 'fragments' | 'blob';
|
|
4
|
+
/** Event shape passed to W3C-style `addEventListener` consumers. ws's public
|
|
5
|
+
* API uses `(event: ws.Event | ws.MessageEvent | ws.CloseEvent | ws.ErrorEvent) => void`;
|
|
6
|
+
* consumers read `event.type`/`event.data`/`event.code`/`event.reason`/`event.error`,
|
|
7
|
+
* all of which are surfaced here. `target` is the wrapper, matching ws. */
|
|
8
|
+
interface WsListenerEvent {
|
|
9
|
+
type: string;
|
|
10
|
+
target: WebSocket;
|
|
11
|
+
data?: unknown;
|
|
12
|
+
code?: number;
|
|
13
|
+
reason?: string;
|
|
14
|
+
wasClean?: boolean;
|
|
15
|
+
error?: unknown;
|
|
16
|
+
message?: string;
|
|
17
|
+
}
|
|
18
|
+
type WsListener = (ev: WsListenerEvent) => void;
|
|
4
19
|
/** Options accepted by the ws constructor. Only a subset is honored on Gjs —
|
|
5
20
|
* see README for the support matrix. Unknown options are silently ignored
|
|
6
21
|
* to preserve drop-in compatibility with `ws`-calling code. */
|
|
@@ -58,9 +73,9 @@ export declare class WebSocket extends EventEmitter {
|
|
|
58
73
|
extensions: string;
|
|
59
74
|
bufferedAmount: number;
|
|
60
75
|
binaryType: BinaryType;
|
|
61
|
-
/** The real WebSocket we delegate to. Typed
|
|
62
|
-
* ambient type comes from multiple realms depending on where
|
|
63
|
-
* ends up (GJS browser-like globals vs. Node's undici). */
|
|
76
|
+
/** The real WebSocket we delegate to. Typed structurally (`NativeWebSocketLike`)
|
|
77
|
+
* because the W3C ambient type comes from multiple realms depending on where
|
|
78
|
+
* this bundle ends up (GJS browser-like globals vs. Node's undici). */
|
|
64
79
|
private _native;
|
|
65
80
|
constructor(address: string | URL | null, protocols?: string | string[], options?: ClientOptions);
|
|
66
81
|
/** Merge `protocols` arg and `options.protocols` / `options.protocol`. */
|
|
@@ -92,8 +107,9 @@ export declare class WebSocket extends EventEmitter {
|
|
|
92
107
|
* ws.isPaused() / internal state checks that some consumers rely on. */
|
|
93
108
|
get isPaused(): boolean;
|
|
94
109
|
private _eventTargetListeners;
|
|
95
|
-
addEventListener(type: string, listener:
|
|
96
|
-
removeEventListener(type: string, listener:
|
|
110
|
+
addEventListener(type: string, listener: WsListener): void;
|
|
111
|
+
removeEventListener(type: string, listener: WsListener): void;
|
|
97
112
|
private _dispatchEvent;
|
|
98
113
|
static get BINARY_TYPES(): readonly ["nodebuffer", "arraybuffer", "fragments"];
|
|
99
114
|
}
|
|
115
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gjsify/ws",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.31",
|
|
4
4
|
"description": "Drop-in replacement for the `ws` npm package on Gjs — wraps globalThis.WebSocket (Soup.WebsocketConnection) and Soup.Server for the WebSocketServer side",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"module": "lib/esm/index.js",
|
|
@@ -37,15 +37,15 @@
|
|
|
37
37
|
"@girs/gio-2.0": "2.88.0-4.0.1",
|
|
38
38
|
"@girs/glib-2.0": "2.88.0-4.0.1",
|
|
39
39
|
"@girs/soup-3.0": "3.6.6-4.0.1",
|
|
40
|
-
"@gjsify/buffer": "^0.4.
|
|
41
|
-
"@gjsify/crypto": "^0.4.
|
|
42
|
-
"@gjsify/events": "^0.4.
|
|
43
|
-
"@gjsify/utils": "^0.4.
|
|
44
|
-
"@gjsify/websocket": "^0.4.
|
|
40
|
+
"@gjsify/buffer": "^0.4.31",
|
|
41
|
+
"@gjsify/crypto": "^0.4.31",
|
|
42
|
+
"@gjsify/events": "^0.4.31",
|
|
43
|
+
"@gjsify/utils": "^0.4.31",
|
|
44
|
+
"@gjsify/websocket": "^0.4.31"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
|
-
"@gjsify/cli": "^0.4.
|
|
48
|
-
"@gjsify/unit": "^0.4.
|
|
47
|
+
"@gjsify/cli": "^0.4.31",
|
|
48
|
+
"@gjsify/unit": "^0.4.31",
|
|
49
49
|
"@types/node": "^25.9.1",
|
|
50
50
|
"typescript": "^6.0.3"
|
|
51
51
|
}
|