From ce1d4561a21759f2d0076107e5b5f4639435d07a Mon Sep 17 00:00:00 2001 From: Alexander Zinn Date: Wed, 17 Sep 2025 23:50:13 -0400 Subject: [PATCH] bfcache: server - WebSocketServer --- Web/bfcache/server/package.json | 26 ++++++- Web/bfcache/server/src/WebSocketServer.ts | 93 +++++++++++++++++++++++ Web/bfcache/server/src/types/Units.ts | 3 + Web/bfcache/server/tsconfig.json | 2 +- 4 files changed, 122 insertions(+), 2 deletions(-) create mode 100644 Web/bfcache/server/src/WebSocketServer.ts create mode 100644 Web/bfcache/server/src/types/Units.ts diff --git a/Web/bfcache/server/package.json b/Web/bfcache/server/package.json index e3e7ad6..139fde0 100644 --- a/Web/bfcache/server/package.json +++ b/Web/bfcache/server/package.json @@ -8,7 +8,8 @@ "format": "prettier --write .", "lint": "eslint --max-warnings 0 .", "prelint:fix": "bun run format", - "lint:fix": "eslint --fix ." + "lint:fix": "eslint --fix .", + "start:dev": "bun run --watch src" }, "devDependencies": { "@eslint/css": "0.11.0", @@ -26,5 +27,28 @@ "dependencies": { "@techniker-me/logger": "0.0.15", "@techniker-me/tools": "2025.0.16" + }, + "author": { + "name": "Alexander Zinn", + "email": "zinntechniker@gmail.com", + "git+url": "https://github.com/zinntechniker" + }, + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/zinntechniker/websocket-relay-server" + }, + "homepage": "https://github.com/zinntechniker/websocket-relay-server", + "bugs": { + "url": "https://github.com/zinntechniker/websocket-relay-server/issues" + }, + "keywords": [ + "websocket", + "relay", + "server" + ], + "engines": { + "node": ">=20.0.0", + "bun": ">=1.2.21" } } diff --git a/Web/bfcache/server/src/WebSocketServer.ts b/Web/bfcache/server/src/WebSocketServer.ts new file mode 100644 index 0000000..0442eaf --- /dev/null +++ b/Web/bfcache/server/src/WebSocketServer.ts @@ -0,0 +1,93 @@ +import type {ServerWebSocket, WebSocketCompressor} from 'bun'; +import type {Seconds, Bytes} from './types/Units'; + +export type PerMessageDeflate = +| boolean +| { + compress?: boolean | WebSocketCompressor; + decomporess?: boolean | WebSocketCompressor; +}; + +export type WebSocketServerOptions = { + maxPayloadLength?: Bytes; + idleTimeout?: Seconds; + backPressureLimit?: Bytes; + closeOnBackPressureLimit?: boolean; + sendPings?: boolean; + publishToSelf?: boolean; + perMessageDeflate?: PerMessageDeflate; + onSocketError?: (client: ServerWebSocket, error: Error) => void; + onSocketOpen?: (client: ServerWebSocket) => void; + onSocketMessage?: (client: ServerWebSocket, message: string | ArrayBuffer | Uint8Array) => void; + onSocketDrain?: (client: ServerWebSocket) => void; + onSocketClose?: (client: ServerWebSocket) => void; +}; + +const webSocketServerDefaults: WebSocketServerOptions = { + maxPayloadLength: 16777216, + idleTimeout: 120, + backPressureLimit: 1048576, + closeOnBackPressureLimit: false, + sendPings: true, + publishToSelf: true, + perMessageDeflate: true +}; + +export default class WebSocketServer { + private readonly _maxPayloadLength: Bytes; + private readonly _idleTimeout: Seconds; + private readonly _backPressureLimit: Bytes; + private readonly _closeOnBackPressureLimit: boolean; + private readonly _sendPings: boolean; + private readonly _publishToSelf: boolean; + private readonly _perMessageDeflate: PerMessageDeflate; + + public readonly error?: (client: ServerWebSocket, error: Error) => void; + public readonly open?: (client: ServerWebSocket) => void; + public readonly message?: (client: ServerWebSocket, message: string | ArrayBuffer | Uint8Array) => void; + public readonly drain?: (client: ServerWebSocket) => void; + public readonly close?: (client: ServerWebSocket) => void; + + constructor(options?: WebSocketServerOptions) { + this._maxPayloadLength = options?.maxPayloadLength ?? webSocketServerDefaults.maxPayloadLength!; + this._idleTimeout = options?.idleTimeout ?? webSocketServerDefaults.idleTimeout!; + this._backPressureLimit = options?.backPressureLimit ?? webSocketServerDefaults.backPressureLimit!; + this._closeOnBackPressureLimit = options?.closeOnBackPressureLimit ?? webSocketServerDefaults.closeOnBackPressureLimit!; + this._sendPings = options?.sendPings ?? webSocketServerDefaults.sendPings!; + this._publishToSelf = options?.publishToSelf ?? webSocketServerDefaults.publishToSelf!; + this._perMessageDeflate = options?.perMessageDeflate ?? webSocketServerDefaults.perMessageDeflate!; + this.error = options?.onSocketError; + this.open = options?.onSocketOpen; + this.message = options?.onSocketMessage; + this.drain = options?.onSocketDrain; + this.close = options?.onSocketClose; + } + + get maxPayloadLength(): Bytes { + return this._maxPayloadLength; + } + + get idleTimeout(): Seconds { + return this._idleTimeout; + } + + get backPressureLimit(): Bytes { + return this._backPressureLimit; + } + + get closeOnBackPressureLimit(): boolean { + return this._closeOnBackPressureLimit; + } + + get sendPings(): boolean { + return this._sendPings; + } + + get publishToSelf(): boolean { + return this._publishToSelf; + } + + get perMessageDeflate(): PerMessageDeflate { + return this._perMessageDeflate; + } +} \ No newline at end of file diff --git a/Web/bfcache/server/src/types/Units.ts b/Web/bfcache/server/src/types/Units.ts new file mode 100644 index 0000000..0995b02 --- /dev/null +++ b/Web/bfcache/server/src/types/Units.ts @@ -0,0 +1,3 @@ +export type Bytes = number; + +export type Seconds = number; diff --git a/Web/bfcache/server/tsconfig.json b/Web/bfcache/server/tsconfig.json index aa9671e..2398840 100644 --- a/Web/bfcache/server/tsconfig.json +++ b/Web/bfcache/server/tsconfig.json @@ -9,7 +9,7 @@ "moduleResolution": "bundler", "allowImportingTsExtensions": true, - "verbatimModuleSyntax": true, + "verbatimModuleSyntax": false, "noEmit": true, "strict": true,