@adonix.org/cloud-spark 2.1.2 → 2.2.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.
@@ -196,6 +196,10 @@ declare class WebSocketSessions<A extends WSAttachment = WSAttachment> {
196
196
  * @returns `true` if the connection was managed and removed, `false` otherwise.
197
197
  */
198
198
  close(ws: WebSocket, code?: number, reason?: string): boolean;
199
+ /**
200
+ * @returns the number of active WebSocket connections.
201
+ */
202
+ get size(): number;
199
203
  /** Iterates over all active WebSocket connections. */
200
204
  [Symbol.iterator](): IterableIterator<WebSocketConnection<A>>;
201
205
  /** Registers a connection internally. */
package/dist/sessions.js CHANGED
@@ -1,2 +1,2 @@
1
- function a(t){return typeof t=="string"}function d(t){return typeof t=="number"&&!Number.isNaN(t)}function k(t){return t instanceof ArrayBuffer||ArrayBuffer.isView(t)}function S(t){return a(t)?t.length>0:k(t)?t.byteLength>0:false}function h(t){if(t===null||typeof t!="object")throw new TypeError("WebSocket attachment must be an object");try{JSON.stringify(t);}catch{throw new TypeError("WebSocket attachment is not serializable")}}var u=class t{server;customListeners={};constructor(e){this.server=e;}addEventListener(e,r,n){if(t.isCustomEvent(e)){let o=this.customListeners[e];o||(o=[],this.customListeners[e]=o),o.push(r);}else {let o=e==="close"?{...n,once:true}:n;this.server.addEventListener(e,r,o);}}removeEventListener(e,r){if(t.isCustomEvent(e)){let n=this.customListeners[e];if(n){let o=n.indexOf(r);o!==-1&&n.splice(o,1);}}else this.server.removeEventListener(e,r);}dispatch(e,r,n=false){let o=this.customListeners[e]?.slice()??[];n&&(this.customListeners[e]=[]);for(let c of o)c(r);}warn(e){this.dispatch("warn",{type:"warn",message:e});}open(){this.dispatch("open",new Event("open"),true);}static isCustomEvent(e){return ["open","warn"].includes(e)}};var s={NORMAL:1e3,NO_STATUS:1005,ABNORMAL:1006,TLS_HANDSHAKE:1015},f=new Set([s.NO_STATUS,s.ABNORMAL,s.TLS_HANDSHAKE]);function m(t){return d(t)?C(t)&&!x(t)?t:s.NORMAL:s.NORMAL}function C(t){return t>=s.NORMAL&&t<=4999}function x(t){return f.has(t)}function v(t){if(a(t))return t.replaceAll(/[^\x20-\x7E]/g,"").slice(0,123)}var i=class extends u{accepted=false;server;constructor(e){super(e),this.server=e,this.server.addEventListener("close",this.onclose);}send(e){if(this.isState(WebSocket.CONNECTING,WebSocket.CLOSED)){this.warn("Cannot send: WebSocket not open");return}if(!S(e)){this.warn("Cannot send: empty or invalid data");return}this.server.send(e);}get attachment(){return this.server.deserializeAttachment()??{}}attach(e){if(e!==void 0)if(e===null)this.server.serializeAttachment({});else {let n={...this.attachment,...e};h(n),this.server.serializeAttachment(n);}}get readyState(){return this.accepted?this.server.readyState:WebSocket.CONNECTING}isState(...e){return e.includes(this.readyState)}close(e,r){this.server.removeEventListener("close",this.onclose),this.server.close(m(e),v(r));}onclose=e=>{this.close(e.code,e.reason);}};var p=class extends i{client;constructor(){let e=new WebSocketPair,[r,n]=[e[0],e[1]];super(n),this.client=r;}acceptWebSocket(e,r){return this.accepted?this.client:(e.acceptWebSocket(this.server,r),this.ready())}accept(){return this.accepted?this.client:(this.server.accept(),this.ready())}ready(){return this.accepted=true,this.open(),this.client}};var l=class extends i{constructor(e){super(e),this.accepted=true;}accept(){throw new Error("Do not call accept() on restore")}acceptWebSocket(){throw new Error("Do not call acceptWebSocket() on restore")}};var A=class{map=new Map;create(e){class r extends p{constructor(b){super();this.sessions=b;}accept(){return this.addEventListener("close",()=>this.sessions.unregister(this.server)),this.sessions.register(this.server,this),super.accept()}acceptWebSocket(b,W){return this.sessions.register(this.server,this),super.acceptWebSocket(b,W)}}let n=new r(this);return n.attach(e),n}restore(e){class r extends l{constructor(o,c){super(c),o.register(this.server,this);}}return new r(this,e)}restoreAll(e){let r=[];for(let n of e)r.push(this.restore(n));return r}get(e){return this.map.get(e)}select(e){let r=[];for(let n of e){let o=this.map.get(n);o&&r.push(o);}return r}values(){return this.map.values()}keys(){return this.map.keys()}close(e,r,n){let o=this.get(e);return o&&o.close(r,n),this.unregister(e)}*[Symbol.iterator](){yield*this.values();}register(e,r){this.map.set(e,r);}unregister(e){return this.map.delete(e)}};export{A as WebSocketSessions};//# sourceMappingURL=sessions.js.map
1
+ function a(t){return typeof t=="string"}function d(t){return typeof t=="number"&&!Number.isNaN(t)}function k(t){return t instanceof ArrayBuffer||ArrayBuffer.isView(t)}function S(t){return a(t)?t.length>0:k(t)?t.byteLength>0:false}function h(t){if(t===null||typeof t!="object")throw new TypeError("WebSocket attachment must be an object");try{JSON.stringify(t);}catch{throw new TypeError("WebSocket attachment is not serializable")}}var u=class t{server;customListeners={};constructor(e){this.server=e;}addEventListener(e,r,n){if(t.isCustomEvent(e)){let o=this.customListeners[e];o||(o=[],this.customListeners[e]=o),o.push(r);}else {let o=e==="close"?{...n,once:true}:n;this.server.addEventListener(e,r,o);}}removeEventListener(e,r){if(t.isCustomEvent(e)){let n=this.customListeners[e];if(n){let o=n.indexOf(r);o!==-1&&n.splice(o,1);}}else this.server.removeEventListener(e,r);}dispatch(e,r,n=false){let o=this.customListeners[e]?.slice()??[];n&&(this.customListeners[e]=[]);for(let c of o)c(r);}warn(e){this.dispatch("warn",{type:"warn",message:e});}open(){this.dispatch("open",new Event("open"),true);}static isCustomEvent(e){return ["open","warn"].includes(e)}};var s={NORMAL:1e3,NO_STATUS:1005,ABNORMAL:1006,TLS_HANDSHAKE:1015},f=new Set([s.NO_STATUS,s.ABNORMAL,s.TLS_HANDSHAKE]);function m(t){return d(t)?C(t)&&!x(t)?t:s.NORMAL:s.NORMAL}function C(t){return t>=s.NORMAL&&t<=4999}function x(t){return f.has(t)}function v(t){if(a(t))return t.replaceAll(/[^\x20-\x7E]/g,"").slice(0,123)}var i=class extends u{accepted=false;server;constructor(e){super(e),this.server=e,this.server.addEventListener("close",this.onclose);}send(e){if(this.isState(WebSocket.CONNECTING,WebSocket.CLOSED)){this.warn("Cannot send: WebSocket not open");return}if(!S(e)){this.warn("Cannot send: empty or invalid data");return}this.server.send(e);}get attachment(){return this.server.deserializeAttachment()??{}}attach(e){if(e!==void 0)if(e===null)this.server.serializeAttachment({});else {let n={...this.attachment,...e};h(n),this.server.serializeAttachment(n);}}get readyState(){return this.accepted?this.server.readyState:WebSocket.CONNECTING}isState(...e){return e.includes(this.readyState)}close(e,r){this.server.removeEventListener("close",this.onclose),this.server.close(m(e),v(r));}onclose=e=>{this.close(e.code,e.reason);}};var p=class extends i{client;constructor(){let e=new WebSocketPair,[r,n]=[e[0],e[1]];super(n),this.client=r;}acceptWebSocket(e,r){return this.accepted?this.client:(e.acceptWebSocket(this.server,r),this.ready())}accept(){return this.accepted?this.client:(this.server.accept(),this.ready())}ready(){return this.accepted=true,this.open(),this.client}};var l=class extends i{constructor(e){super(e),this.accepted=true;}accept(){throw new Error("Do not call accept() on restore")}acceptWebSocket(){throw new Error("Do not call acceptWebSocket() on restore")}};var A=class{map=new Map;create(e){class r extends p{constructor(b){super();this.sessions=b;}accept(){return this.addEventListener("close",()=>this.sessions.unregister(this.server)),this.sessions.register(this.server,this),super.accept()}acceptWebSocket(b,W){return this.sessions.register(this.server,this),super.acceptWebSocket(b,W)}}let n=new r(this);return n.attach(e),n}restore(e){class r extends l{constructor(o,c){super(c),o.register(this.server,this);}}return new r(this,e)}restoreAll(e){let r=[];for(let n of e)r.push(this.restore(n));return r}get(e){return this.map.get(e)}select(e){let r=[];for(let n of e){let o=this.map.get(n);o&&r.push(o);}return r}values(){return this.map.values()}keys(){return this.map.keys()}close(e,r,n){let o=this.get(e);return o&&o.close(r,n),this.unregister(e)}get size(){return this.map.size}*[Symbol.iterator](){yield*this.values();}register(e,r){this.map.set(e,r);}unregister(e){return this.map.delete(e)}};export{A as WebSocketSessions};//# sourceMappingURL=sessions.js.map
2
2
  //# sourceMappingURL=sessions.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/guards/basic.ts","../src/guards/websocket.ts","../src/websocket/events.ts","../src/websocket/constants.ts","../src/websocket/utils.ts","../src/websocket/base.ts","../src/websocket/new.ts","../src/websocket/restore.ts","../src/websocket/sessions.ts"],"names":["isString","value","isNumber","isBinary","isSendable","assertSerializable","WebSocketEvents","_WebSocketEvents","server","type","listener","options","arr","finalOptions","index","ev","once","listeners","msg","CloseCode","WS_RESERVED_CODES","safeCloseCode","code","isCodeInRange","isReservedCode","safeReason","reason","BaseWebSocket","data","attachment","merged","states","event","NewConnectionBase","pair","client","ctx","tags","RestoredConnectionBase","ws","WebSocketSessions","NewConnection","sessions","connection","RestoredConnection","restore","all","restored","sockets","result","con"],"mappings":"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","file":"sessions.js","sourcesContent":["/*\n * Copyright (C) 2025 Ty Busby\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Checks if the provided value is an array of strings.\n *\n * @param value - The value to check.\n * @returns True if `array` is an array where every item is a string.\n */\nexport function isStringArray(value: unknown): value is string[] {\n return Array.isArray(value) && value.every((item) => typeof item === \"string\");\n}\n\n/**\n * Checks if a value is a string.\n *\n * @param value - The value to check.\n * @returns `true` if the value is a string, otherwise `false`.\n */\nexport function isString(value: unknown): value is string {\n return typeof value === \"string\";\n}\n\n\n/**\n * Checks if a value is a valid number (not NaN).\n *\n * This function returns `true` if the value is of type `number`\n * and is not `NaN`. It works as a type guard for TypeScript.\n *\n * @param value - The value to check.\n * @returns `true` if the value is a number and not `NaN`, otherwise `false`.\n */\nexport function isNumber(value: unknown): value is number {\n return typeof value === \"number\" && !Number.isNaN(value);\n}\n\n/**\n * Checks if a value is a boolean.\n *\n * @param value - The value to check.\n * @returns `true` if the value is a boolean (`true` or `false`), otherwise `false`.\n */\nexport function isBoolean(value: unknown): value is boolean {\n return typeof value === \"boolean\";\n}\n","/*\n * Copyright (C) 2025 Ty Busby\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { isString } from \"./basic\";\n\n/**\n * Checks whether a value is binary data suitable for WebSocket transmission.\n *\n * A value is considered binary if it is an {@link ArrayBuffer} or any\n * {@link ArrayBufferView} (such as a `Uint8Array`, `DataView`, etc.).\n *\n * @param value - The value to check.\n * @returns `true` if `value` is an {@link ArrayBuffer} or {@link ArrayBufferView}, otherwise `false`.\n */\nexport function isBinary(value: unknown): value is ArrayBuffer | ArrayBufferView {\n return value instanceof ArrayBuffer || ArrayBuffer.isView(value);\n}\n\n/**\n * Checks whether a value can be sent over a WebSocket connection.\n *\n * A sendable value is either:\n * - a non-empty string, or\n * - a non-empty {@link ArrayBuffer} / {@link ArrayBufferView}.\n *\n * Empty strings and zero-length binary data are considered non-sendable.\n *\n * @param value - The value to check.\n * @returns `true` if `value` is a non-empty string or binary buffer, otherwise `false`.\n */\nexport function isSendable(value: unknown): value is string | ArrayBuffer | ArrayBufferView {\n if (isString(value)) return value.length > 0;\n if (isBinary(value)) return value.byteLength > 0;\n return false;\n}\n\n/**\n * Asserts that a value is a serializable object suitable for JSON encoding.\n *\n * This function ensures the value is a non-null object and that\n * `JSON.stringify()` succeeds without throwing an error.\n *\n * @param value - The value to validate.\n * @throws {TypeError} If `value` is not an object or cannot be serialized to JSON.\n */\nexport function assertSerializable(value: unknown): asserts value is object {\n if (value === null || typeof value !== \"object\") {\n throw new TypeError(\"WebSocket attachment must be an object\");\n }\n try {\n JSON.stringify(value);\n } catch {\n throw new TypeError(\"WebSocket attachment is not serializable\");\n }\n}\n","/*\n * Copyright (C) 2025 Ty Busby\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n CustomEventType,\n EventOptions,\n ExtendedEventListener,\n ExtendedEventMap,\n ExtendedEventType,\n} from \"../interfaces/websocket\";\n\n/**\n * Base class for managing WebSocket events, including both standard WebSocket events\n * and internal custom events (`open` and `warn`).\n *\n * This class wraps a native WebSocket instance (`server`) and provides:\n * - Delegation of standard events (`message`, `close`, etc.)\n * - Support for custom events used internally by the library:\n * - `open`: dispatched once when the connection is accepted\n * - `warn`: dispatched whenever a warning occurs\n *\n * Subclasses can call `warn()` or `open()` to trigger custom events.\n */\nexport abstract class WebSocketEvents {\n /** The underlying WebSocket server instance being wrapped. */\n protected readonly server: WebSocket;\n\n /** Internal map of custom event listeners. */\n private customListeners: {\n [K in ExtendedEventType]?: ((ev: ExtendedEventMap[K]) => void)[];\n } = {};\n\n /**\n * @param server - The native WebSocket instance to wrap.\n */\n constructor(server: WebSocket) {\n this.server = server;\n }\n\n /**\n * Adds an event listener for either a standard WebSocket event or a custom event.\n *\n * - Custom events: `open`, `warn`\n * - Standard events: `message`, `close`, `error`, etc.\n *\n * The `close` event is automatically set to `{ once: true }` if no options are provided.\n *\n * @param type - Event type to listen for.\n * @param listener - Callback invoked when the event occurs.\n * @param options - Optional event options (`once`).\n */\n public addEventListener<K extends ExtendedEventType>(\n type: K,\n listener: ExtendedEventListener<K>,\n options?: EventOptions,\n ): void {\n if (WebSocketEvents.isCustomEvent(type)) {\n let arr = this.customListeners[type];\n if (!arr) {\n arr = [];\n this.customListeners[type] = arr;\n }\n arr.push(listener);\n } else {\n const finalOptions = type === \"close\" ? { ...options, once: true } : options;\n this.server.addEventListener(\n type as keyof WebSocketEventMap,\n listener as EventListener,\n finalOptions,\n );\n }\n }\n\n /**\n * Removes a previously registered event listener.\n *\n * Works for both standard WebSocket events and custom events.\n *\n * @param type - Event type to remove.\n * @param listener - Listener function to remove.\n */\n public removeEventListener<K extends ExtendedEventType>(\n type: K,\n listener: ExtendedEventListener<K>,\n ): void {\n if (WebSocketEvents.isCustomEvent(type)) {\n const arr = this.customListeners[type];\n if (arr) {\n const index = arr.indexOf(listener);\n if (index !== -1) arr.splice(index, 1);\n }\n } else {\n this.server.removeEventListener(\n type as keyof WebSocketEventMap,\n listener as EventListener,\n );\n }\n }\n\n /**\n * Dispatches a custom event to all registered listeners.\n *\n * @param type - The custom event type (`open` or `warn`).\n * @param ev - Event object to pass to listeners.\n * @param once - If `true`, all listeners for this event are removed after dispatch.\n */\n private dispatch<K extends CustomEventType>(\n type: K,\n ev: ExtendedEventMap[K],\n once: boolean = false,\n ): void {\n const listeners = this.customListeners[type]?.slice() ?? [];\n if (once) {\n this.customListeners[type] = [];\n }\n for (const listener of listeners) {\n listener(ev);\n }\n }\n\n /**\n * Dispatches a `warn` event with a given message.\n *\n * Intended for internal use to notify listeners of warnings.\n *\n * @param msg - Warning message to emit.\n */\n protected warn(msg: string) {\n this.dispatch(\"warn\", { type: \"warn\", message: msg });\n }\n\n /**\n * Dispatches an `open` event.\n *\n * Intended to signal that the WebSocket has been accepted and is ready.\n * This event is dispatched only once.\n */\n protected open() {\n this.dispatch(\"open\", new Event(\"open\"), true);\n }\n\n /** Internal helper to determine if an event is a custom event. */\n private static isCustomEvent(type: ExtendedEventType): boolean {\n return [\"open\", \"warn\"].includes(type);\n }\n}\n","/*\n * Copyright (C) 2025 Ty Busby\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/** Max close code a user can send */\nexport const WS_MAX_CLOSE_CODE = 4999;\n/** Max number of reason chars a user can send */\nexport const WS_MAX_REASON_CHARS = 123;\n\n/** WebSocket close codes */\nexport const CloseCode = {\n NORMAL: 1000,\n NO_STATUS: 1005,\n ABNORMAL: 1006,\n TLS_HANDSHAKE: 1015,\n} as const;\n\n/** WebSocket RESERVED close codes */\nexport const WS_RESERVED_CODES = new Set<number>([\n CloseCode.NO_STATUS,\n CloseCode.ABNORMAL,\n CloseCode.TLS_HANDSHAKE,\n]);\n","/*\n * Copyright (C) 2025 Ty Busby\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { isNumber, isString } from \"../guards/basic\";\n\nimport { CloseCode, WS_MAX_CLOSE_CODE, WS_MAX_REASON_CHARS, WS_RESERVED_CODES } from \"./constants\";\n\n/**\n * Normalizes a WebSocket close code to ensure it is safe to send.\n *\n * - Returns `CloseCode.NORMAL` if the code is undefined, out of range, or reserved.\n *\n * @param code - The optional close code to validate.\n * @returns A valid close code to use for WebSocket closure.\n */\nexport function safeCloseCode(code?: number): number {\n if (!isNumber(code)) return CloseCode.NORMAL;\n if (isCodeInRange(code) && !isReservedCode(code)) return code;\n return CloseCode.NORMAL;\n}\n\n/**\n * Determines whether a close code is within the valid WebSocket range.\n *\n * @param code - The code to validate.\n * @returns `true` if the code is within 1000–4999, `false` otherwise.\n */\nexport function isCodeInRange(code: number): boolean {\n return code >= CloseCode.NORMAL && code <= WS_MAX_CLOSE_CODE;\n}\n\n/**\n * Determines whether a close code is reserved by the WebSocket specification.\n *\n * @param code - The code to check.\n * @returns `true` if the code is reserved, `false` otherwise.\n */\nexport function isReservedCode(code: number): boolean {\n return WS_RESERVED_CODES.has(code);\n}\n\n/**\n * Sanitizes a close reason string to comply with WebSocket limits.\n *\n * - Removes non-printable ASCII characters.\n * - Truncates to the maximum allowed length (`WS_MAX_REASON_CHARS`).\n *\n * @param reason - The optional reason string to sanitize.\n * @returns A cleaned reason string or `undefined` if input is invalid.\n */\nexport function safeReason(reason?: string): string | undefined {\n if (!isString(reason)) return;\n return reason.replaceAll(/[^\\x20-\\x7E]/g, \"\").slice(0, WS_MAX_REASON_CHARS);\n}\n","/*\n * Copyright (C) 2025 Ty Busby\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { assertSerializable, isSendable } from \"../guards/websocket\";\nimport { WSAttachment } from \"../interfaces/websocket\";\n\nimport { WebSocketEvents } from \"./events\";\nimport { safeCloseCode, safeReason } from \"./utils\";\n\n/**\n * Base class providing core WebSocket functionality and attachment management.\n *\n * Extends `WebSocketEvents` to inherit custom event handling.\n *\n * @template A - Type of the attachment object associated with this WebSocket.\n */\nexport abstract class BaseWebSocket<A extends WSAttachment> extends WebSocketEvents {\n /** Tracks whether the WebSocket has been accepted. */\n protected accepted = false;\n\n /** The underlying WebSocket server instance. */\n protected readonly server: WebSocket;\n\n /**\n * Initializes the base WebSocket wrapper.\n *\n * Registers a listener to handle the underlying WebSocket `close` event.\n *\n * @param server - The underlying WebSocket instance.\n */\n constructor(server: WebSocket) {\n super(server);\n this.server = server;\n this.server.addEventListener(\"close\", this.onclose);\n }\n\n /**\n * Sends a message over the WebSocket if it is open.\n *\n * Performs validation to ensure the WebSocket is in an open state\n * and the data is non-empty and sendable. Emits a warning if not.\n *\n * @param data - The message to send, as a string or binary data.\n */\n public send(data: string | ArrayBuffer | ArrayBufferView): void {\n if (this.isState(WebSocket.CONNECTING, WebSocket.CLOSED)) {\n this.warn(\"Cannot send: WebSocket not open\");\n return;\n }\n if (!isSendable(data)) {\n this.warn(\"Cannot send: empty or invalid data\");\n return;\n }\n\n this.server.send(data);\n }\n\n /**\n * Returns the current attachment associated with this WebSocket.\n *\n * Attachments are stored as serialized objects on the underlying WebSocket.\n *\n * @returns Readonly attachment object of type `A`.\n */\n public get attachment(): Readonly<A> {\n return (this.server.deserializeAttachment() ?? {}) as A;\n }\n\n /**\n * Updates the attachment object for this WebSocket.\n *\n * Merges the provided partial attachment with the current attachment,\n * ensures it is serializable, and stores it on the underlying WebSocket.\n *\n * @param attachment - Partial or full attachment object to store,\n * or `null` to clear the attachment.\n */\n public attach(attachment?: Partial<A> | null): void {\n if (attachment === undefined) return;\n if (attachment === null) {\n this.server.serializeAttachment({});\n } else {\n const current = this.attachment;\n const merged = { ...current, ...attachment };\n assertSerializable(merged);\n this.server.serializeAttachment(merged);\n }\n }\n\n /**\n * Returns the current WebSocket ready state.\n *\n * If the WebSocket has not been accepted, returns `WebSocket.CONNECTING`.\n *\n * @returns The ready state of the WebSocket.\n */\n public get readyState(): number {\n if (!this.accepted) return WebSocket.CONNECTING;\n return this.server.readyState;\n }\n\n /**\n * Checks if the current ready state matches any of the provided states.\n *\n * @param states - One or more WebSocket state constants to check.\n * @returns `true` if the current state matches any provided, `false` otherwise.\n */\n public isState(...states: number[]): boolean {\n return states.includes(this.readyState);\n }\n\n /**\n * Closes the WebSocket safely.\n *\n * Removes the internal `close` listener and closes the underlying WebSocket\n * using validated close code and sanitized reason.\n *\n * @param code - Optional WebSocket close code.\n * @param reason - Optional reason for closing the WebSocket.\n */\n public close(code?: number, reason?: string): void {\n this.server.removeEventListener(\"close\", this.onclose);\n this.server.close(safeCloseCode(code), safeReason(reason));\n }\n\n /** Internal handler for the underlying WebSocket `close` event. */\n private readonly onclose = (event: CloseEvent): void => {\n this.close(event.code, event.reason);\n };\n}\n","/*\n * Copyright (C) 2025 Ty Busby\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { WebSocketConnection, WSAttachment } from \"../interfaces/websocket\";\n\nimport { BaseWebSocket } from \"./base\";\n\n/**\n * Internal base for managing a new WebSocket connection.\n *\n * - Creates a WebSocket pair and stores the client side internally.\n * - Provides methods to accept the server WebSocket and retrieve the client.\n * - Ensures the `open` event is dispatched when the connection is accepted.\n * - Tracks whether the connection has already been accepted to prevent multiple acceptances.\n *\n * @template A - Type of the attachment object for this connection.\n */\nexport abstract class NewConnectionBase<A extends WSAttachment>\n extends BaseWebSocket<A>\n implements WebSocketConnection<A>\n{\n /** The client-facing end of the WebSocket pair. */\n private readonly client: WebSocket;\n\n /**\n * Creates a new WebSocket pair and initializes the server side.\n *\n * The client side is stored internally and returned upon acceptance.\n */\n public constructor() {\n const pair = new WebSocketPair();\n const [client, server] = [pair[0], pair[1]];\n super(server);\n this.client = client;\n }\n\n /**\n * Accepts the server WebSocket and returns the client WebSocket.\n *\n * If already accepted, returns the existing client WebSocket.\n * Otherwise, uses a Durable Object state to accept the connection\n * and marks it as ready.\n *\n * @param ctx - DurableObjectState used to accept the WebSocket.\n * @param tags - Optional array of tags to attach to the WebSocket.\n * @returns Readonly client WebSocket ready for use.\n */\n public acceptWebSocket(ctx: DurableObjectState, tags?: string[]): Readonly<WebSocket> {\n if (this.accepted) return this.client;\n ctx.acceptWebSocket(this.server, tags);\n return this.ready();\n }\n\n /**\n * Accepts the server WebSocket and returns the client WebSocket.\n *\n * If already accepted, returns the existing client WebSocket.\n * Otherwise, calls the internal server accept method and marks\n * the connection as ready.\n *\n * @returns Readonly client WebSocket ready for use.\n */\n public accept(): Readonly<WebSocket> {\n if (this.accepted) return this.client;\n this.server.accept();\n return this.ready();\n }\n\n /**\n * Marks the WebSocket connection as ready.\n *\n * Sets the accepted flag, dispatches the `open` event,\n * and returns the client WebSocket.\n *\n * @returns Client WebSocket ready for use.\n */\n private ready(): WebSocket {\n this.accepted = true;\n this.open();\n\n return this.client;\n }\n}\n","/*\n * Copyright (C) 2025 Ty Busby\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { WebSocketConnection, WSAttachment } from \"../interfaces/websocket\";\n\nimport { BaseWebSocket } from \"./base\";\n\n/**\n * Internal base for a WebSocket connection that has already been restored.\n *\n * - Marks the connection as accepted immediately upon construction.\n * - Overrides acceptance methods to prevent re-accepting an already-active WebSocket.\n * - Throws an error if `accept()` or `acceptWebSocket()` is called.\n *\n * @template A - Type of the attachment object for this connection.\n */\nexport abstract class RestoredConnectionBase<A extends WSAttachment>\n extends BaseWebSocket<A>\n implements WebSocketConnection<A>\n{\n constructor(ws: WebSocket) {\n super(ws);\n this.accepted = true;\n }\n\n /** Not supported for restored connections; throws an error. */\n public accept(): Readonly<WebSocket> {\n throw new Error(\"Do not call accept() on restore\");\n }\n\n /** Not supported for restored connections; throws an error. */\n public acceptWebSocket(): Readonly<WebSocket> {\n throw new Error(\"Do not call acceptWebSocket() on restore\");\n }\n}\n","/*\n * Copyright (C) 2025 Ty Busby\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { WebSocketConnection, WSAttachment } from \"../interfaces/websocket\";\n\nimport { NewConnectionBase } from \"./new\";\nimport { RestoredConnectionBase } from \"./restore\";\n\n/**\n * Manages active WebSocket connections in a Cloudflare Workers environment.\n *\n * Provides a simple interface for creating, restoring, and managing\n * WebSocket connections with optional attachments. Users can:\n *\n * - Create new WebSocket connections (`create`) and attach arbitrary data.\n * - Accept connections using the standard WebSocket API (`accept`).\n * - Accept connections using the hibernatable WebSocket API (`acceptWebSocket`),\n * which allows the connection to be put to sleep when inactive.\n * - Restore existing WebSockets into a managed session (`restore`, `restoreAll`),\n * maintaining their hibernation state.\n * - Iterate over active connections or retrieve a connection by its WebSocket instance.\n * - Close a connection cleanly with optional code and reason (`close`).\n *\n * @template A - Type of attachment data stored on each WebSocket connection.\n */\nexport class WebSocketSessions<A extends WSAttachment = WSAttachment> {\n /** @internal Map of active WebSocket to their connection wrapper. */\n private readonly map = new Map<WebSocket, WebSocketConnection<A>>();\n\n /**\n * Create a new WebSocket connection and optionally attach user data.\n *\n * @param attachment - Partial attachment object to initialize the connection with.\n * @returns A `WebSocketConnection` instance ready for accepting and sending messages.\n */\n public create(attachment?: Partial<A>): WebSocketConnection<A> {\n class NewConnection extends NewConnectionBase<A> {\n constructor(private readonly sessions: WebSocketSessions<A>) {\n super();\n }\n\n public override accept(): WebSocket {\n this.addEventListener(\"close\", () => this.sessions.unregister(this.server));\n this.sessions.register(this.server, this);\n return super.accept();\n }\n\n public override acceptWebSocket(ctx: DurableObjectState, tags?: string[]): WebSocket {\n this.sessions.register(this.server, this);\n return super.acceptWebSocket(ctx, tags);\n }\n }\n\n const connection = new NewConnection(this);\n connection.attach(attachment);\n return connection;\n }\n\n /**\n * Wraps an existing WebSocket in a managed connection session.\n *\n * @param ws - An existing WebSocket to restore.\n * @returns A `WebSocketConnection` representing the restored session.\n */\n public restore(ws: WebSocket): WebSocketConnection<A> {\n class RestoredConnection extends RestoredConnectionBase<A> {\n constructor(sessions: WebSocketSessions<A>, restore: WebSocket) {\n super(restore);\n sessions.register(this.server, this);\n }\n }\n return new RestoredConnection(this, ws);\n }\n\n /**\n * Restores multiple WebSockets into managed sessions at once.\n *\n * @param all - Array of WebSocket instances to restore.\n * @returns Array of `WebSocketConnections` restored.\n */\n public restoreAll(all: WebSocket[]): ReadonlyArray<WebSocketConnection<A>> {\n const restored: WebSocketConnection<A>[] = [];\n for (const ws of all) {\n restored.push(this.restore(ws));\n }\n return restored;\n }\n\n /**\n * Retrieves the managed connection for a specific WebSocket, if any.\n *\n * @param ws - WebSocket instance.\n * @returns Corresponding `WebSocketConnection` or `undefined` if not managed.\n */\n public get(ws: WebSocket): WebSocketConnection<A> | undefined {\n return this.map.get(ws);\n }\n\n /**\n * Selects the managed `WebSocketConnection` objects corresponding to the given WebSockets.\n *\n * @param sockets - Array of WebSocket instances to resolve.\n * @returns Array of corresponding `WebSocketConnection` objects.\n */\n public select(sockets: WebSocket[]): WebSocketConnection<A>[] {\n const result: WebSocketConnection<A>[] = [];\n for (const ws of sockets) {\n const con = this.map.get(ws);\n if (con) result.push(con);\n }\n return result;\n }\n\n /**\n * Returns an iterator over all active `WebSocketConnection` objects\n * managed by this session.\n *\n * Useful for iterating over all connections to perform actions such as\n * broadcasting messages.\n *\n * @returns Iterable iterator of all active `WebSocketConnection` objects.\n */\n public values(): IterableIterator<WebSocketConnection<A>> {\n return this.map.values();\n }\n\n /**\n * Returns an iterator over all active raw `WebSocket` instances\n * currently tracked by this session.\n *\n * @returns Iterable iterator of all active `WebSocket` instances.\n */\n public keys(): IterableIterator<WebSocket> {\n return this.map.keys();\n }\n\n /**\n * Closes a managed WebSocket connection with optional code and reason.\n *\n * @param ws - WebSocket to close.\n * @param code - Optional WebSocket close code.\n * @param reason - Optional reason string.\n * @returns `true` if the connection was managed and removed, `false` otherwise.\n */\n public close(ws: WebSocket, code?: number, reason?: string): boolean {\n const con = this.get(ws);\n if (con) con.close(code, reason);\n\n return this.unregister(ws);\n }\n\n /** Iterates over all active WebSocket connections. */\n public *[Symbol.iterator](): IterableIterator<WebSocketConnection<A>> {\n yield* this.values();\n }\n\n /** Registers a connection internally. */\n private register(ws: WebSocket, con: WebSocketConnection<A>): void {\n this.map.set(ws, con);\n }\n\n /** Un-registers a connection internally. */\n private unregister(ws: WebSocket): boolean {\n return this.map.delete(ws);\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/guards/basic.ts","../src/guards/websocket.ts","../src/websocket/events.ts","../src/websocket/constants.ts","../src/websocket/utils.ts","../src/websocket/base.ts","../src/websocket/new.ts","../src/websocket/restore.ts","../src/websocket/sessions.ts"],"names":["isString","value","isNumber","isBinary","isSendable","assertSerializable","WebSocketEvents","_WebSocketEvents","server","type","listener","options","arr","finalOptions","index","ev","once","listeners","msg","CloseCode","WS_RESERVED_CODES","safeCloseCode","code","isCodeInRange","isReservedCode","safeReason","reason","BaseWebSocket","data","attachment","merged","states","event","NewConnectionBase","pair","client","ctx","tags","RestoredConnectionBase","ws","WebSocketSessions","NewConnection","sessions","connection","RestoredConnection","restore","all","restored","sockets","result","con"],"mappings":"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","file":"sessions.js","sourcesContent":["/*\n * Copyright (C) 2025 Ty Busby\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Checks if the provided value is an array of strings.\n *\n * @param value - The value to check.\n * @returns True if `array` is an array where every item is a string.\n */\nexport function isStringArray(value: unknown): value is string[] {\n return Array.isArray(value) && value.every((item) => typeof item === \"string\");\n}\n\n/**\n * Checks if a value is a string.\n *\n * @param value - The value to check.\n * @returns `true` if the value is a string, otherwise `false`.\n */\nexport function isString(value: unknown): value is string {\n return typeof value === \"string\";\n}\n\n\n/**\n * Checks if a value is a valid number (not NaN).\n *\n * This function returns `true` if the value is of type `number`\n * and is not `NaN`. It works as a type guard for TypeScript.\n *\n * @param value - The value to check.\n * @returns `true` if the value is a number and not `NaN`, otherwise `false`.\n */\nexport function isNumber(value: unknown): value is number {\n return typeof value === \"number\" && !Number.isNaN(value);\n}\n\n/**\n * Checks if a value is a boolean.\n *\n * @param value - The value to check.\n * @returns `true` if the value is a boolean (`true` or `false`), otherwise `false`.\n */\nexport function isBoolean(value: unknown): value is boolean {\n return typeof value === \"boolean\";\n}\n","/*\n * Copyright (C) 2025 Ty Busby\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { isString } from \"./basic\";\n\n/**\n * Checks whether a value is binary data suitable for WebSocket transmission.\n *\n * A value is considered binary if it is an {@link ArrayBuffer} or any\n * {@link ArrayBufferView} (such as a `Uint8Array`, `DataView`, etc.).\n *\n * @param value - The value to check.\n * @returns `true` if `value` is an {@link ArrayBuffer} or {@link ArrayBufferView}, otherwise `false`.\n */\nexport function isBinary(value: unknown): value is ArrayBuffer | ArrayBufferView {\n return value instanceof ArrayBuffer || ArrayBuffer.isView(value);\n}\n\n/**\n * Checks whether a value can be sent over a WebSocket connection.\n *\n * A sendable value is either:\n * - a non-empty string, or\n * - a non-empty {@link ArrayBuffer} / {@link ArrayBufferView}.\n *\n * Empty strings and zero-length binary data are considered non-sendable.\n *\n * @param value - The value to check.\n * @returns `true` if `value` is a non-empty string or binary buffer, otherwise `false`.\n */\nexport function isSendable(value: unknown): value is string | ArrayBuffer | ArrayBufferView {\n if (isString(value)) return value.length > 0;\n if (isBinary(value)) return value.byteLength > 0;\n return false;\n}\n\n/**\n * Asserts that a value is a serializable object suitable for JSON encoding.\n *\n * This function ensures the value is a non-null object and that\n * `JSON.stringify()` succeeds without throwing an error.\n *\n * @param value - The value to validate.\n * @throws {TypeError} If `value` is not an object or cannot be serialized to JSON.\n */\nexport function assertSerializable(value: unknown): asserts value is object {\n if (value === null || typeof value !== \"object\") {\n throw new TypeError(\"WebSocket attachment must be an object\");\n }\n try {\n JSON.stringify(value);\n } catch {\n throw new TypeError(\"WebSocket attachment is not serializable\");\n }\n}\n","/*\n * Copyright (C) 2025 Ty Busby\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n CustomEventType,\n EventOptions,\n ExtendedEventListener,\n ExtendedEventMap,\n ExtendedEventType,\n} from \"../interfaces/websocket\";\n\n/**\n * Base class for managing WebSocket events, including both standard WebSocket events\n * and internal custom events (`open` and `warn`).\n *\n * This class wraps a native WebSocket instance (`server`) and provides:\n * - Delegation of standard events (`message`, `close`, etc.)\n * - Support for custom events used internally by the library:\n * - `open`: dispatched once when the connection is accepted\n * - `warn`: dispatched whenever a warning occurs\n *\n * Subclasses can call `warn()` or `open()` to trigger custom events.\n */\nexport abstract class WebSocketEvents {\n /** The underlying WebSocket server instance being wrapped. */\n protected readonly server: WebSocket;\n\n /** Internal map of custom event listeners. */\n private customListeners: {\n [K in ExtendedEventType]?: ((ev: ExtendedEventMap[K]) => void)[];\n } = {};\n\n /**\n * @param server - The native WebSocket instance to wrap.\n */\n constructor(server: WebSocket) {\n this.server = server;\n }\n\n /**\n * Adds an event listener for either a standard WebSocket event or a custom event.\n *\n * - Custom events: `open`, `warn`\n * - Standard events: `message`, `close`, `error`, etc.\n *\n * The `close` event is automatically set to `{ once: true }` if no options are provided.\n *\n * @param type - Event type to listen for.\n * @param listener - Callback invoked when the event occurs.\n * @param options - Optional event options (`once`).\n */\n public addEventListener<K extends ExtendedEventType>(\n type: K,\n listener: ExtendedEventListener<K>,\n options?: EventOptions,\n ): void {\n if (WebSocketEvents.isCustomEvent(type)) {\n let arr = this.customListeners[type];\n if (!arr) {\n arr = [];\n this.customListeners[type] = arr;\n }\n arr.push(listener);\n } else {\n const finalOptions = type === \"close\" ? { ...options, once: true } : options;\n this.server.addEventListener(\n type as keyof WebSocketEventMap,\n listener as EventListener,\n finalOptions,\n );\n }\n }\n\n /**\n * Removes a previously registered event listener.\n *\n * Works for both standard WebSocket events and custom events.\n *\n * @param type - Event type to remove.\n * @param listener - Listener function to remove.\n */\n public removeEventListener<K extends ExtendedEventType>(\n type: K,\n listener: ExtendedEventListener<K>,\n ): void {\n if (WebSocketEvents.isCustomEvent(type)) {\n const arr = this.customListeners[type];\n if (arr) {\n const index = arr.indexOf(listener);\n if (index !== -1) arr.splice(index, 1);\n }\n } else {\n this.server.removeEventListener(\n type as keyof WebSocketEventMap,\n listener as EventListener,\n );\n }\n }\n\n /**\n * Dispatches a custom event to all registered listeners.\n *\n * @param type - The custom event type (`open` or `warn`).\n * @param ev - Event object to pass to listeners.\n * @param once - If `true`, all listeners for this event are removed after dispatch.\n */\n private dispatch<K extends CustomEventType>(\n type: K,\n ev: ExtendedEventMap[K],\n once: boolean = false,\n ): void {\n const listeners = this.customListeners[type]?.slice() ?? [];\n if (once) {\n this.customListeners[type] = [];\n }\n for (const listener of listeners) {\n listener(ev);\n }\n }\n\n /**\n * Dispatches a `warn` event with a given message.\n *\n * Intended for internal use to notify listeners of warnings.\n *\n * @param msg - Warning message to emit.\n */\n protected warn(msg: string) {\n this.dispatch(\"warn\", { type: \"warn\", message: msg });\n }\n\n /**\n * Dispatches an `open` event.\n *\n * Intended to signal that the WebSocket has been accepted and is ready.\n * This event is dispatched only once.\n */\n protected open() {\n this.dispatch(\"open\", new Event(\"open\"), true);\n }\n\n /** Internal helper to determine if an event is a custom event. */\n private static isCustomEvent(type: ExtendedEventType): boolean {\n return [\"open\", \"warn\"].includes(type);\n }\n}\n","/*\n * Copyright (C) 2025 Ty Busby\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/** Max close code a user can send */\nexport const WS_MAX_CLOSE_CODE = 4999;\n/** Max number of reason chars a user can send */\nexport const WS_MAX_REASON_CHARS = 123;\n\n/** WebSocket close codes */\nexport const CloseCode = {\n NORMAL: 1000,\n NO_STATUS: 1005,\n ABNORMAL: 1006,\n TLS_HANDSHAKE: 1015,\n} as const;\n\n/** WebSocket RESERVED close codes */\nexport const WS_RESERVED_CODES = new Set<number>([\n CloseCode.NO_STATUS,\n CloseCode.ABNORMAL,\n CloseCode.TLS_HANDSHAKE,\n]);\n","/*\n * Copyright (C) 2025 Ty Busby\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { isNumber, isString } from \"../guards/basic\";\n\nimport { CloseCode, WS_MAX_CLOSE_CODE, WS_MAX_REASON_CHARS, WS_RESERVED_CODES } from \"./constants\";\n\n/**\n * Normalizes a WebSocket close code to ensure it is safe to send.\n *\n * - Returns `CloseCode.NORMAL` if the code is undefined, out of range, or reserved.\n *\n * @param code - The optional close code to validate.\n * @returns A valid close code to use for WebSocket closure.\n */\nexport function safeCloseCode(code?: number): number {\n if (!isNumber(code)) return CloseCode.NORMAL;\n if (isCodeInRange(code) && !isReservedCode(code)) return code;\n return CloseCode.NORMAL;\n}\n\n/**\n * Determines whether a close code is within the valid WebSocket range.\n *\n * @param code - The code to validate.\n * @returns `true` if the code is within 1000–4999, `false` otherwise.\n */\nexport function isCodeInRange(code: number): boolean {\n return code >= CloseCode.NORMAL && code <= WS_MAX_CLOSE_CODE;\n}\n\n/**\n * Determines whether a close code is reserved by the WebSocket specification.\n *\n * @param code - The code to check.\n * @returns `true` if the code is reserved, `false` otherwise.\n */\nexport function isReservedCode(code: number): boolean {\n return WS_RESERVED_CODES.has(code);\n}\n\n/**\n * Sanitizes a close reason string to comply with WebSocket limits.\n *\n * - Removes non-printable ASCII characters.\n * - Truncates to the maximum allowed length (`WS_MAX_REASON_CHARS`).\n *\n * @param reason - The optional reason string to sanitize.\n * @returns A cleaned reason string or `undefined` if input is invalid.\n */\nexport function safeReason(reason?: string): string | undefined {\n if (!isString(reason)) return;\n return reason.replaceAll(/[^\\x20-\\x7E]/g, \"\").slice(0, WS_MAX_REASON_CHARS);\n}\n","/*\n * Copyright (C) 2025 Ty Busby\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { assertSerializable, isSendable } from \"../guards/websocket\";\nimport { WSAttachment } from \"../interfaces/websocket\";\n\nimport { WebSocketEvents } from \"./events\";\nimport { safeCloseCode, safeReason } from \"./utils\";\n\n/**\n * Base class providing core WebSocket functionality and attachment management.\n *\n * Extends `WebSocketEvents` to inherit custom event handling.\n *\n * @template A - Type of the attachment object associated with this WebSocket.\n */\nexport abstract class BaseWebSocket<A extends WSAttachment> extends WebSocketEvents {\n /** Tracks whether the WebSocket has been accepted. */\n protected accepted = false;\n\n /** The underlying WebSocket server instance. */\n protected readonly server: WebSocket;\n\n /**\n * Initializes the base WebSocket wrapper.\n *\n * Registers a listener to handle the underlying WebSocket `close` event.\n *\n * @param server - The underlying WebSocket instance.\n */\n constructor(server: WebSocket) {\n super(server);\n this.server = server;\n this.server.addEventListener(\"close\", this.onclose);\n }\n\n /**\n * Sends a message over the WebSocket if it is open.\n *\n * Performs validation to ensure the WebSocket is in an open state\n * and the data is non-empty and sendable. Emits a warning if not.\n *\n * @param data - The message to send, as a string or binary data.\n */\n public send(data: string | ArrayBuffer | ArrayBufferView): void {\n if (this.isState(WebSocket.CONNECTING, WebSocket.CLOSED)) {\n this.warn(\"Cannot send: WebSocket not open\");\n return;\n }\n if (!isSendable(data)) {\n this.warn(\"Cannot send: empty or invalid data\");\n return;\n }\n\n this.server.send(data);\n }\n\n /**\n * Returns the current attachment associated with this WebSocket.\n *\n * Attachments are stored as serialized objects on the underlying WebSocket.\n *\n * @returns Readonly attachment object of type `A`.\n */\n public get attachment(): Readonly<A> {\n return (this.server.deserializeAttachment() ?? {}) as A;\n }\n\n /**\n * Updates the attachment object for this WebSocket.\n *\n * Merges the provided partial attachment with the current attachment,\n * ensures it is serializable, and stores it on the underlying WebSocket.\n *\n * @param attachment - Partial or full attachment object to store,\n * or `null` to clear the attachment.\n */\n public attach(attachment?: Partial<A> | null): void {\n if (attachment === undefined) return;\n if (attachment === null) {\n this.server.serializeAttachment({});\n } else {\n const current = this.attachment;\n const merged = { ...current, ...attachment };\n assertSerializable(merged);\n this.server.serializeAttachment(merged);\n }\n }\n\n /**\n * Returns the current WebSocket ready state.\n *\n * If the WebSocket has not been accepted, returns `WebSocket.CONNECTING`.\n *\n * @returns The ready state of the WebSocket.\n */\n public get readyState(): number {\n if (!this.accepted) return WebSocket.CONNECTING;\n return this.server.readyState;\n }\n\n /**\n * Checks if the current ready state matches any of the provided states.\n *\n * @param states - One or more WebSocket state constants to check.\n * @returns `true` if the current state matches any provided, `false` otherwise.\n */\n public isState(...states: number[]): boolean {\n return states.includes(this.readyState);\n }\n\n /**\n * Closes the WebSocket safely.\n *\n * Removes the internal `close` listener and closes the underlying WebSocket\n * using validated close code and sanitized reason.\n *\n * @param code - Optional WebSocket close code.\n * @param reason - Optional reason for closing the WebSocket.\n */\n public close(code?: number, reason?: string): void {\n this.server.removeEventListener(\"close\", this.onclose);\n this.server.close(safeCloseCode(code), safeReason(reason));\n }\n\n /** Internal handler for the underlying WebSocket `close` event. */\n private readonly onclose = (event: CloseEvent): void => {\n this.close(event.code, event.reason);\n };\n}\n","/*\n * Copyright (C) 2025 Ty Busby\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { WebSocketConnection, WSAttachment } from \"../interfaces/websocket\";\n\nimport { BaseWebSocket } from \"./base\";\n\n/**\n * Internal base for managing a new WebSocket connection.\n *\n * - Creates a WebSocket pair and stores the client side internally.\n * - Provides methods to accept the server WebSocket and retrieve the client.\n * - Ensures the `open` event is dispatched when the connection is accepted.\n * - Tracks whether the connection has already been accepted to prevent multiple acceptances.\n *\n * @template A - Type of the attachment object for this connection.\n */\nexport abstract class NewConnectionBase<A extends WSAttachment>\n extends BaseWebSocket<A>\n implements WebSocketConnection<A>\n{\n /** The client-facing end of the WebSocket pair. */\n private readonly client: WebSocket;\n\n /**\n * Creates a new WebSocket pair and initializes the server side.\n *\n * The client side is stored internally and returned upon acceptance.\n */\n public constructor() {\n const pair = new WebSocketPair();\n const [client, server] = [pair[0], pair[1]];\n super(server);\n this.client = client;\n }\n\n /**\n * Accepts the server WebSocket and returns the client WebSocket.\n *\n * If already accepted, returns the existing client WebSocket.\n * Otherwise, uses a Durable Object state to accept the connection\n * and marks it as ready.\n *\n * @param ctx - DurableObjectState used to accept the WebSocket.\n * @param tags - Optional array of tags to attach to the WebSocket.\n * @returns Readonly client WebSocket ready for use.\n */\n public acceptWebSocket(ctx: DurableObjectState, tags?: string[]): Readonly<WebSocket> {\n if (this.accepted) return this.client;\n ctx.acceptWebSocket(this.server, tags);\n return this.ready();\n }\n\n /**\n * Accepts the server WebSocket and returns the client WebSocket.\n *\n * If already accepted, returns the existing client WebSocket.\n * Otherwise, calls the internal server accept method and marks\n * the connection as ready.\n *\n * @returns Readonly client WebSocket ready for use.\n */\n public accept(): Readonly<WebSocket> {\n if (this.accepted) return this.client;\n this.server.accept();\n return this.ready();\n }\n\n /**\n * Marks the WebSocket connection as ready.\n *\n * Sets the accepted flag, dispatches the `open` event,\n * and returns the client WebSocket.\n *\n * @returns Client WebSocket ready for use.\n */\n private ready(): WebSocket {\n this.accepted = true;\n this.open();\n\n return this.client;\n }\n}\n","/*\n * Copyright (C) 2025 Ty Busby\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { WebSocketConnection, WSAttachment } from \"../interfaces/websocket\";\n\nimport { BaseWebSocket } from \"./base\";\n\n/**\n * Internal base for a WebSocket connection that has already been restored.\n *\n * - Marks the connection as accepted immediately upon construction.\n * - Overrides acceptance methods to prevent re-accepting an already-active WebSocket.\n * - Throws an error if `accept()` or `acceptWebSocket()` is called.\n *\n * @template A - Type of the attachment object for this connection.\n */\nexport abstract class RestoredConnectionBase<A extends WSAttachment>\n extends BaseWebSocket<A>\n implements WebSocketConnection<A>\n{\n constructor(ws: WebSocket) {\n super(ws);\n this.accepted = true;\n }\n\n /** Not supported for restored connections; throws an error. */\n public accept(): Readonly<WebSocket> {\n throw new Error(\"Do not call accept() on restore\");\n }\n\n /** Not supported for restored connections; throws an error. */\n public acceptWebSocket(): Readonly<WebSocket> {\n throw new Error(\"Do not call acceptWebSocket() on restore\");\n }\n}\n","/*\n * Copyright (C) 2025 Ty Busby\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { WebSocketConnection, WSAttachment } from \"../interfaces/websocket\";\n\nimport { NewConnectionBase } from \"./new\";\nimport { RestoredConnectionBase } from \"./restore\";\n\n/**\n * Manages active WebSocket connections in a Cloudflare Workers environment.\n *\n * Provides a simple interface for creating, restoring, and managing\n * WebSocket connections with optional attachments. Users can:\n *\n * - Create new WebSocket connections (`create`) and attach arbitrary data.\n * - Accept connections using the standard WebSocket API (`accept`).\n * - Accept connections using the hibernatable WebSocket API (`acceptWebSocket`),\n * which allows the connection to be put to sleep when inactive.\n * - Restore existing WebSockets into a managed session (`restore`, `restoreAll`),\n * maintaining their hibernation state.\n * - Iterate over active connections or retrieve a connection by its WebSocket instance.\n * - Close a connection cleanly with optional code and reason (`close`).\n *\n * @template A - Type of attachment data stored on each WebSocket connection.\n */\nexport class WebSocketSessions<A extends WSAttachment = WSAttachment> {\n /** @internal Map of active WebSocket to their connection wrapper. */\n private readonly map = new Map<WebSocket, WebSocketConnection<A>>();\n\n /**\n * Create a new WebSocket connection and optionally attach user data.\n *\n * @param attachment - Partial attachment object to initialize the connection with.\n * @returns A `WebSocketConnection` instance ready for accepting and sending messages.\n */\n public create(attachment?: Partial<A>): WebSocketConnection<A> {\n class NewConnection extends NewConnectionBase<A> {\n constructor(private readonly sessions: WebSocketSessions<A>) {\n super();\n }\n\n public override accept(): WebSocket {\n this.addEventListener(\"close\", () => this.sessions.unregister(this.server));\n this.sessions.register(this.server, this);\n return super.accept();\n }\n\n public override acceptWebSocket(ctx: DurableObjectState, tags?: string[]): WebSocket {\n this.sessions.register(this.server, this);\n return super.acceptWebSocket(ctx, tags);\n }\n }\n\n const connection = new NewConnection(this);\n connection.attach(attachment);\n return connection;\n }\n\n /**\n * Wraps an existing WebSocket in a managed connection session.\n *\n * @param ws - An existing WebSocket to restore.\n * @returns A `WebSocketConnection` representing the restored session.\n */\n public restore(ws: WebSocket): WebSocketConnection<A> {\n class RestoredConnection extends RestoredConnectionBase<A> {\n constructor(sessions: WebSocketSessions<A>, restore: WebSocket) {\n super(restore);\n sessions.register(this.server, this);\n }\n }\n return new RestoredConnection(this, ws);\n }\n\n /**\n * Restores multiple WebSockets into managed sessions at once.\n *\n * @param all - Array of WebSocket instances to restore.\n * @returns Array of `WebSocketConnections` restored.\n */\n public restoreAll(all: WebSocket[]): ReadonlyArray<WebSocketConnection<A>> {\n const restored: WebSocketConnection<A>[] = [];\n for (const ws of all) {\n restored.push(this.restore(ws));\n }\n return restored;\n }\n\n /**\n * Retrieves the managed connection for a specific WebSocket, if any.\n *\n * @param ws - WebSocket instance.\n * @returns Corresponding `WebSocketConnection` or `undefined` if not managed.\n */\n public get(ws: WebSocket): WebSocketConnection<A> | undefined {\n return this.map.get(ws);\n }\n\n /**\n * Selects the managed `WebSocketConnection` objects corresponding to the given WebSockets.\n *\n * @param sockets - Array of WebSocket instances to resolve.\n * @returns Array of corresponding `WebSocketConnection` objects.\n */\n public select(sockets: WebSocket[]): WebSocketConnection<A>[] {\n const result: WebSocketConnection<A>[] = [];\n for (const ws of sockets) {\n const con = this.map.get(ws);\n if (con) result.push(con);\n }\n return result;\n }\n\n /**\n * Returns an iterator over all active `WebSocketConnection` objects\n * managed by this session.\n *\n * Useful for iterating over all connections to perform actions such as\n * broadcasting messages.\n *\n * @returns Iterable iterator of all active `WebSocketConnection` objects.\n */\n public values(): IterableIterator<WebSocketConnection<A>> {\n return this.map.values();\n }\n\n /**\n * Returns an iterator over all active raw `WebSocket` instances\n * currently tracked by this session.\n *\n * @returns Iterable iterator of all active `WebSocket` instances.\n */\n public keys(): IterableIterator<WebSocket> {\n return this.map.keys();\n }\n\n /**\n * Closes a managed WebSocket connection with optional code and reason.\n *\n * @param ws - WebSocket to close.\n * @param code - Optional WebSocket close code.\n * @param reason - Optional reason string.\n * @returns `true` if the connection was managed and removed, `false` otherwise.\n */\n public close(ws: WebSocket, code?: number, reason?: string): boolean {\n const con = this.get(ws);\n if (con) con.close(code, reason);\n\n return this.unregister(ws);\n }\n\n /**\n * @returns the number of active WebSocket connections.\n */\n public get size(): number {\n return this.map.size;\n }\n\n /** Iterates over all active WebSocket connections. */\n public *[Symbol.iterator](): IterableIterator<WebSocketConnection<A>> {\n yield* this.values();\n }\n\n /** Registers a connection internally. */\n private register(ws: WebSocket, con: WebSocketConnection<A>): void {\n this.map.set(ws, con);\n }\n\n /** Un-registers a connection internally. */\n private unregister(ws: WebSocket): boolean {\n return this.map.delete(ws);\n }\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adonix.org/cloud-spark",
3
- "version": "2.1.2",
3
+ "version": "2.2.0",
4
4
  "description": "Ignite your Cloudflare Workers with a type-safe library for rapid development.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -68,20 +68,20 @@
68
68
  },
