UNPKG

@qsocket/protocol

Version:

QSocket Protocol: A versatile protocol for transmitting messages of any type in buffer format, designed exclusively for the QSocket ecosystem. Enables efficient, high-speed data transfer between processes and across client-server connections.

296 lines (294 loc) 10.3 kB
// src/bin/protocol.enums.ts var EQSocketProtocolMessageType = /* @__PURE__ */ ((EQSocketProtocolMessageType2) => { EQSocketProtocolMessageType2[EQSocketProtocolMessageType2["DATA"] = 0] = "DATA"; EQSocketProtocolMessageType2[EQSocketProtocolMessageType2["CONTROL"] = 1] = "CONTROL"; EQSocketProtocolMessageType2[EQSocketProtocolMessageType2["ACK"] = 2] = "ACK"; return EQSocketProtocolMessageType2; })(EQSocketProtocolMessageType || {}); var EQSocketProtocolContentType = /* @__PURE__ */ ((EQSocketProtocolContentType2) => { EQSocketProtocolContentType2[EQSocketProtocolContentType2["UNDEFINED"] = 0] = "UNDEFINED"; EQSocketProtocolContentType2[EQSocketProtocolContentType2["NULL"] = 1] = "NULL"; EQSocketProtocolContentType2[EQSocketProtocolContentType2["BOOLEAN"] = 2] = "BOOLEAN"; EQSocketProtocolContentType2[EQSocketProtocolContentType2["NUMBER"] = 3] = "NUMBER"; EQSocketProtocolContentType2[EQSocketProtocolContentType2["STRING"] = 4] = "STRING"; EQSocketProtocolContentType2[EQSocketProtocolContentType2["JSON"] = 5] = "JSON"; EQSocketProtocolContentType2[EQSocketProtocolContentType2["BUFFER"] = 6] = "BUFFER"; return EQSocketProtocolContentType2; })(EQSocketProtocolContentType || {}); // src/bin/protocol.errors.ts var QSocketProtocolEncodeError = class extends Error { /** * Creates an instance of QSocketProtocolEncodeError. * * @param message - A descriptive error message. * @param originalError - (Optional) The original error that caused this encoding error, if any. */ constructor(message, originalError) { super(message); this.originalError = originalError; this.name = "QSocketProtocolEncodeError"; if (originalError) { this.stack += ` Caused by: ${originalError.stack}`; } } }; var QSocketProtocolDecodeError = class extends Error { /** * Creates an instance of QSocketProtocolDecodeError. * * @param message - A descriptive error message. * @param originalError - (Optional) The original error that caused this decoding error, if any. */ constructor(message, originalError) { super(message); this.originalError = originalError; this.name = "QSocketProtocolDecodeError"; if (originalError) { this.stack += ` Caused by: ${originalError.stack}`; } } }; // src/bin/protocol.ts var encodeString = (() => { if (typeof TextEncoder !== "undefined") { const encoder = new TextEncoder(); return (str) => encoder.encode(str); } if (typeof Buffer !== "undefined") { return (str) => { const buffer = Buffer.from(str, "utf8"); const copy = new Uint8Array(buffer.length); copy.set(buffer); return copy; }; } return (str) => { const maxLength = str.length * 4; const result = new Uint8Array(maxLength); let offset = 0; for (let i = 0; i < str.length; i++) { let charCode = str.charCodeAt(i); if (charCode < 128) { result[offset++] = charCode; } else if (charCode < 2048) { result[offset++] = 192 | charCode >> 6; result[offset++] = 128 | charCode & 63; } else if (charCode < 55296 || charCode >= 57344) { result[offset++] = 224 | charCode >> 12; result[offset++] = 128 | charCode >> 6 & 63; result[offset++] = 128 | charCode & 63; } else { if (++i >= str.length) throw new Error("Invalid surrogate pair in string"); charCode = 65536 + ((charCode & 1023) << 10 | str.charCodeAt(i) & 1023); result[offset++] = 240 | charCode >> 18; result[offset++] = 128 | charCode >> 12 & 63; result[offset++] = 128 | charCode >> 6 & 63; result[offset++] = 128 | charCode & 63; } } const resultBuffer = result.slice(0, offset); return resultBuffer; }; })(); var decodeString = (() => { if (typeof TextDecoder !== "undefined") { const decoder = new TextDecoder("utf-8"); return (bytes) => decoder.decode(bytes); } if (typeof Buffer !== "undefined") { return (bytes) => Buffer.from(bytes).toString("utf8"); } return (bytes) => { let result = ""; let i = 0; while (i < bytes.length) { const byte1 = bytes[i++]; if (byte1 < 128) { result += String.fromCharCode(byte1); } else if (byte1 < 224) { const byte2 = bytes[i++]; if ((byte2 & 192) !== 128) throw new Error("Invalid UTF-8 sequence"); result += String.fromCharCode((byte1 & 31) << 6 | byte2 & 63); } else if (byte1 < 240) { const byte2 = bytes[i++]; const byte3 = bytes[i++]; if ((byte2 & 192) !== 128 || (byte3 & 192) !== 128) throw new Error("Invalid UTF-8 sequence"); result += String.fromCharCode((byte1 & 15) << 12 | (byte2 & 63) << 6 | byte3 & 63); } else { const byte2 = bytes[i++]; const byte3 = bytes[i++]; const byte4 = bytes[i++]; if ((byte2 & 192) !== 128 || (byte3 & 192) !== 128 || (byte4 & 192) !== 128) throw new Error("Invalid UTF-8 sequence"); const codePoint = (byte1 & 7) << 18 | (byte2 & 63) << 12 | (byte3 & 63) << 6 | byte4 & 63; const highSurrogate = 55296 + (codePoint - 65536 >> 10); const lowSurrogate = 56320 + (codePoint - 65536 & 1023); result += String.fromCharCode(highSurrogate, lowSurrogate); } } return result; }; })(); function writeUInt32BE(buffer, offset, value) { buffer[offset] = value >>> 24 & 255; buffer[offset + 1] = value >>> 16 & 255; buffer[offset + 2] = value >>> 8 & 255; buffer[offset + 3] = value & 255; } function readUInt32BE(buffer, offset) { return (buffer[offset] << 24 | buffer[offset + 1] << 16 | buffer[offset + 2] << 8 | buffer[offset + 3]) >>> 0; } var staticDataViewBuffer = new Uint8Array(8); var staticDataView = new DataView(staticDataViewBuffer.buffer); function writeDoubleBE(buffer, offset, value) { staticDataView.setFloat64(0, value, false); buffer.set(staticDataViewBuffer, offset); } function readDoubleBE(buffer, offset) { staticDataViewBuffer.set(buffer.subarray(offset, offset + 8)); return staticDataView.getFloat64(0, false); } function to(message) { try { let totalLength = 0; const chunkBinaries = new Array(message.length); let chunk; let meta; let data; for (let i = 0; i < message.length; i++) { chunk = message[i]; meta = encodeString(JSON.stringify(chunk.meta)); data = encodePayload(chunk); totalLength += 1 + 4 + meta.length + 4 + data.length; chunkBinaries[i] = { encoding: chunk.payload["Content-Type"], meta, data }; } const messageBinary = new Uint8Array(totalLength); let offset = 0; let chunkBinary; for (let i = 0; i < chunkBinaries.length; i++) { chunkBinary = chunkBinaries[i]; messageBinary[offset] = chunkBinary.encoding; offset += 1; writeUInt32BE(messageBinary, offset, chunkBinary.meta.length); offset += 4; messageBinary.set(chunkBinary.meta, offset); offset += chunkBinary.meta.length; writeUInt32BE(messageBinary, offset, chunkBinary.data.length); offset += 4; messageBinary.set(chunkBinary.data, offset); offset += chunkBinary.data.length; } return messageBinary; } catch (error) { throw new QSocketProtocolEncodeError(error.message); } } function encodePayload(chunk) { const { data } = chunk.payload; const contentType = chunk.payload["Content-Type"]; switch (contentType) { case 0 /* UNDEFINED */: case 1 /* NULL */: return new Uint8Array(0); // No data for undefined or null case 2 /* BOOLEAN */: return new Uint8Array([data ? 1 : 0]); // 1 byte representing boolean value case 3 /* NUMBER */: const numberBuffer = new Uint8Array(8); writeDoubleBE(numberBuffer, 0, data); return numberBuffer; case 4 /* STRING */: return encodeString(data); // Encode the string case 5 /* JSON */: return encodeString(JSON.stringify(data)); // Encode the JSON string case 6 /* BUFFER */: if (data instanceof Uint8Array) { return data; } else if (data instanceof Buffer) { return new Uint8Array(data); } else { throw new Error("Invalid data type for BUFFER content type"); } default: throw new Error("Unknown content type"); } } function from(buffer) { try { const message = []; let offset = 0; while (offset < buffer.length) { const contentType = buffer[offset]; offset += 1; const metaLength = readUInt32BE(buffer, offset); offset += 4; const metaString = decodeString(buffer.slice(offset, offset + metaLength)); let meta; try { meta = JSON.parse(metaString); } catch (error) { console.log("Failed to parse metadata"); throw error; } offset += metaLength; const dataLength = readUInt32BE(buffer, offset); offset += 4; const data = decodePayloadData(buffer, contentType, offset, dataLength); offset += dataLength; message.push({ meta, payload: { data, "Content-Type": contentType } }); } return message; } catch (error) { throw new QSocketProtocolDecodeError(error.message); } } function decodePayloadData(buffer, contentType, offset, length) { switch (contentType) { case 0 /* UNDEFINED */: return void 0; // No data for undefined case 1 /* NULL */: return null; // No data for null case 2 /* BOOLEAN */: return buffer[offset] !== 0; // Decode boolean value case 3 /* NUMBER */: return readDoubleBE(buffer, offset); // Decode 64-bit float case 4 /* STRING */: return decodeString(buffer.slice(offset, offset + length)); // Decode string case 5 /* JSON */: return JSON.parse(decodeString(buffer.slice(offset, offset + length))); // Decode JSON string case 6 /* BUFFER */: return buffer.slice(offset, offset + length); // Return a slice of the buffer default: throw new Error("Unknown content type"); } } export { EQSocketProtocolContentType, EQSocketProtocolMessageType, QSocketProtocolDecodeError, QSocketProtocolEncodeError, from, to }; //# sourceMappingURL=index.js.map