import type { CodecOpts, Dup, Payload, QoS, Topic } from "./types.ts"; import type { ProtocolLevelNoV5 } from "./protocolLevels.ts"; import type { PublishProperties } from "./Properties.ts"; import { PacketType } from "./PacketType.ts"; import { BitMask } from "./BitMask.ts"; import { Encoder, EncoderError } from "./encoder.ts"; import { booleanFlag, Decoder, DecoderError } from "./decoder.ts"; /** * PublishPacket is used to send data from client to server and * from server to subscribers */ export type PublishPacketV4 = { type: PacketType; protocolLevel: ProtocolLevelNoV5; topic: Topic; payload: Payload; dup?: Dup | undefined; retain?: boolean | undefined; qos?: QoS | undefined; id?: number | undefined; }; export type PublishPacketV5 = { type: PacketType; protocolLevel: 5; topic: Topic; payload: Payload; dup?: Dup | undefined; retain?: boolean | undefined; qos?: QoS | undefined; id?: number | undefined; properties?: PublishProperties | undefined; }; /** Publish packet (v4|v5). */ export type PublishPacket = PublishPacketV4 | PublishPacketV5; export function encode( packet: PublishPacket, codecOpts: CodecOpts, ): Uint8Array { const qos = packet.qos || 0; const flags = (packet.dup ? BitMask.bit3 : 0) + (qos & 2 ? BitMask.bit2 : 0) + (qos & 1 ? BitMask.bit1 : 0) + (packet.retain ? BitMask.bit0 : 0); const encoder = new Encoder(packet.type); encoder.setUtf8String(packet.topic); if (qos === 1 || qos === 2) { if (typeof packet.id !== "number" || packet.id < 1) { throw new EncoderError("when qos is 1 or 2, packet must have id"); } encoder.setInt16(packet.id); } if (packet.protocolLevel === 5) { encoder.setProperties( packet.properties || {}, PacketType.publish, codecOpts.maxOutgoingPacketSize, ); } encoder.setRemainder(packet.payload); return encoder.done(flags); } export function decode( buffer: Uint8Array, flags: number, codecOpts: CodecOpts, packetType: PacketType, ): PublishPacket { const dup = booleanFlag(flags, BitMask.bit3); const qos = (flags & 6) >> 1; const retain = booleanFlag(flags, BitMask.bit0); if (qos !== 0 && qos !== 1 && qos !== 2) { throw new DecoderError("Invalid qos"); } if (dup && qos === 0) { throw new DecoderError("Invalid qos for possible duplicate"); } const decoder = new Decoder(packetType, buffer); const topic = decoder.getUTF8String(); let id = undefined; if (qos > 0) { id = decoder.getInt16(); } if (codecOpts.protocolLevel === 5) { const properties = decoder.getProperties(PacketType.publish); const payload = decoder.getRemainder(); return { type: PacketType.publish, protocolLevel: 5, properties, topic, payload, dup, retain, qos, id, }; } // no decoder.done() required because of getRemainder const payload = decoder.getRemainder(); return { type: PacketType.publish, protocolLevel: codecOpts.protocolLevel, topic, payload, dup, retain, qos, id, }; }