UNPKG

@litert/televoke

Version:
292 lines 9.79 kB
"use strict"; /** * Copyright 2025 Angus.Fenying <fenying@litert.org> * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ Object.defineProperty(exports, "__esModule", { value: true }); exports.TvDecoderV2 = void 0; const CSv2 = require("./Constants.v2"); const CS = require("../../Constants"); const Errors_1 = require("../../Errors"); const B8 = Buffer.allocUnsafe(8); const B4 = Buffer.allocUnsafe(4); const B2 = Buffer.allocUnsafe(2); function readSmallBytes(bytes, buf, ctx) { let readBytes = 0; while (readBytes < bytes) { if (ctx.chIndex === ctx.chunks.length) { throw new Errors_1.errors.incomplete_packet(); } const c = ctx.chunks[ctx.chIndex]; const cLen = c.byteLength; let cOff = ctx.chOffset; do { buf[readBytes++] = c[cOff++]; } while (cOff < cLen && readBytes < bytes); if (cOff === cLen) { ctx.chIndex++; ctx.chOffset = 0; } else { ctx.chOffset = cOff; } if (readBytes === bytes) { break; } } return buf; } function readLargeBytes(bytes, ctx) { let readBytes = 0; const ret = []; while (readBytes < bytes) { if (ctx.chIndex === ctx.chunks.length) { throw new Errors_1.errors.incomplete_packet(); } const c = ctx.chunks[ctx.chIndex]; const bytes2Read = Math.min(bytes - readBytes, c.byteLength - ctx.chOffset); ret.push(c.subarray(ctx.chOffset, ctx.chOffset + bytes2Read)); readBytes += bytes2Read; ctx.chOffset += bytes2Read; if (ctx.chOffset === c.byteLength) { ctx.chIndex++; ctx.chOffset = 0; } if (readBytes === bytes) { break; } } return ret; } function readString(bytes, ctx) { const td = new TextDecoder('utf8'); let readBytes = 0; let ret = ''; while (readBytes < bytes) { if (ctx.chIndex === ctx.chunks.length) { throw new Errors_1.errors.incomplete_packet(); } const c = ctx.chunks[ctx.chIndex]; const bytes2Read = Math.min(bytes - readBytes, c.byteLength - ctx.chOffset); ret += td.decode(c.subarray(ctx.chOffset, ctx.chOffset + bytes2Read), { stream: true }); readBytes += bytes2Read; ctx.chOffset += bytes2Read; if (ctx.chOffset === c.byteLength) { ctx.chIndex++; ctx.chOffset = 0; } if (readBytes === bytes) { break; } } return ret; } function readVarString(ctx) { const len = readSmallBytes(2, B2, ctx).readUInt16LE(0); return readString(len, ctx); } function readVarBuffer(ctx) { const len = readSmallBytes(4, B4, ctx).readUInt32LE(0); return readLargeBytes(len, ctx); } function readVarBuffer16(ctx) { const len = readSmallBytes(2, B4, ctx).readUInt16LE(0); return readLargeBytes(len, ctx); } class TvErrorResponseDecoder { decode(ctx) { const len = readSmallBytes(2, B2, ctx).readUInt16LE(0); const errorMsg = readString(len, ctx); let err; if (errorMsg.startsWith(CS.PROTOCOL_ERROR_NAMESPACE)) { const errMsg = errorMsg.slice(CS.PROTOCOL_ERROR_NAMESPACE.length + 1); if (errMsg in Errors_1.errors) { err = new Errors_1.errors[errMsg](); } else { err = new Errors_1.ProtocolError(errMsg, null, null); } } else { err = new Errors_1.errors.app_error(errorMsg.slice(CS.APP_ERROR_NAMESPACE.length + 1), null); } return { 'cmd': ctx.command, 'typ': CSv2.EPacketType.ERROR_RESPONSE, 'seq': ctx.seq, 'ct': err, }; } } class TvApiRequestDecoder { decode(ctx) { const name = readVarString(ctx); return { 'cmd': CSv2.ECommand.API_CALL, 'typ': CSv2.EPacketType.REQUEST, 'seq': ctx.seq, 'ct': { 'name': name, 'body': readVarBuffer(ctx), } }; } } class TvPingRequestDecoder { decode(ctx) { return { 'cmd': CSv2.ECommand.PING, 'typ': CSv2.EPacketType.REQUEST, 'seq': ctx.seq, 'ct': readVarBuffer16(ctx) }; } } class TvPushMessageRequestDecoder { decode(ctx) { return { 'cmd': CSv2.ECommand.PUSH_MESSAGE, 'typ': CSv2.EPacketType.REQUEST, 'seq': ctx.seq, 'ct': readVarBuffer(ctx) }; } } class TvBinaryChunkRequestDecoder { decode(ctx) { const streamId = readSmallBytes(4, B4, ctx).readUInt32LE(0); const chunkIndex = readSmallBytes(4, B4, ctx).readUInt32LE(0); return { 'cmd': CSv2.ECommand.BINARY_CHUNK, 'typ': CSv2.EPacketType.REQUEST, 'seq': ctx.seq, 'ct': { 'streamId': streamId, 'index': chunkIndex, 'body': readVarBuffer(ctx), } }; } } class TvCloseRequestDecoder { decode(ctx) { return { 'cmd': CSv2.ECommand.CLOSE, 'typ': CSv2.EPacketType.REQUEST, 'seq': ctx.seq, 'ct': null }; } } class TvVoidResponseDecoder { decode(ctx) { return { 'cmd': ctx.command, 'typ': CSv2.EPacketType.SUCCESS_RESPONSE, 'seq': ctx.seq, 'ct': null }; } } class TvApiResponseDecoder { decode(ctx) { return { 'cmd': CSv2.ECommand.API_CALL, 'typ': CSv2.EPacketType.SUCCESS_RESPONSE, 'seq': ctx.seq, 'ct': readVarBuffer(ctx) }; } } class TvPingResponseDecoder { decode(ctx) { return { 'cmd': CSv2.ECommand.PING, 'typ': CSv2.EPacketType.SUCCESS_RESPONSE, 'seq': ctx.seq, 'ct': readVarBuffer16(ctx) }; } } const packetDecoders = { [CSv2.EPacketType.ERROR_RESPONSE + 0x100 * CSv2.ECommand.API_CALL]: new TvErrorResponseDecoder(), [CSv2.EPacketType.ERROR_RESPONSE + 0x100 * CSv2.ECommand.PING]: new TvErrorResponseDecoder(), [CSv2.EPacketType.ERROR_RESPONSE + 0x100 * CSv2.ECommand.BINARY_CHUNK]: new TvErrorResponseDecoder(), [CSv2.EPacketType.ERROR_RESPONSE + 0x100 * CSv2.ECommand.CLOSE]: new TvErrorResponseDecoder(), [CSv2.EPacketType.ERROR_RESPONSE + 0x100 * CSv2.ECommand.PUSH_MESSAGE]: new TvErrorResponseDecoder(), [CSv2.EPacketType.REQUEST + 0x100 * CSv2.ECommand.API_CALL]: new TvApiRequestDecoder(), [CSv2.EPacketType.REQUEST + 0x100 * CSv2.ECommand.PING]: new TvPingRequestDecoder(), [CSv2.EPacketType.REQUEST + 0x100 * CSv2.ECommand.PUSH_MESSAGE]: new TvPushMessageRequestDecoder(), [CSv2.EPacketType.REQUEST + 0x100 * CSv2.ECommand.CLOSE]: new TvCloseRequestDecoder(), [CSv2.EPacketType.REQUEST + 0x100 * CSv2.ECommand.BINARY_CHUNK]: new TvBinaryChunkRequestDecoder(), [CSv2.EPacketType.SUCCESS_RESPONSE + 0x100 * CSv2.ECommand.PUSH_MESSAGE]: new TvVoidResponseDecoder(), [CSv2.EPacketType.SUCCESS_RESPONSE + 0x100 * CSv2.ECommand.CLOSE]: new TvVoidResponseDecoder(), [CSv2.EPacketType.SUCCESS_RESPONSE + 0x100 * CSv2.ECommand.BINARY_CHUNK]: new TvVoidResponseDecoder(), [CSv2.EPacketType.SUCCESS_RESPONSE + 0x100 * CSv2.ECommand.API_CALL]: new TvApiResponseDecoder(), [CSv2.EPacketType.SUCCESS_RESPONSE + 0x100 * CSv2.ECommand.PING]: new TvPingResponseDecoder(), }; class TvDecoderV2 { /** * Pass all chunks of a whole packet to this method, and it will return an array of decoded results. * * @throws {TelevokeError} */ decode(packetChunks) { const ctx = this._decodeHeader(packetChunks); try { return packetDecoders[ctx.command * 0x100 + ctx.type].decode(ctx); } catch (e) { if (e instanceof Errors_1.TelevokeError) { throw e; } throw new Errors_1.errors.invalid_packet(null, e); } } _decodeHeader(chunks) { try { const ctx = { chunks, chIndex: 0, chOffset: 0, command: 0, type: 0, seq: 0, }; readSmallBytes(8, B8, ctx); ctx.command = B8[0]; ctx.type = B8[1]; if (undefined === CSv2.EPacketType[ctx.type]) { throw new Errors_1.errors.invalid_packet({ unknownPacketType: ctx.type, }); } if (undefined === CSv2.ECommand[ctx.command]) { throw new Errors_1.errors.invalid_packet({ unknownCommand: ctx.command, }); } ctx.seq = B8.readUInt16LE(2) * 4294967296 + B8.readUInt32LE(4); return ctx; } catch (e) { if (e instanceof Errors_1.TelevokeError) { throw e; } throw new Errors_1.errors.invalid_packet(null, e); } } } exports.TvDecoderV2 = TvDecoderV2; //# sourceMappingURL=DecoderV2.js.map