@undercroft/pulse-frame
Version:
Low-level WebSocket framing library implementing RFC 6455. Supports payload masking, control frames, and binary/text handling.
2 lines • 6.67 kB
JavaScript
"use strict";var g=Object.defineProperty;var j=Object.getOwnPropertyDescriptor;var z=Object.getOwnPropertyNames;var J=Object.prototype.hasOwnProperty;var q=(e,r)=>{for(var t in r)g(e,t,{get:r[t],enumerable:!0})},Q=(e,r,t,o)=>{if(r&&typeof r=="object"||typeof r=="function")for(let a of z(r))!J.call(e,a)&&a!==t&&g(e,a,{get:()=>r[a],enumerable:!(o=j(r,a))||o.enumerable});return e};var W=e=>Q(g({},"__esModule",{value:!0}),e);var te={};q(te,{createFrame:()=>b,createFrameFromBuffer:()=>G,createFrameFromControlCode:()=>Y,createFrameFromText:()=>K,frameToBuffer:()=>U});module.exports=W(te);function B(e,r){let t=Buffer.allocUnsafe(e.length);for(let o=0;o<e.length;o++)t[o]=e[o]^r[o%4];return t}var l=(i=>(i[i.CONTINUATION=0]="CONTINUATION",i[i.TEXT=1]="TEXT",i[i.BINARY=2]="BINARY",i[i.CLOSE=8]="CLOSE",i[i.PING=9]="PING",i[i.PONG=10]="PONG",i))(l||{});var f=class{constructor(r,t,o,a){this.header=r,this.payload=t,this.extensionData=o,this.applicationData=a,this.masked=r.mask===1&&!!r.maskingKey}setApplicationData(r){this.applicationData=r}getPayload(){return this.applicationData??this.payload}getPayloadString(){return this.applicationData?.toString("utf8")??""}getExtensionData(){return this.extensionData}getHeader(){return this.header}isMasked(){return this.masked}isFinal(){return this.header.fin===1}getOpcode(){return this.header.opcode}isText(){return this.header.opcode===1}isBinary(){return this.header.opcode===2}isContinuation(){return this.header.opcode===0}isControl(){return this.header.opcode>=8}isPing(){return this.header.opcode===9}isPong(){return this.header.opcode===10}isClose(){return this.header.opcode===8}isData(){return this.isText()||this.isBinary()}toJSON(){return{fin:this.header.fin,rsv1:this.header.rsv1,rsv2:this.header.rsv2,rsv3:this.header.rsv3,opcode:l[this.header.opcode]??`Unknown(${this.header.opcode})`,mask:this.header.mask,length:this.header.length,isControl:this.isControl(),isText:this.isText(),isBinary:this.isBinary(),isClose:this.isClose(),isPing:this.isPing(),isPong:this.isPong(),isMasked:this.masked,applicationDataLength:this.applicationData?.length??0,extensionDataLength:this.extensionData?.length??0}}toString(){return`PulseFrame[opcode=${l[this.header.opcode]??this.header.opcode}, fin=${this.header.fin}, length=${this.header.length}, masked=${this.masked}]`}};function b(e){let r={fin:e.fin??1,rsv1:e.rsv1??0,rsv2:e.rsv2??0,rsv3:e.rsv3??0,mask:e.mask??0,opcode:e.opcode,extensionDataLength:e.extensionDataLength??0,maskingKey:e.maskingKey,length:0},t=e.payloadData??Buffer.alloc(0),o=r.mask===1&&!!r.maskingKey,a=e.extensionData??void 0,m=o?B(t,r.maskingKey):t;return r.length=m.length,new f(r,m,a,m)}var n=class e extends Error{constructor(t){super(t);this.message=t;this.name="PulseFrameError",Object.setPrototypeOf(this,e.prototype)}};function N(e,r){if(r===0)return e;if(r>e.length)throw new n("Invalid application data length: exceeds buffer size");return e.subarray(r)}function O(e,r,t){let o=r+t;if(e.length<o)throw new n("Incomplete frame: expected 4 bytes for masking key");return e.subarray(r,o)}function A(e,r,t){let o=r+t;if(e.length<o)throw new n("Incomplete frame: not enough bytes for payload");return e.subarray(r,o)}function L(e){if(e<126)return 0;if(e===126)return 2;if(e===127)return 8;throw new n("Invalid payload length for extended length")}function v(e,r){let t=e[0],o=e[1];return{fin:(t&128)>>>7,rsv1:(t&64)>>>6,rsv2:(t&32)>>>5,rsv3:(t&16)>>>4,mask:(o&128)>>>7,opcode:t&15,length:o&127,extensionDataLength:r,maskingKey:void 0}}function k(e,r){let t=Buffer.allocUnsafe(e.length);for(let o=0;o<e.length;o++)t[o]=e[o]^r[o%4];return t}var Z=new TextDecoder("utf-8",{fatal:!0});function w(e){return Z.decode(e)}var H=e=>{if(e.length===1)throw new n("Protocol error: close frame payload too small");if(e.length>=2){let r=e.readUInt16BE(0);if(r<1e3||r>4999)throw new n(`Protocol error: invalid close code ${r}`)}if(e.length>2){let r=e.subarray(2);try{w(r)}catch{throw new n("Protocol error: invalid UTF-8 in close reason")}}};function S(e,r){if(e.rsv1===1&&!r.rsv1)throw new Error("RSV1 is set but not supported by any extension");if(e.rsv2===1&&!r.rsv2)throw new Error("RSV2 is set but not supported by any extension");if(e.rsv3===1&&!r.rsv3)throw new Error("RSV3 is set but not supported by any extension")}function F(e){let{opcode:r,fin:t}=e;if(!Object.values(l).includes(r))throw new n(`Invalid opcode: ${r}`);let o=r>=8;if(o&&t!==1)throw new n("Protocol error: control frames must not be fragmented (FIN=1)");if(o&&e.length>125)throw new n("Control frame payload too long")}function I(e,r,t){if(r<126)return r;if(r===126){if(e.length<t+2)throw new n("Incomplete frame: expected 2 bytes for extended length");return e.readUInt16BE(t)}if(r===127){if(e.length<t+8)throw new n("Incomplete frame: expected 8 bytes for extended length");return Number(e.readBigUInt64BE(t))}throw new n("Invalid payload length")}function C(e,r,t){if(t===0)return Buffer.alloc(0);let o=r+t;if(o>e.length)throw new n("Incomplete frame: extension data exceeds buffer length");return e.subarray(r,o)}function G(e,r){if(e.length<2)throw new n("Incomplete frame: missing first two bytes");let t=r?.extensionLength??0,o=r?.extensionSupport??{rsv1:!1,rsv2:!1,rsv3:!1},a=v(e,t),m=a.mask===1||!!a.maskingKey;S(a,o),F(a);let i=I(e,a.length,2),u=L(a.length);if(m){let $=O(e,2+u,4);a.maskingKey=$}let P=2+u+(m?4:0),x=A(e,P,i),_=m?k(x,a.maskingKey):x,V=C(_,0,t),y=N(_,t);return a.opcode===8&&H(y),new f(a,_,V,y)}function Y(e,r){let t=Buffer.isBuffer(e)?e:Buffer.alloc(0);if(t.length>125)throw new n("Control frame payload too large (max 125 bytes)");let o={fin:1,rsv1:0,rsv2:0,rsv3:0,mask:0,opcode:r,length:t.length,extensionDataLength:0,maskingKey:void 0};return new f(o,t,void 0,t)}function K(e){let r=Buffer.from(e,"utf8"),t={fin:1,rsv1:0,rsv2:0,rsv3:0,mask:0,opcode:1,length:r.length,extensionDataLength:0,maskingKey:void 0};return new f(t,r,void 0,r)}function X(e){return e<126?0:e<65536?2:8}function M(e){if(e<126)return Buffer.alloc(0);if(e<65536){let t=Buffer.alloc(2);return t.writeUInt16BE(e,0),t}let r=Buffer.alloc(8);return r.writeBigUInt64BE(BigInt(e),0),r}var R=(e,r)=>{let t=e.fin<<7|e.rsv1<<6|e.rsv2<<5|e.rsv3<<4|e.opcode&15,o=r<126?r:r<65536?126:127,a=e.mask<<7|o;return[t,a]};function U(e){let r=e.getPayload(),t=r.length,o=X(t),a=M(t),m=2+o;e.isMasked()&&(m+=4);let i=e.isMasked()?B(r,e.getHeader().maskingKey):r,u=Buffer.alloc(m),[P,x]=R(e.getHeader(),t);return u[0]=P,u[1]=x,a.copy(u,2),e.isMasked()&&e.getHeader().maskingKey.copy(u,2+o),Buffer.concat([u,i])}0&&(module.exports={createFrame,createFrameFromBuffer,createFrameFromControlCode,createFrameFromText,frameToBuffer});
//# sourceMappingURL=index.js.map