69
69
  "devDependencies": {
70
70
  "@eslint/js": "^9.39.2",
71
- "@types/node": "^25.0.3",
72
- "@typescript-eslint/eslint-plugin": "^8.51.0",
73
- "@typescript-eslint/parser": "^8.51.0",
74
- "@vitest/coverage-v8": "^4.0.16",
71
+ "@types/node": "^25.2.1",
72
+ "@typescript-eslint/eslint-plugin": "^8.54.0",
73
+ "@typescript-eslint/parser": "^8.54.0",
74
+ "@vitest/coverage-v8": "^4.0.18",
75
75
  "eslint": "^9.39.2",
76
76
  "eslint-plugin-import": "^2.32.0",
77
- "globals": "^16.5.0",
77
+ "globals": "^17.3.0",
78
78
  "jiti": "^2.6.1",
79
- "prettier": "^3.7.4",
79
+ "prettier": "^3.8.1",
80
80
  "tsup": "^8.5.1",
81
81
  "typescript": "^5.9.3",
82
- "typescript-eslint": "^8.51.0",
83
- "vitest": "^4.0.16",
84
- "wrangler": "^4.56.0"
82
+ "typescript-eslint": "^8.54.0",
83
+ "vitest": "^4.0.18",
84
+ "wrangler": "^4.63.0"
85
85
  },
86
86
  "dependencies": {
87
87
  "cache-control-parser": "^2.0.6",