@eventmsg/core
Version:
EventMsgV3 TypeScript library - Core protocol implementation with transport abstraction
1 lines • 24.3 kB
Source Map (JSON)
{"version":3,"file":"protocol.cjs","names":["DEFAULT_PROTOCOL_OPTIONS","getLogger","stuff","MessageSizeError","CONTROL_CHARS","hexDump","EncodingError","InvalidMessageError","unstuff","DecodingError"],"sources":["../src/protocol.ts"],"sourcesContent":["import {\n DecodingError,\n EncodingError,\n InvalidMessageError,\n} from \"./errors/protocol-error.js\";\nimport { MessageSizeError } from \"./errors/validation-error.js\";\nimport { CONTROL_CHARS, stuff, unstuff } from \"./internal/byte-stuffing.js\";\nimport { getLogger, hexDump } from \"./internal/logger.js\";\nimport type { EventMsgConfig, ProtocolOptions } from \"./types/config.js\";\nimport { DEFAULT_PROTOCOL_OPTIONS } from \"./types/config.js\";\nimport type { MessageHeader } from \"./types/message.js\";\n\n/**\n * Decoded message structure\n */\nexport interface DecodedMessage {\n header: MessageHeader;\n eventName: string;\n eventData: string;\n}\n\n/**\n * EventMsgV3 Protocol Implementation\n *\n * Handles encoding and decoding of EventMsgV3 binary protocol messages.\n * Message format: [SOH][StuffedHeader][STX][StuffedEventName][US][StuffedEventData][EOT]\n */\nexport class Protocol {\n private readonly options: Required<ProtocolOptions>;\n private readonly encoding: \"utf8\" | \"binary\";\n private readonly logger: ReturnType<typeof getLogger>;\n\n constructor(config: EventMsgConfig) {\n this.options = { ...DEFAULT_PROTOCOL_OPTIONS, ...config.protocol };\n this.encoding = config.encoding ?? \"utf8\";\n this.logger = getLogger(\"PROTOCOL\");\n }\n\n /**\n * Encode a message into EventMsgV3 binary format\n *\n * @param header Message header (7 bytes)\n * @param eventName Event name string\n * @param eventData Event data string (JSON)\n * @returns Encoded binary message\n * @throws {EncodingError} If encoding fails\n * @throws {MessageSizeError} If message exceeds size limits\n */\n encode(\n header: MessageHeader,\n eventName: string,\n eventData: string\n ): Uint8Array {\n this.logger.debug(\"Starting message encode\", {\n eventName,\n eventDataLength: eventData.length,\n header,\n encoding: this.encoding,\n });\n\n try {\n // Validate inputs\n this.validateHeader(header);\n this.validateEventName(eventName);\n this.validateEventData(eventData);\n\n // Convert strings to bytes\n const eventNameBytes = this.stringToBytes(eventName);\n const eventDataBytes = this.stringToBytes(eventData);\n\n // Serialize header (7 bytes, big-endian messageId)\n const headerBytes = this.serializeHeader(header);\n\n // Apply byte stuffing to each section\n const stuffedHeader = stuff(headerBytes);\n const stuffedEventName = stuff(eventNameBytes);\n const stuffedEventData = stuff(eventDataBytes);\n\n this.logger.trace(\"Byte stuffing complete\", {\n headerSize: `${headerBytes.length} -> ${stuffedHeader.length}`,\n nameSize: `${eventNameBytes.length} -> ${stuffedEventName.length}`,\n dataSize: `${eventDataBytes.length} -> ${stuffedEventData.length}`,\n });\n\n // Calculate total message size\n const totalSize =\n 1 +\n stuffedHeader.length +\n 1 +\n stuffedEventName.length +\n 1 +\n stuffedEventData.length +\n 1;\n\n if (totalSize > this.options.maxEventDataLength + 1000) {\n // Allow some overhead\n throw new MessageSizeError(\n totalSize,\n this.options.maxEventDataLength + 1000,\n \"encoded message\"\n );\n }\n\n // Build final message: [SOH][StuffedHeader][STX][StuffedEventName][US][StuffedEventData][EOT]\n const message = new Uint8Array(totalSize);\n let offset = 0;\n\n // SOH\n message[offset++] = CONTROL_CHARS.SOH;\n\n // Stuffed Header\n message.set(stuffedHeader, offset);\n offset += stuffedHeader.length;\n\n // STX\n message[offset++] = CONTROL_CHARS.STX;\n\n // Stuffed Event Name\n message.set(stuffedEventName, offset);\n offset += stuffedEventName.length;\n\n // US\n message[offset++] = CONTROL_CHARS.US;\n\n // Stuffed Event Data\n message.set(stuffedEventData, offset);\n offset += stuffedEventData.length;\n\n // EOT\n message[offset++] = CONTROL_CHARS.EOT;\n\n const finalMessage = message.subarray(0, offset);\n\n this.logger.info(\"Message encoded successfully\", {\n eventName,\n finalSize: finalMessage.length,\n messageId: header.messageId,\n });\n\n this.logger.trace(\"Encoded message hex dump\", {\n hex: hexDump(finalMessage),\n });\n\n return finalMessage;\n } catch (error) {\n this.logger.error(\"Message encoding failed\", {\n error: error instanceof Error ? error.message : String(error),\n eventName,\n eventDataLength: eventData.length,\n cause: error instanceof Error ? error.name : \"Unknown\",\n });\n\n if (error instanceof MessageSizeError) {\n throw error;\n }\n throw new EncodingError(\"Failed to encode EventMsgV3 message\", {\n context: { eventName, eventDataLength: eventData.length },\n cause: error instanceof Error ? error : new Error(String(error)),\n });\n }\n }\n\n /**\n * Encode a message with binary data into EventMsgV3 format\n *\n * @param header Message header (7 bytes)\n * @param eventName Event name string\n * @param eventData Binary event data\n * @returns Encoded binary message\n * @throws {EncodingError} If encoding fails\n * @throws {MessageSizeError} If message exceeds size limits\n */\n encodeBinary(\n header: MessageHeader,\n eventName: string,\n eventData: Uint8Array\n ): Uint8Array {\n this.logger.debug(\"Starting binary message encode\", {\n eventName,\n eventDataLength: eventData.length,\n header,\n });\n\n try {\n this.validateHeader(header);\n this.validateEventName(eventName);\n\n if (eventData.length > this.options.maxEventDataLength) {\n throw new MessageSizeError(\n eventData.length,\n this.options.maxEventDataLength,\n \"eventData\"\n );\n }\n\n const eventNameBytes = this.stringToBytes(eventName);\n const headerBytes = this.serializeHeader(header);\n\n const stuffedHeader = stuff(headerBytes);\n const stuffedEventName = stuff(eventNameBytes);\n const stuffedEventData = stuff(eventData);\n\n this.logger.trace(\"Binary byte stuffing complete\", {\n headerSize: `${headerBytes.length} -> ${stuffedHeader.length}`,\n nameSize: `${eventNameBytes.length} -> ${stuffedEventName.length}`,\n dataSize: `${eventData.length} -> ${stuffedEventData.length}`,\n });\n\n const totalSize =\n 1 +\n stuffedHeader.length +\n 1 +\n stuffedEventName.length +\n 1 +\n stuffedEventData.length +\n 1;\n\n if (totalSize > this.options.maxEventDataLength + 1000) {\n throw new MessageSizeError(\n totalSize,\n this.options.maxEventDataLength + 1000,\n \"encoded message\"\n );\n }\n\n const message = new Uint8Array(totalSize);\n let offset = 0;\n\n message[offset++] = CONTROL_CHARS.SOH;\n message.set(stuffedHeader, offset);\n offset += stuffedHeader.length;\n\n message[offset++] = CONTROL_CHARS.STX;\n message.set(stuffedEventName, offset);\n offset += stuffedEventName.length;\n\n message[offset++] = CONTROL_CHARS.US;\n message.set(stuffedEventData, offset);\n offset += stuffedEventData.length;\n\n message[offset++] = CONTROL_CHARS.EOT;\n\n const finalMessage = message.subarray(0, offset);\n\n this.logger.info(\"Binary message encoded successfully\", {\n eventName,\n finalSize: finalMessage.length,\n messageId: header.messageId,\n });\n\n this.logger.trace(\"Encoded binary message hex dump\", {\n hex: hexDump(finalMessage),\n });\n\n return finalMessage;\n } catch (error) {\n this.logger.error(\"Binary message encoding failed\", {\n error: error instanceof Error ? error.message : String(error),\n eventName,\n eventDataLength: eventData.length,\n cause: error instanceof Error ? error.name : \"Unknown\",\n });\n\n if (error instanceof MessageSizeError) {\n throw error;\n }\n throw new EncodingError(\"Failed to encode EventMsgV3 binary message\", {\n context: {\n eventName,\n eventDataLength: eventData.length,\n },\n cause: error instanceof Error ? error : new Error(String(error)),\n });\n }\n }\n\n /**\n * Decode a binary message into EventMsgV3 components\n *\n * @param data Binary message data\n * @returns Decoded message components\n * @throws {DecodingError} If decoding fails\n * @throws {InvalidMessageError} If message format is invalid\n */\n decode(data: Uint8Array): DecodedMessage {\n this.logger.debug(\"Starting message decode\", {\n messageLength: data.length,\n encoding: this.encoding,\n });\n\n this.logger.trace(\"Incoming message hex dump\", {\n hex: hexDump(data),\n });\n\n try {\n // Validate minimum message length (SOH + header + STX + name + US + data + EOT)\n if (data.length < 10) {\n throw new InvalidMessageError(\n `Message too short: ${data.length} bytes (minimum 10)`,\n { context: { messageLength: data.length } }\n );\n }\n\n // Validate framing characters\n if (data[0] !== CONTROL_CHARS.SOH) {\n throw new InvalidMessageError(\n `Message must start with SOH (0x01), got 0x${data[0]?.toString(16).padStart(2, \"0\")}`,\n { context: { firstByte: data[0] } }\n );\n }\n\n if (data.at(-1) !== CONTROL_CHARS.EOT) {\n throw new InvalidMessageError(\n `Message must end with EOT (0x04), got 0x${data.at(-1)?.toString(16).padStart(2, \"0\")}`,\n { context: { lastByte: data.at(-1) } }\n );\n }\n\n // Find section boundaries\n const stxIndex = this.findControlChar(data, CONTROL_CHARS.STX, 1);\n const usIndex = this.findControlChar(\n data,\n CONTROL_CHARS.US,\n stxIndex + 1\n );\n\n if (stxIndex === -1) {\n throw new InvalidMessageError(\"STX (0x02) not found in message\");\n }\n if (usIndex === -1) {\n throw new InvalidMessageError(\"US (0x1F) not found in message\");\n }\n\n // Extract and unstuff sections\n const stuffedHeader = data.subarray(1, stxIndex);\n const stuffedEventName = data.subarray(stxIndex + 1, usIndex);\n const stuffedEventData = data.subarray(usIndex + 1, data.length - 1);\n\n const headerBytes = unstuff(stuffedHeader);\n const eventNameBytes = unstuff(stuffedEventName);\n const eventDataBytes = unstuff(stuffedEventData);\n\n this.logger.trace(\"Byte unstuffing complete\", {\n headerSize: `${stuffedHeader.length} -> ${headerBytes.length}`,\n nameSize: `${stuffedEventName.length} -> ${eventNameBytes.length}`,\n dataSize: `${stuffedEventData.length} -> ${eventDataBytes.length}`,\n });\n\n // Parse components\n const header = this.deserializeHeader(headerBytes);\n const eventName = this.bytesToString(eventNameBytes);\n const eventData = this.bytesToString(eventDataBytes);\n\n // Validate decoded components\n if (this.options.strictValidation) {\n this.validateHeader(header);\n this.validateEventName(eventName);\n this.validateEventData(eventData);\n }\n\n this.logger.info(\"Message decoded successfully\", {\n eventName,\n messageId: header.messageId,\n senderId: header.senderId,\n receiverId: header.receiverId,\n eventDataLength: eventData.length,\n });\n\n return { header, eventName, eventData };\n } catch (error) {\n this.logger.error(\"Message decoding failed\", {\n error: error instanceof Error ? error.message : String(error),\n messageLength: data.length,\n\n cause: error instanceof Error ? error.name : \"Unknown\",\n hex: hexDump(data, 64),\n });\n\n if (error instanceof InvalidMessageError) {\n throw error;\n }\n throw new DecodingError(\"Failed to decode EventMsgV3 message\", {\n context: { messageLength: data.length },\n cause: error instanceof Error ? error : new Error(String(error)),\n });\n }\n }\n\n /**\n * Find a control character in data, skipping escaped instances\n */\n private findControlChar(\n data: Uint8Array,\n target: number,\n startIndex: number\n ): number {\n let escaped = false;\n\n for (let i = startIndex; i < data.length; i++) {\n const byte = data[i];\n if (byte === undefined) {\n continue;\n }\n\n if (escaped) {\n escaped = false;\n } else if (byte === CONTROL_CHARS.ESC) {\n escaped = true;\n } else if (byte === target) {\n return i;\n }\n }\n\n return -1;\n }\n\n /**\n * Serialize message header to 7 bytes (big-endian messageId)\n */\n private serializeHeader(header: MessageHeader): Uint8Array {\n const bytes = new Uint8Array(7);\n bytes[0] = header.senderId;\n bytes[1] = header.receiverId;\n bytes[2] = header.senderGroupId;\n bytes[3] = header.receiverGroupId;\n bytes[4] = header.flags;\n bytes[5] = (header.messageId >> 8) & 0xff; // MSB\n bytes[6] = header.messageId & 0xff; // LSB\n return bytes;\n }\n\n /**\n * Deserialize 7 bytes to message header (big-endian messageId)\n */\n private deserializeHeader(bytes: Uint8Array): MessageHeader {\n if (bytes.length !== 7) {\n throw new InvalidMessageError(\n `Header must be exactly 7 bytes, got ${bytes.length}`,\n { context: { headerLength: bytes.length } }\n );\n }\n\n if (\n bytes[0] === undefined ||\n bytes[1] === undefined ||\n bytes[2] === undefined ||\n bytes[3] === undefined ||\n bytes[4] === undefined ||\n bytes[5] === undefined ||\n bytes[6] === undefined\n ) {\n throw new InvalidMessageError(\n `Header must be exactly 7 bytes, got ${bytes.length}`,\n { context: { headerLength: bytes.length } }\n );\n }\n\n return {\n senderId: bytes[0],\n receiverId: bytes[1],\n senderGroupId: bytes[2],\n receiverGroupId: bytes[3],\n flags: bytes[4],\n messageId: (bytes[5] << 8) | bytes[6], // Big-endian\n };\n }\n\n /**\n * Convert string to bytes using configured encoding\n */\n private stringToBytes(str: string): Uint8Array {\n if (this.encoding === \"utf8\") {\n return new TextEncoder().encode(str);\n }\n // Binary encoding - each character is a byte\n const bytes = new Uint8Array(str.length);\n for (let i = 0; i < str.length; i++) {\n bytes[i] = str.charCodeAt(i) & 0xff;\n }\n return bytes;\n }\n\n /**\n * Convert bytes to string using configured encoding\n */\n private bytesToString(bytes: Uint8Array): string {\n if (this.encoding === \"utf8\") {\n return new TextDecoder(\"utf8\").decode(bytes);\n }\n // Binary encoding - each byte is a character\n return String.fromCharCode(...bytes);\n }\n\n /**\n * Validate message header fields\n */\n private validateHeader(header: MessageHeader): void {\n const fields = [\n { name: \"senderId\", value: header.senderId },\n { name: \"receiverId\", value: header.receiverId },\n { name: \"senderGroupId\", value: header.senderGroupId },\n { name: \"receiverGroupId\", value: header.receiverGroupId },\n { name: \"flags\", value: header.flags },\n ];\n\n for (const field of fields) {\n if (\n field.value < 0 ||\n field.value > 255 ||\n !Number.isInteger(field.value)\n ) {\n throw new MessageSizeError(field.value, 255, field.name, {\n context: { validRange: \"0-255\" },\n });\n }\n }\n\n if (\n header.messageId < 0 ||\n header.messageId > 65_535 ||\n !Number.isInteger(header.messageId)\n ) {\n throw new MessageSizeError(header.messageId, 65_535, \"messageId\", {\n context: { validRange: \"0-65535\" },\n });\n }\n }\n\n /**\n * Validate event name length\n */\n private validateEventName(eventName: string): void {\n const bytes = this.stringToBytes(eventName);\n if (bytes.length > this.options.maxEventNameLength) {\n throw new MessageSizeError(\n bytes.length,\n this.options.maxEventNameLength,\n \"eventName\"\n );\n }\n }\n\n /**\n * Validate event data length\n */\n private validateEventData(eventData: string): void {\n const bytes = this.stringToBytes(eventData);\n if (bytes.length > this.options.maxEventDataLength) {\n throw new MessageSizeError(\n bytes.length,\n this.options.maxEventDataLength,\n \"eventData\"\n );\n }\n }\n}\n"],"mappings":";;;;;;;;;;;;;AA2BA,IAAa,WAAb,MAAsB;CACpB,AAAiB;CACjB,AAAiB;CACjB,AAAiB;CAEjB,YAAY,QAAwB;AAClC,OAAK,UAAU;GAAE,GAAGA;GAA0B,GAAG,OAAO;GAAU;AAClE,OAAK,WAAW,OAAO,YAAY;AACnC,OAAK,SAASC,yBAAU,WAAW;;;;;;;;;;;;CAarC,OACE,QACA,WACA,WACY;AACZ,OAAK,OAAO,MAAM,2BAA2B;GAC3C;GACA,iBAAiB,UAAU;GAC3B;GACA,UAAU,KAAK;GAChB,CAAC;AAEF,MAAI;AAEF,QAAK,eAAe,OAAO;AAC3B,QAAK,kBAAkB,UAAU;AACjC,QAAK,kBAAkB,UAAU;GAGjC,MAAM,iBAAiB,KAAK,cAAc,UAAU;GACpD,MAAM,iBAAiB,KAAK,cAAc,UAAU;GAGpD,MAAM,cAAc,KAAK,gBAAgB,OAAO;GAGhD,MAAM,gBAAgBC,4BAAM,YAAY;GACxC,MAAM,mBAAmBA,4BAAM,eAAe;GAC9C,MAAM,mBAAmBA,4BAAM,eAAe;AAE9C,QAAK,OAAO,MAAM,0BAA0B;IAC1C,YAAY,GAAG,YAAY,OAAO,MAAM,cAAc;IACtD,UAAU,GAAG,eAAe,OAAO,MAAM,iBAAiB;IAC1D,UAAU,GAAG,eAAe,OAAO,MAAM,iBAAiB;IAC3D,CAAC;GAGF,MAAM,YACJ,IACA,cAAc,SACd,IACA,iBAAiB,SACjB,IACA,iBAAiB,SACjB;AAEF,OAAI,YAAY,KAAK,QAAQ,qBAAqB,IAEhD,OAAM,IAAIC,0CACR,WACA,KAAK,QAAQ,qBAAqB,KAClC,kBACD;GAIH,MAAM,UAAU,IAAI,WAAW,UAAU;GACzC,IAAI,SAAS;AAGb,WAAQ,YAAYC,oCAAc;AAGlC,WAAQ,IAAI,eAAe,OAAO;AAClC,aAAU,cAAc;AAGxB,WAAQ,YAAYA,oCAAc;AAGlC,WAAQ,IAAI,kBAAkB,OAAO;AACrC,aAAU,iBAAiB;AAG3B,WAAQ,YAAYA,oCAAc;AAGlC,WAAQ,IAAI,kBAAkB,OAAO;AACrC,aAAU,iBAAiB;AAG3B,WAAQ,YAAYA,oCAAc;GAElC,MAAM,eAAe,QAAQ,SAAS,GAAG,OAAO;AAEhD,QAAK,OAAO,KAAK,gCAAgC;IAC/C;IACA,WAAW,aAAa;IACxB,WAAW,OAAO;IACnB,CAAC;AAEF,QAAK,OAAO,MAAM,4BAA4B,EAC5C,KAAKC,uBAAQ,aAAa,EAC3B,CAAC;AAEF,UAAO;WACA,OAAO;AACd,QAAK,OAAO,MAAM,2BAA2B;IAC3C,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM;IAC7D;IACA,iBAAiB,UAAU;IAC3B,OAAO,iBAAiB,QAAQ,MAAM,OAAO;IAC9C,CAAC;AAEF,OAAI,iBAAiBF,0CACnB,OAAM;AAER,SAAM,IAAIG,qCAAc,uCAAuC;IAC7D,SAAS;KAAE;KAAW,iBAAiB,UAAU;KAAQ;IACzD,OAAO,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,MAAM,CAAC;IACjE,CAAC;;;;;;;;;;;;;CAcN,aACE,QACA,WACA,WACY;AACZ,OAAK,OAAO,MAAM,kCAAkC;GAClD;GACA,iBAAiB,UAAU;GAC3B;GACD,CAAC;AAEF,MAAI;AACF,QAAK,eAAe,OAAO;AAC3B,QAAK,kBAAkB,UAAU;AAEjC,OAAI,UAAU,SAAS,KAAK,QAAQ,mBAClC,OAAM,IAAIH,0CACR,UAAU,QACV,KAAK,QAAQ,oBACb,YACD;GAGH,MAAM,iBAAiB,KAAK,cAAc,UAAU;GACpD,MAAM,cAAc,KAAK,gBAAgB,OAAO;GAEhD,MAAM,gBAAgBD,4BAAM,YAAY;GACxC,MAAM,mBAAmBA,4BAAM,eAAe;GAC9C,MAAM,mBAAmBA,4BAAM,UAAU;AAEzC,QAAK,OAAO,MAAM,iCAAiC;IACjD,YAAY,GAAG,YAAY,OAAO,MAAM,cAAc;IACtD,UAAU,GAAG,eAAe,OAAO,MAAM,iBAAiB;IAC1D,UAAU,GAAG,UAAU,OAAO,MAAM,iBAAiB;IACtD,CAAC;GAEF,MAAM,YACJ,IACA,cAAc,SACd,IACA,iBAAiB,SACjB,IACA,iBAAiB,SACjB;AAEF,OAAI,YAAY,KAAK,QAAQ,qBAAqB,IAChD,OAAM,IAAIC,0CACR,WACA,KAAK,QAAQ,qBAAqB,KAClC,kBACD;GAGH,MAAM,UAAU,IAAI,WAAW,UAAU;GACzC,IAAI,SAAS;AAEb,WAAQ,YAAYC,oCAAc;AAClC,WAAQ,IAAI,eAAe,OAAO;AAClC,aAAU,cAAc;AAExB,WAAQ,YAAYA,oCAAc;AAClC,WAAQ,IAAI,kBAAkB,OAAO;AACrC,aAAU,iBAAiB;AAE3B,WAAQ,YAAYA,oCAAc;AAClC,WAAQ,IAAI,kBAAkB,OAAO;AACrC,aAAU,iBAAiB;AAE3B,WAAQ,YAAYA,oCAAc;GAElC,MAAM,eAAe,QAAQ,SAAS,GAAG,OAAO;AAEhD,QAAK,OAAO,KAAK,uCAAuC;IACtD;IACA,WAAW,aAAa;IACxB,WAAW,OAAO;IACnB,CAAC;AAEF,QAAK,OAAO,MAAM,mCAAmC,EACnD,KAAKC,uBAAQ,aAAa,EAC3B,CAAC;AAEF,UAAO;WACA,OAAO;AACd,QAAK,OAAO,MAAM,kCAAkC;IAClD,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM;IAC7D;IACA,iBAAiB,UAAU;IAC3B,OAAO,iBAAiB,QAAQ,MAAM,OAAO;IAC9C,CAAC;AAEF,OAAI,iBAAiBF,0CACnB,OAAM;AAER,SAAM,IAAIG,qCAAc,8CAA8C;IACpE,SAAS;KACP;KACA,iBAAiB,UAAU;KAC5B;IACD,OAAO,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,MAAM,CAAC;IACjE,CAAC;;;;;;;;;;;CAYN,OAAO,MAAkC;AACvC,OAAK,OAAO,MAAM,2BAA2B;GAC3C,eAAe,KAAK;GACpB,UAAU,KAAK;GAChB,CAAC;AAEF,OAAK,OAAO,MAAM,6BAA6B,EAC7C,KAAKD,uBAAQ,KAAK,EACnB,CAAC;AAEF,MAAI;AAEF,OAAI,KAAK,SAAS,GAChB,OAAM,IAAIE,2CACR,sBAAsB,KAAK,OAAO,sBAClC,EAAE,SAAS,EAAE,eAAe,KAAK,QAAQ,EAAE,CAC5C;AAIH,OAAI,KAAK,OAAOH,oCAAc,IAC5B,OAAM,IAAIG,2CACR,6CAA6C,KAAK,IAAI,SAAS,GAAG,CAAC,SAAS,GAAG,IAAI,IACnF,EAAE,SAAS,EAAE,WAAW,KAAK,IAAI,EAAE,CACpC;AAGH,OAAI,KAAK,GAAG,GAAG,KAAKH,oCAAc,IAChC,OAAM,IAAIG,2CACR,2CAA2C,KAAK,GAAG,GAAG,EAAE,SAAS,GAAG,CAAC,SAAS,GAAG,IAAI,IACrF,EAAE,SAAS,EAAE,UAAU,KAAK,GAAG,GAAG,EAAE,EAAE,CACvC;GAIH,MAAM,WAAW,KAAK,gBAAgB,MAAMH,oCAAc,KAAK,EAAE;GACjE,MAAM,UAAU,KAAK,gBACnB,MACAA,oCAAc,IACd,WAAW,EACZ;AAED,OAAI,aAAa,GACf,OAAM,IAAIG,2CAAoB,kCAAkC;AAElE,OAAI,YAAY,GACd,OAAM,IAAIA,2CAAoB,iCAAiC;GAIjE,MAAM,gBAAgB,KAAK,SAAS,GAAG,SAAS;GAChD,MAAM,mBAAmB,KAAK,SAAS,WAAW,GAAG,QAAQ;GAC7D,MAAM,mBAAmB,KAAK,SAAS,UAAU,GAAG,KAAK,SAAS,EAAE;GAEpE,MAAM,cAAcC,8BAAQ,cAAc;GAC1C,MAAM,iBAAiBA,8BAAQ,iBAAiB;GAChD,MAAM,iBAAiBA,8BAAQ,iBAAiB;AAEhD,QAAK,OAAO,MAAM,4BAA4B;IAC5C,YAAY,GAAG,cAAc,OAAO,MAAM,YAAY;IACtD,UAAU,GAAG,iBAAiB,OAAO,MAAM,eAAe;IAC1D,UAAU,GAAG,iBAAiB,OAAO,MAAM,eAAe;IAC3D,CAAC;GAGF,MAAM,SAAS,KAAK,kBAAkB,YAAY;GAClD,MAAM,YAAY,KAAK,cAAc,eAAe;GACpD,MAAM,YAAY,KAAK,cAAc,eAAe;AAGpD,OAAI,KAAK,QAAQ,kBAAkB;AACjC,SAAK,eAAe,OAAO;AAC3B,SAAK,kBAAkB,UAAU;AACjC,SAAK,kBAAkB,UAAU;;AAGnC,QAAK,OAAO,KAAK,gCAAgC;IAC/C;IACA,WAAW,OAAO;IAClB,UAAU,OAAO;IACjB,YAAY,OAAO;IACnB,iBAAiB,UAAU;IAC5B,CAAC;AAEF,UAAO;IAAE;IAAQ;IAAW;IAAW;WAChC,OAAO;AACd,QAAK,OAAO,MAAM,2BAA2B;IAC3C,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM;IAC7D,eAAe,KAAK;IAEpB,OAAO,iBAAiB,QAAQ,MAAM,OAAO;IAC7C,KAAKH,uBAAQ,MAAM,GAAG;IACvB,CAAC;AAEF,OAAI,iBAAiBE,2CACnB,OAAM;AAER,SAAM,IAAIE,qCAAc,uCAAuC;IAC7D,SAAS,EAAE,eAAe,KAAK,QAAQ;IACvC,OAAO,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,MAAM,CAAC;IACjE,CAAC;;;;;;CAON,AAAQ,gBACN,MACA,QACA,YACQ;EACR,IAAI,UAAU;AAEd,OAAK,IAAI,IAAI,YAAY,IAAI,KAAK,QAAQ,KAAK;GAC7C,MAAM,OAAO,KAAK;AAClB,OAAI,SAAS,OACX;AAGF,OAAI,QACF,WAAU;YACD,SAASL,oCAAc,IAChC,WAAU;YACD,SAAS,OAClB,QAAO;;AAIX,SAAO;;;;;CAMT,AAAQ,gBAAgB,QAAmC;EACzD,MAAM,QAAQ,IAAI,WAAW,EAAE;AAC/B,QAAM,KAAK,OAAO;AAClB,QAAM,KAAK,OAAO;AAClB,QAAM,KAAK,OAAO;AAClB,QAAM,KAAK,OAAO;AAClB,QAAM,KAAK,OAAO;AAClB,QAAM,KAAM,OAAO,aAAa,IAAK;AACrC,QAAM,KAAK,OAAO,YAAY;AAC9B,SAAO;;;;;CAMT,AAAQ,kBAAkB,OAAkC;AAC1D,MAAI,MAAM,WAAW,EACnB,OAAM,IAAIG,2CACR,uCAAuC,MAAM,UAC7C,EAAE,SAAS,EAAE,cAAc,MAAM,QAAQ,EAAE,CAC5C;AAGH,MACE,MAAM,OAAO,UACb,MAAM,OAAO,UACb,MAAM,OAAO,UACb,MAAM,OAAO,UACb,MAAM,OAAO,UACb,MAAM,OAAO,UACb,MAAM,OAAO,OAEb,OAAM,IAAIA,2CACR,uCAAuC,MAAM,UAC7C,EAAE,SAAS,EAAE,cAAc,MAAM,QAAQ,EAAE,CAC5C;AAGH,SAAO;GACL,UAAU,MAAM;GAChB,YAAY,MAAM;GAClB,eAAe,MAAM;GACrB,iBAAiB,MAAM;GACvB,OAAO,MAAM;GACb,WAAY,MAAM,MAAM,IAAK,MAAM;GACpC;;;;;CAMH,AAAQ,cAAc,KAAyB;AAC7C,MAAI,KAAK,aAAa,OACpB,QAAO,IAAI,aAAa,CAAC,OAAO,IAAI;EAGtC,MAAM,QAAQ,IAAI,WAAW,IAAI,OAAO;AACxC,OAAK,IAAI,IAAI,GAAG,IAAI,IAAI,QAAQ,IAC9B,OAAM,KAAK,IAAI,WAAW,EAAE,GAAG;AAEjC,SAAO;;;;;CAMT,AAAQ,cAAc,OAA2B;AAC/C,MAAI,KAAK,aAAa,OACpB,QAAO,IAAI,YAAY,OAAO,CAAC,OAAO,MAAM;AAG9C,SAAO,OAAO,aAAa,GAAG,MAAM;;;;;CAMtC,AAAQ,eAAe,QAA6B;EAClD,MAAM,SAAS;GACb;IAAE,MAAM;IAAY,OAAO,OAAO;IAAU;GAC5C;IAAE,MAAM;IAAc,OAAO,OAAO;IAAY;GAChD;IAAE,MAAM;IAAiB,OAAO,OAAO;IAAe;GACtD;IAAE,MAAM;IAAmB,OAAO,OAAO;IAAiB;GAC1D;IAAE,MAAM;IAAS,OAAO,OAAO;IAAO;GACvC;AAED,OAAK,MAAM,SAAS,OAClB,KACE,MAAM,QAAQ,KACd,MAAM,QAAQ,OACd,CAAC,OAAO,UAAU,MAAM,MAAM,CAE9B,OAAM,IAAIJ,0CAAiB,MAAM,OAAO,KAAK,MAAM,MAAM,EACvD,SAAS,EAAE,YAAY,SAAS,EACjC,CAAC;AAIN,MACE,OAAO,YAAY,KACnB,OAAO,YAAY,SACnB,CAAC,OAAO,UAAU,OAAO,UAAU,CAEnC,OAAM,IAAIA,0CAAiB,OAAO,WAAW,OAAQ,aAAa,EAChE,SAAS,EAAE,YAAY,WAAW,EACnC,CAAC;;;;;CAON,AAAQ,kBAAkB,WAAyB;EACjD,MAAM,QAAQ,KAAK,cAAc,UAAU;AAC3C,MAAI,MAAM,SAAS,KAAK,QAAQ,mBAC9B,OAAM,IAAIA,0CACR,MAAM,QACN,KAAK,QAAQ,oBACb,YACD;;;;;CAOL,AAAQ,kBAAkB,WAAyB;EACjD,MAAM,QAAQ,KAAK,cAAc,UAAU;AAC3C,MAAI,MAAM,SAAS,KAAK,QAAQ,mBAC9B,OAAM,IAAIA,0CACR,MAAM,QACN,KAAK,QAAQ,oBACb,YACD"}