@apocaliss92/nodelink-js 0.1.8 → 0.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/protocol/framing.ts","../src/bcudp/BcUdpStream.ts","../src/bcudp/constants.ts","../src/bcudp/crc.ts","../src/bcudp/xmlCrypto.ts","../src/bcudp/packets.ts","../src/bcudp/xml.ts","../src/logging/logger.ts","../src/client/BaichuanClient.ts","../src/rfc/helpers.ts","../src/baichuan/stream/BaichuanRtspServer.ts","../src/baichuan/stream/rtspFlow.ts","../src/reolink/baichuan/capabilities.ts","../src/reolink/baichuan/ReolinkBaichuanApi.ts","../src/baichuan/stream/MpegTsMuxer.ts","../src/reolink/baichuan/utils/xml.ts","../src/reolink/baichuan/utils/abilityInfo.ts","../src/reolink/baichuan/utils/logging.ts","../src/reolink/baichuan/utils/aiState.ts","../src/reolink/baichuan/utils/channelInfoPush.ts","../src/reolink/baichuan/utils/channelInfoStore.ts","../src/reolink/baichuan/utils/events.ts","../src/reolink/baichuan/utils/parsing.ts","../src/reolink/baichuan/utils/pip.ts","../src/reolink/baichuan/utils/ptz.ts","../src/reolink/baichuan/xmlUtils.ts","../src/reolink/baichuan/utils/pushSettings.ts","../src/reolink/baichuan/utils/recordingDownload.ts","../src/reolink/baichuan/utils/recordingReplay.ts","../src/reolink/baichuan/utils/recordings.ts","../src/reolink/baichuan/utils/recordingsFileInfoList.ts","../src/reolink/baichuan/utils/streamMetadata.ts","../src/reolink/baichuan/utils/talk.ts","../src/reolink/baichuan/utils/talkConfig.ts","../src/reolink/baichuan/utils/talkSession.ts","../src/reolink/baichuan/utils/uid.ts","../src/reolink/baichuan/utils/uidDiscovery.ts","../src/reolink/baichuan/utils/eventsGetEvents.ts","../src/reolink/baichuan/utils/pir.ts","../src/reolink/baichuan/utils/uidRecordings.ts","../src/reolink/baichuan/utils/whiteLed.ts","../src/reolink/discovery.ts","../src/reolink/autodetect.ts"],"sourcesContent":["import { BC_MAGIC, BC_MAGIC_REV, bcHeaderHasPayloadOffset } from \"./constants\";\n\nexport type AnyBuffer = Buffer<ArrayBufferLike>;\n\nexport type BaichuanHeader = {\n magic: AnyBuffer;\n cmdId: number;\n bodyLen: number;\n channelId: number;\n streamType: number;\n msgNum: number;\n responseCode: number;\n messageClass: number;\n payloadOffset?: number;\n};\n\nexport type BaichuanFrame = {\n header: BaichuanHeader;\n /** Raw body bytes, exactly `bodyLen` bytes (extension+payload, if any). */\n body: AnyBuffer;\n /** `Buffer` slice of `body` for extension (can be empty). */\n extension: AnyBuffer;\n /** `Buffer` slice of `body` for payload (can be empty). */\n payload: AnyBuffer;\n /** Convenience key: u32 little-endian from header bytes [12..16]. */\n messageKey: number;\n /** Total raw bytes of this frame: header+body. */\n raw: AnyBuffer;\n};\n\nexport function encodeHeader(h: Omit<BaichuanHeader, \"magic\"> & { magic?: AnyBuffer }): AnyBuffer {\n const hasOffset = bcHeaderHasPayloadOffset(h.messageClass);\n const headerLen = hasOffset ? 24 : 20;\n const buf = Buffer.alloc(headerLen) as AnyBuffer;\n\n const magic = (h.magic ?? (BC_MAGIC as AnyBuffer)) as AnyBuffer;\n if (magic.length !== 4) throw new Error(\"magic must be 4 bytes\");\n magic.copy(buf, 0);\n\n buf.writeUInt32LE(h.cmdId >>> 0, 4);\n buf.writeUInt32LE(h.bodyLen >>> 0, 8);\n buf.writeUInt8(h.channelId & 0xff, 12);\n buf.writeUInt8(h.streamType & 0xff, 13);\n buf.writeUInt16LE(h.msgNum & 0xffff, 14);\n buf.writeUInt16LE(h.responseCode & 0xffff, 16);\n buf.writeUInt16LE(h.messageClass & 0xffff, 18);\n if (hasOffset) {\n buf.writeUInt32LE((h.payloadOffset ?? 0) >>> 0, 20);\n }\n return buf;\n}\n\nexport function decodeHeader(buf: AnyBuffer): { header: BaichuanHeader; headerLen: number; messageKey: number } {\n if (buf.length < 20) throw new Error(\"not enough data for Baichuan header\");\n\n const magic = buf.subarray(0, 4);\n if (!magic.equals(BC_MAGIC) && !magic.equals(BC_MAGIC_REV)) {\n throw new Error(`invalid Baichuan magic: ${magic.toString(\"hex\")}`);\n }\n\n const cmdId = buf.readUInt32LE(4);\n const bodyLen = buf.readUInt32LE(8);\n const channelId = buf.readUInt8(12);\n const streamType = buf.readUInt8(13);\n const msgNum = buf.readUInt16LE(14);\n const responseCode = buf.readUInt16LE(16);\n const messageClass = buf.readUInt16LE(18);\n\n const headerLen = bcHeaderHasPayloadOffset(messageClass) ? 24 : 20;\n if (buf.length < headerLen) throw new Error(\"not enough data for Baichuan header (needs 24 bytes)\");\n\n const messageKey = buf.readUInt32LE(12);\n const header: BaichuanHeader = {\n magic: Buffer.from(magic) as AnyBuffer,\n cmdId,\n bodyLen,\n channelId,\n streamType,\n msgNum,\n responseCode,\n messageClass,\n };\n if (headerLen === 24) {\n header.payloadOffset = buf.readUInt32LE(20);\n }\n\n return { header, headerLen, messageKey };\n}\n\n/**\n * Streaming parser for Baichuan TCP.\n * It follows the Python reference behavior:\n * - expects each message to start with the magic header\n * - supports multiple messages per TCP chunk\n * - waits for full body before emitting\n */\nexport class BaichuanFrameParser {\n private buffer: AnyBuffer = Buffer.alloc(0) as AnyBuffer;\n\n push(chunk: Buffer): BaichuanFrame[] {\n if (chunk.length === 0) return [];\n const c = chunk as AnyBuffer;\n this.buffer = this.buffer.length === 0 ? c : (Buffer.concat([this.buffer, c]) as AnyBuffer);\n const out: BaichuanFrame[] = [];\n\n while (true) {\n // Need at least 4 to check magic\n if (this.buffer.length < 4) break;\n\n // Realign to magic if needed\n if (!this.buffer.subarray(0, 4).equals(BC_MAGIC) && !this.buffer.subarray(0, 4).equals(BC_MAGIC_REV)) {\n const idx = this.buffer.indexOf(BC_MAGIC);\n const idxRev = this.buffer.indexOf(BC_MAGIC_REV);\n const next = idx === -1 ? idxRev : idxRev === -1 ? idx : Math.min(idx, idxRev);\n if (next === -1) {\n // keep only last 3 bytes in case they start a magic prefix\n this.buffer = this.buffer.subarray(Math.max(0, this.buffer.length - 3));\n break;\n }\n this.buffer = this.buffer.subarray(next);\n if (this.buffer.length < 20) break;\n }\n\n if (this.buffer.length < 20) break;\n let headerInfo: ReturnType<typeof decodeHeader>;\n try {\n headerInfo = decodeHeader(this.buffer);\n } catch {\n // not enough for 24 bytes or invalid; wait for more\n break;\n }\n\n const { header, headerLen, messageKey } = headerInfo;\n const frameLen = headerLen + header.bodyLen;\n if (this.buffer.length < frameLen) break;\n\n const raw = this.buffer.subarray(0, frameLen);\n const body = raw.subarray(headerLen);\n\n let extLen = 0;\n if (headerLen === 24) {\n const off = header.payloadOffset ?? 0;\n // payloadOffset==0 can mean \"no extension\", treat as extLen=0\n extLen = off > 0 ? Math.min(off, header.bodyLen) : 0;\n }\n const extension = body.subarray(0, extLen);\n const payload = body.subarray(extLen);\n\n out.push({ header, body, extension, payload, messageKey, raw });\n this.buffer = this.buffer.subarray(frameLen);\n }\n\n return out;\n }\n}\n\n","import dgram from \"node:dgram\";\nimport dns from \"node:dns/promises\";\nimport { EventEmitter } from \"node:events\";\nimport { type AddressInfo } from \"node:net\";\nimport { networkInterfaces } from \"node:os\";\nimport { setInterval as setIntervalNode } from \"node:timers\";\nimport { BCUDP_DATA_HEADER_SIZE, BCUDP_DEFAULT_MTU, BCUDP_DISCOVERY_PORT_LOCAL_UID, BCUDP_DISCOVERY_PORT_LOCAL_ANY } from \"./constants\";\nimport { decodeBcUdpPacket, encodeAckPacket, encodeDataPacket, encodeDiscoveryPacket } from \"./packets\";\nimport { buildC2dA, buildC2dC, buildC2dHb, buildC2dT, buildC2mQ, buildC2rC, buildC2rCfm, parseD2cCfm, parseD2cCr, parseD2cDisc, parseD2cHb, parseD2cT, parseM2cQr, parseR2cCr, type IpPort } from \"./xml\";\n\nclass AckLatency {\n private currentValues: number[] = [];\n private lastReceiveTime: number | null = null;\n private displayValue: number = 0;\n private lastDisplayTime: number | null = null;\n\n getValue(): number {\n return this.displayValue;\n }\n\n feed(): void {\n const now = performance.now(); // Use high-res timer if available, or Date.now()\n if (this.lastReceiveTime !== null) {\n const diff = (now - this.lastReceiveTime) * 1000; // ms to micros\n this.currentValues.push(diff);\n }\n this.lastReceiveTime = now;\n\n if (this.lastDisplayTime !== null) {\n if (now - this.lastDisplayTime > 1000) {\n this.lastDisplayTime = now;\n const count = this.currentValues.length;\n if (count > 0) {\n const sum = this.currentValues.reduce((a, b) => a + b, 0);\n this.displayValue = Math.floor(sum / count);\n } else {\n this.displayValue = 0;\n }\n this.currentValues = [];\n }\n } else {\n this.lastDisplayTime = now;\n this.displayValue = 0;\n }\n }\n}\n\nexport type BcUdpStreamOptions =\n | {\n /** Local discovery via UID (typical for battery cameras). */\n mode: \"uid\";\n uid: string;\n\n /**\n * Optional direct discovery target.\n *\n * When provided, discovery will first try sending UID discovery packets directly to this host\n * (unicast) before falling back to LAN broadcast.\n *\n * Useful when the camera IP is already known and you want to avoid relying solely on broadcast.\n */\n directHost?: string;\n\n /**\n * Discovery method for UID-based connection.\n * - `local-broadcast`: UDP broadcast to LAN (no Reolink servers)\n * - `local`: legacy alias for `local-broadcast`\n * - `local-broadcast`: legacy alias for `local-broadcast`\n * - `remote`: use Reolink servers to learn addresses, then try direct device connection\n * - `map`: device-initiated via public (dmap) address\n * - `relay`: fully relayed via Reolink servers\n */\n discoveryMethod?: BcUdpDiscoveryMethod;\n }\n | {\n /** Direct connection with already-known parameters. */\n mode: \"direct\";\n host: string;\n port: number;\n clientId: number;\n cameraId: number;\n };\n\nexport type BcUdpDiscoveryMethod =\n | \"local-broadcast\"\n | \"local-direct\"\n | \"remote\"\n | \"map\"\n | \"relay\"\n\ntype BcUdpP2pDiscoveryMethod = Exclude<BcUdpDiscoveryMethod, \"local-broadcast\" | \"local-direct\">;\n\ntype SockAddr = { host: string; port: number };\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((r) => setTimeout(r, ms));\n}\n\nconst P2P_RELAY_HOSTNAMES = [\n \"p2p.reolink.com\",\n \"p2p1.reolink.com\",\n \"p2p2.reolink.com\",\n \"p2p3.reolink.com\",\n \"p2p4.reolink.com\",\n \"p2p5.reolink.com\",\n \"p2p6.reolink.com\",\n \"p2p7.reolink.com\",\n \"p2p8.reolink.com\",\n \"p2p9.reolink.com\",\n \"p2p10.reolink.com\",\n \"p2p11.reolink.com\",\n];\n\nconst P2P_LOOKUP_PORT = 9999;\nconst P2P_MAX_WAIT_MS = 15_000;\nconst P2P_RESEND_WAIT_MS = 500;\n\ntype SendEntry = { packetId: number; buf: Buffer; ts: number };\n\n/**\n * Implements BCUDP as a reliable \"byte stream\" (ACK + resend).\n */\nexport class BcUdpStream extends EventEmitter<{\n data: [Buffer];\n close: [];\n error: [Error];\n debug: [string, unknown?];\n}> {\n private readonly opts: BcUdpStreamOptions;\n private sock: dgram.Socket | undefined;\n private remote?: { host: string; port: number };\n private mtu: number;\n\n private clientId: number | undefined;\n private cameraId: number | undefined;\n private sid: number | undefined;\n\n private sendPacketId = 0;\n private packetsWant = 0;\n private received = new Map<number, Buffer>();\n private sent = new Map<number, SendEntry>();\n\n private ackTimer: NodeJS.Timeout | undefined;\n private resendTimer: NodeJS.Timeout | undefined;\n private hbTimer: NodeJS.Timeout | undefined;\n private discoveryTid: number | undefined;\n\n private acceptSent = false;\n private lastAcceptAtMs: number | undefined;\n\n private ackScheduled = false;\n private ackLatency = new AckLatency();\n\n // Pattern: compute the ACK payload when state changes (on data receive),\n // but send the latest ACK on a tight interval. This keeps the send path cheap,\n // which is important in Node under heavy load.\n private lastAckPacket: Buffer = Buffer.alloc(0);\n private ackSentCount = 0;\n\n // Decouple UDP receive path (ACK timing) from consumer parsing.\n // Emitting data synchronously from the UDP socket callback can block the event loop\n // (especially with verbose logging / heavy frame parsing), which delays ACKs and can\n // cause the camera to abort the stream.\n private pendingData: Buffer[] = [];\n private pendingDataOffset = 0;\n private drainScheduled = false;\n\n private getKeepAliveTid(): number {\n // Keep a stable TID for device keepalive.\n // Many cameras appear to associate the session with that TID.\n if (this.discoveryTid != null) return this.discoveryTid;\n return (Math.floor(Math.random() * 255) | 0) >>> 0;\n }\n\n constructor(options: BcUdpStreamOptions) {\n super();\n this.opts = options;\n this.mtu = BCUDP_DEFAULT_MTU;\n }\n\n /** True if the underlying UDP socket is open and the remote peer is known. */\n isConnected(): boolean {\n return !!this.sock && !!this.remote && this.cameraId != null;\n }\n\n async connect(): Promise<void> {\n if (this.sock) return;\n const sock = dgram.createSocket(\"udp4\");\n this.sock = sock;\n\n try {\n sock.setRecvBufferSize(4 * 1024 * 1024);\n sock.setSendBufferSize(4 * 1024 * 1024);\n } catch (e) {\n // Ignore if not supported\n }\n\n sock.on(\"message\", (msg, rinfo) => {\n try {\n const p = decodeBcUdpPacket(msg);\n this.handlePacket(p, rinfo.address, rinfo.port);\n } catch (e) {\n this.emit(\"error\", e instanceof Error ? e : new Error(String(e)));\n }\n });\n sock.on(\"error\", (e) => this.emit(\"error\", e));\n sock.on(\"close\", () => this.emit(\"close\"));\n\n await new Promise<void>((resolve) => sock.bind(0, \"0.0.0.0\", () => resolve()));\n\n if (this.opts.mode === \"direct\") {\n this.remote = { host: this.opts.host, port: this.opts.port };\n this.clientId = this.opts.clientId;\n this.cameraId = this.opts.cameraId;\n } else {\n await this.discoveryUid(sock);\n }\n\n this.startTimers();\n }\n\n private async discoveryUid(sock: dgram.Socket): Promise<void> {\n if (this.opts.mode !== \"uid\") throw new Error(\"Internal: discoveryUid called for non-uid mode\");\n\n const method: BcUdpDiscoveryMethod = this.opts.discoveryMethod ?? \"local-direct\";\n\n if (method === \"local-broadcast\" || method === \"local-direct\") {\n await this.discoveryUidLocal(sock, { localMode: method });\n return;\n }\n\n if (method !== \"remote\" && method !== \"map\" && method !== \"relay\") {\n throw new Error(`Unsupported BCUDP discovery method: ${String(method)}`);\n }\n\n await this.discoveryUidP2p(sock, method);\n }\n\n private getLocalIPv4(): string {\n const ifaces = networkInterfaces();\n for (const name of Object.keys(ifaces)) {\n const entries = ifaces[name];\n if (!entries) continue;\n for (const addr of entries) {\n if (addr.family === \"IPv4\" && !addr.internal) {\n return addr.address;\n }\n }\n }\n throw new Error(\"No non-loopback IPv4 address found (required for P2P discovery)\");\n }\n\n private async discoveryUidP2p(sock: dgram.Socket, method: BcUdpP2pDiscoveryMethod): Promise<void> {\n if (this.opts.mode !== \"uid\") throw new Error(\"Internal: discoveryUidP2p called for non-uid mode\");\n\n const addr = sock.address();\n const localPort = typeof addr === \"string\" ? 0 : (addr as AddressInfo).port;\n const cid = (Math.floor(Math.random() * 0x7fffffff) | 0) || 82000;\n\n const lookup = await this.p2pUidLookup(sock, this.opts.uid);\n const reg = await this.p2pRegister(sock, {\n uid: this.opts.uid,\n cid,\n localPort,\n relay: lookup.relay,\n reg: lookup.reg,\n });\n\n const conn: \"local\" | \"map\" | \"relay\" = method === \"remote\" ? \"local\" : method;\n const connected = await this.p2pConnect(sock, { ...reg, uid: this.opts.uid, cid }, method);\n\n // Confirm to register server (best-effort like neolink: send a few times).\n await this.p2pConfirm(sock, reg.reg, {\n sid: reg.sid,\n conn,\n cid,\n did: connected.did,\n });\n\n this.clientId = cid;\n this.cameraId = connected.did;\n this.sid = reg.sid;\n this.discoveryTid = connected.tid;\n this.remote = { host: connected.rhost, port: connected.rport };\n }\n\n private async p2pUidLookup(sock: dgram.Socket, uid: string): Promise<{ reg: IpPort; relay: IpPort }> {\n const resolved: string[] = [];\n for (const host of P2P_RELAY_HOSTNAMES) {\n try {\n const answers = await dns.lookup(host, { family: 4, all: true });\n for (const a of answers) {\n if (a.address && !resolved.includes(a.address)) resolved.push(a.address);\n }\n } catch {\n // ignore DNS failures per-host\n }\n }\n if (resolved.length === 0) {\n throw new Error(\"P2P UID lookup failed: no p2p.reolink.com addresses resolved\");\n }\n\n const start = Date.now();\n let lastErr: Error | undefined;\n for (const ip of resolved) {\n const remaining = P2P_MAX_WAIT_MS - (Date.now() - start);\n if (remaining <= 0) break;\n try {\n const res = await this.p2pUidLookupOne(sock, uid, { host: ip, port: P2P_LOOKUP_PORT }, Math.min(remaining, 3_000));\n return res;\n } catch (e) {\n lastErr = e instanceof Error ? e : new Error(String(e));\n }\n }\n throw lastErr ?? new Error(\"P2P UID lookup failed\");\n }\n\n private async p2pUidLookupOne(\n sock: dgram.Socket,\n uid: string,\n dest: SockAddr,\n timeoutMs: number,\n ): Promise<{ reg: IpPort; relay: IpPort }> {\n const tid = (Math.floor(Math.random() * 0x7fffffff) | 0) >>> 0;\n const xml = buildC2mQ({ uid });\n const pkt = encodeDiscoveryPacket(tid, xml);\n\n return await new Promise((resolve, reject) => {\n const deadline = setTimeout(() => {\n cleanup();\n reject(new Error(`P2P UID lookup timeout (${dest.host}:${dest.port})`));\n }, timeoutMs);\n\n const onMsg = (msg: Buffer) => {\n try {\n const p = decodeBcUdpPacket(msg);\n if (p.kind !== \"discovery\") return;\n if ((p.tid >>> 0) !== (tid >>> 0)) return;\n const qr = parseM2cQr(p.xml);\n if (!qr?.reg || !qr?.relay) return;\n cleanup();\n resolve({ reg: qr.reg, relay: qr.relay });\n } catch {\n // ignore\n }\n };\n\n const send = () => {\n try {\n sock.send(pkt, dest.port, dest.host);\n } catch {\n // ignore\n }\n };\n\n const retryTimer = setIntervalNode(send, P2P_RESEND_WAIT_MS);\n const cleanup = () => {\n clearTimeout(deadline);\n clearInterval(retryTimer);\n sock.off(\"message\", onMsg);\n };\n\n sock.on(\"message\", onMsg);\n send();\n });\n }\n\n private async p2pRegister(\n sock: dgram.Socket,\n params: { uid: string; cid: number; localPort: number; reg: IpPort; relay: IpPort },\n ): Promise<{ reg: SockAddr; dev?: SockAddr; dmap?: SockAddr; relay?: SockAddr; sid: number }> {\n const localIp = this.getLocalIPv4();\n const tid = (Math.floor(Math.random() * 0x7fffffff) | 0) >>> 0;\n const xml = buildC2rC({\n uid: params.uid,\n cli: { ip: localIp, port: params.localPort },\n relay: params.relay,\n cid: params.cid,\n family: 4,\n revision: 3,\n });\n const pkt = encodeDiscoveryPacket(tid, xml);\n const regDest: SockAddr = { host: params.reg.ip, port: params.reg.port };\n\n const parsed = await new Promise<{ sid: number; dev?: SockAddr; dmap?: SockAddr; relay?: SockAddr }>((resolve, reject) => {\n const timeout = setTimeout(() => {\n cleanup();\n reject(new Error(`P2P register timeout after ${P2P_MAX_WAIT_MS}ms`));\n }, P2P_MAX_WAIT_MS);\n\n const onMsg = (msg: Buffer, rinfo: dgram.RemoteInfo) => {\n try {\n const p = decodeBcUdpPacket(msg);\n if (p.kind !== \"discovery\") return;\n if ((p.tid >>> 0) !== (tid >>> 0)) return;\n\n const r = parseR2cCr(p.xml);\n if (!r) return;\n if (r.rsp === -1 || r.rsp === -3) {\n cleanup();\n reject(new Error(`P2P register rejected (rsp=${r.rsp})`));\n return;\n }\n if (r.sid == null) return;\n\n const normalize = (ipPort?: IpPort): SockAddr | undefined => {\n if (!ipPort?.ip) return undefined;\n const port = ipPort.port === 0 ? rinfo.port : ipPort.port;\n if (!port) return undefined;\n return { host: ipPort.ip, port };\n };\n\n const dev = normalize(r.dev);\n const dmap = normalize(r.dmap);\n const relay = normalize(r.relay);\n if (!dev && !dmap && !relay) return;\n\n cleanup();\n resolve({\n sid: r.sid,\n ...(dev ? { dev } : {}),\n ...(dmap ? { dmap } : {}),\n ...(relay ? { relay } : {}),\n });\n } catch {\n // ignore\n }\n };\n\n const send = () => {\n try {\n sock.send(pkt, regDest.port, regDest.host);\n } catch {\n // ignore\n }\n };\n\n const retryTimer = setIntervalNode(send, P2P_RESEND_WAIT_MS);\n const cleanup = () => {\n clearTimeout(timeout);\n clearInterval(retryTimer);\n sock.off(\"message\", onMsg);\n };\n\n sock.on(\"message\", onMsg);\n send();\n });\n\n return {\n reg: regDest,\n sid: parsed.sid,\n ...(parsed.dev ? { dev: parsed.dev } : {}),\n ...(parsed.dmap ? { dmap: parsed.dmap } : {}),\n ...(parsed.relay ? { relay: parsed.relay } : {}),\n };\n }\n\n private async p2pConnect(\n sock: dgram.Socket,\n reg: { reg: SockAddr; dev?: SockAddr; dmap?: SockAddr; relay?: SockAddr; sid: number; uid: string; cid: number },\n method: BcUdpP2pDiscoveryMethod,\n ): Promise<{ did: number; rhost: string; rport: number; tid: number }> {\n if (method === \"remote\") {\n const tasks: Array<Promise<{ did: number; rhost: string; rport: number; tid: number }>> = [];\n if (reg.dev) tasks.push(this.p2pClientInitiated(sock, { sid: reg.sid, cid: reg.cid, conn: \"local\", dest: reg.dev }));\n if (reg.dmap) tasks.push(this.p2pDeviceInitiated(sock, { sid: reg.sid, cid: reg.cid, conn: \"local\", expectFrom: reg.dmap }));\n if (tasks.length === 0) throw new Error(\"P2P remote discovery: missing dev/dmap addresses\");\n return await Promise.race(tasks);\n }\n\n if (method === \"map\") {\n if (!reg.dmap) throw new Error(\"P2P map discovery: missing dmap address\");\n return await this.p2pDeviceInitiated(sock, { sid: reg.sid, cid: reg.cid, conn: \"map\", expectFrom: reg.dmap });\n }\n\n // relay\n if (!reg.relay) throw new Error(\"P2P relay discovery: missing relay address\");\n return await this.p2pClientInitiated(sock, { sid: reg.sid, cid: reg.cid, conn: \"relay\", dest: reg.relay, requireConnMatch: true });\n }\n\n private async p2pClientInitiated(\n sock: dgram.Socket,\n params: { sid: number; cid: number; conn: \"local\" | \"relay\"; dest: SockAddr; requireConnMatch?: boolean },\n ): Promise<{ did: number; rhost: string; rport: number; tid: number }> {\n const tid = (Math.floor(Math.random() * 0x7fffffff) | 0) >>> 0;\n const xml = buildC2dT({ sid: params.sid, conn: params.conn, cid: params.cid, mtu: this.mtu });\n const pkt = encodeDiscoveryPacket(tid, xml);\n\n return await new Promise((resolve, reject) => {\n const timeout = setTimeout(() => {\n cleanup();\n reject(new Error(`P2P client-initiated (${params.conn}) timeout after ${P2P_MAX_WAIT_MS}ms`));\n }, P2P_MAX_WAIT_MS);\n\n const onMsg = (msg: Buffer, rinfo: dgram.RemoteInfo) => {\n try {\n const p = decodeBcUdpPacket(msg);\n if (p.kind !== \"discovery\") return;\n const cfm = parseD2cCfm(p.xml);\n if (!cfm) return;\n if (cfm.cid !== params.cid) return;\n if (cfm.sid !== params.sid) return;\n if (params.requireConnMatch && (cfm.conn ?? \"\") !== params.conn) return;\n if (cfm.did == null) return;\n cleanup();\n resolve({ did: cfm.did, rhost: rinfo.address, rport: rinfo.port, tid });\n } catch {\n // ignore\n }\n };\n\n const send = () => {\n try {\n sock.send(pkt, params.dest.port, params.dest.host);\n } catch {\n // ignore\n }\n };\n\n const retryTimer = setIntervalNode(send, P2P_RESEND_WAIT_MS);\n const cleanup = () => {\n clearTimeout(timeout);\n clearInterval(retryTimer);\n sock.off(\"message\", onMsg);\n };\n\n sock.on(\"message\", onMsg);\n send();\n });\n }\n\n private async p2pDeviceInitiated(\n sock: dgram.Socket,\n params: { sid: number; cid: number; conn: \"local\" | \"map\"; expectFrom: SockAddr },\n ): Promise<{ did: number; rhost: string; rport: number; tid: number }> {\n return await new Promise((resolve, reject) => {\n const timeout = setTimeout(() => {\n cleanup();\n reject(new Error(`P2P device-initiated (${params.conn}) timeout after ${P2P_MAX_WAIT_MS}ms`));\n }, P2P_MAX_WAIT_MS);\n\n let state: \"wait_t\" | \"wait_cfm\" = \"wait_t\";\n let did: number | undefined;\n let tid: number | undefined;\n let rhost: string | undefined;\n let rport: number | undefined;\n let resendA: NodeJS.Timeout | undefined;\n\n const sendA = () => {\n if (state !== \"wait_cfm\" || did == null || tid == null || rhost == null || rport == null) return;\n try {\n const aXml = buildC2dA({ sid: params.sid, conn: params.conn, cid: params.cid, did, mtu: this.mtu });\n const aPkt = encodeDiscoveryPacket(tid, aXml);\n sock.send(aPkt, rport, rhost);\n } catch {\n // ignore\n }\n };\n\n const onMsg = (msg: Buffer, info: dgram.RemoteInfo) => {\n try {\n const p = decodeBcUdpPacket(msg);\n if (p.kind !== \"discovery\") return;\n\n if (state === \"wait_t\") {\n const dt = parseD2cT(p.xml);\n if (!dt) return;\n if (dt.cid !== params.cid) return;\n if (dt.sid !== params.sid) return;\n if ((dt.conn ?? \"\") !== params.conn) return;\n if (info.address !== params.expectFrom.host) return;\n if (params.expectFrom.port && info.port !== params.expectFrom.port) {\n // Some environments can re-map ports; be permissive if we know only host.\n }\n\n did = dt.did;\n tid = p.tid;\n rhost = info.address;\n rport = info.port;\n state = \"wait_cfm\";\n\n sendA();\n resendA = setIntervalNode(sendA, P2P_RESEND_WAIT_MS);\n return;\n }\n\n const cfm = parseD2cCfm(p.xml);\n if (!cfm) return;\n if (cfm.cid !== params.cid) return;\n if (cfm.sid !== params.sid) return;\n if (cfm.did == null || did == null) return;\n if (cfm.did !== did) return;\n if ((cfm.conn ?? \"\") !== params.conn) return;\n\n cleanup();\n resolve({ did, rhost: info.address, rport: info.port, tid: tid ?? 0 });\n } catch {\n // ignore\n }\n };\n\n const cleanup = () => {\n clearTimeout(timeout);\n if (resendA) clearInterval(resendA);\n sock.off(\"message\", onMsg);\n };\n\n sock.on(\"message\", onMsg);\n });\n }\n\n private async p2pConfirm(sock: dgram.Socket, reg: SockAddr, params: { sid: number; conn: string; cid: number; did: number }): Promise<void> {\n const xml = buildC2rCfm({ sid: params.sid, conn: params.conn, rsp: 0, cid: params.cid, did: params.did });\n for (let i = 0; i < 5; i++) {\n const tid = (Math.floor(Math.random() * 0x7fffffff) | 0) >>> 0;\n const pkt = encodeDiscoveryPacket(tid, xml);\n try {\n sock.send(pkt, reg.port, reg.host);\n } catch {\n // ignore\n }\n await sleep(P2P_RESEND_WAIT_MS);\n }\n }\n\n private async discoveryUidLocal(sock: dgram.Socket, opts?: { localMode?: \"local-broadcast\" | \"local-direct\" }): Promise<void> {\n if (this.opts.mode !== \"uid\") throw new Error(\"Internal: discoveryUidLocal called for non-uid mode\");\n // Internal defaults (do not expose knobs):\n // - Battery cameras may be sleeping -> keep a longer timeout.\n // - Send to both discovery ports 2015/2018.\n // - Use broadcast to discover by UID.\n const ports = [BCUDP_DISCOVERY_PORT_LOCAL_ANY, BCUDP_DISCOVERY_PORT_LOCAL_UID];\n const broadcastHost = \"255.255.255.255\";\n const directHost = (this.opts.directHost ?? \"\").trim();\n const localMode = opts?.localMode ?? \"local-broadcast\";\n const directFirstWindowMs = localMode === \"local-direct\" && directHost ? 3_000 : 0;\n const discoveryTimeout = 30_000;\n const retryInterval = 500;\n\n const startMs = Date.now();\n\n sock.setBroadcast(true);\n\n const addr = sock.address();\n const localPort = typeof addr === \"string\" ? 0 : (addr as AddressInfo).port;\n const cid = (Math.floor(Math.random() * 0x7fffffff) | 0) || 82000;\n\n // Build discovery packet (will be reused for retries)\n // Default OS is \"MAC\" for discovery\n const xml = buildC2dC({ uid: this.opts.uid, clientPort: localPort, cid, mtu: this.mtu });\n\n const reply = await new Promise<{ cid: number; did: number; rhost: string; rport: number; sid?: number; tid?: number }>((resolve, reject) => {\n const timeout = setTimeout(() => {\n if (retryTimer) clearInterval(retryTimer);\n sock.off(\"message\", onMsg);\n reject(new Error(`BCUDP discovery timeout after ${discoveryTimeout}ms (camera may be sleeping or unreachable)`));\n }, discoveryTimeout);\n\n let retryTimer: NodeJS.Timeout | undefined;\n let retryCount = 0;\n let discoveredSid: number | undefined;\n let discoveredTid: number | undefined;\n let discovered: { cid: number; did: number; rhost: string; rport: number } | undefined;\n let sentT = false;\n let gotT = false;\n let sentA = false;\n let finalizeTimer: NodeJS.Timeout | undefined;\n\n const maybeFinalize = (reason: string) => {\n if (!discovered) return;\n // Once we have cid/did, we can proceed. SID helps stability but some cams omit it.\n // If we managed to complete T/A, great; otherwise continue with best-effort.\n if (finalizeTimer) return;\n // Small delay to allow an immediate CFM to arrive after A.\n finalizeTimer = setTimeout(() => {\n const d = discovered;\n const sid = discoveredSid;\n const tid = discoveredTid;\n if (!d) return;\n sock.off(\"message\", onMsg);\n clearTimeout(timeout);\n if (retryTimer) clearInterval(retryTimer);\n this.emit(\"debug\", \"discovery_finalize\", { reason, ...(sid != null ? { sid } : {}), ...d });\n resolve({ ...d, ...(sid != null ? { sid } : {}), ...(tid != null ? { tid } : {}) });\n }, 250);\n };\n\n const sendT = (rhost: string, rport: number) => {\n if (sentT) return;\n if (!discovered) return;\n // Do NOT send C2D_T when SID is unknown.\n // Some cameras appear to treat unsolicited C2D_T as an error and later disconnect (D2C_DISC).\n if (discoveredSid == null) return;\n try {\n const tid = (Math.floor(Math.random() * 0x7fffffff) | 0) >>> 0;\n const tXml = buildC2dT({ ...(discoveredSid != null ? { sid: discoveredSid } : {}), cid: discovered.cid, mtu: this.mtu, conn: \"local\" });\n const tPkt = encodeDiscoveryPacket(tid, tXml);\n sock.send(tPkt, rport, rhost);\n sentT = true;\n this.emit(\"debug\", \"discovery_t_send\", { sid: discoveredSid, cid: discovered.cid, did: discovered.did, rhost, rport });\n } catch (e) {\n this.emit(\"debug\", \"discovery_t_send_error\", e);\n }\n };\n\n const sendA = (tid: number, rhost: string, rport: number, dt: { sid: number; cid: number; did: number; conn?: string }) => {\n if (sentA) return;\n try {\n const aXml = buildC2dA({ sid: dt.sid, conn: dt.conn ?? \"local\", cid: dt.cid, did: dt.did, mtu: this.mtu });\n const aPkt = encodeDiscoveryPacket(tid, aXml);\n sock.send(aPkt, rport, rhost);\n sentA = true;\n this.emit(\"debug\", \"discovery_a_send\", { sid: dt.sid, cid: dt.cid, did: dt.did, rhost, rport });\n } catch (e) {\n this.emit(\"debug\", \"discovery_a_send_error\", e);\n }\n };\n\n const onMsg = (msg: Buffer, rinfo: dgram.RemoteInfo) => {\n try {\n const p = decodeBcUdpPacket(msg);\n if (p.kind !== \"discovery\") return;\n\n // Helpful for debugging odd camera behavior.\n this.emit(\"debug\", \"discovery_rx\", { tid: p.tid, rhost: rinfo.address, rport: rinfo.port, xmlPreview: p.xml.slice(0, 120) });\n\n // Some models send a D2C_CFM before/around discovery completion.\n // Treat it as a strong signal that the session is established.\n const cfm = parseD2cCfm(p.xml);\n if (cfm) {\n discoveredSid = cfm.sid;\n if (!discovered && cfm.cid != null && cfm.did != null) {\n discovered = { cid: cfm.cid, did: cfm.did, rhost: rinfo.address, rport: rinfo.port };\n }\n // If we have enough to proceed, finalize (but still attempt T/A if possible).\n if (discovered) {\n sendT(rinfo.address, rinfo.port);\n maybeFinalize(\"cfm\");\n }\n }\n\n // Camera->Client T step. Reply with C2D_A using the same tid.\n const dt = parseD2cT(p.xml);\n if (dt) {\n gotT = true;\n discoveredSid = dt.sid;\n if (!discovered) {\n discovered = { cid: dt.cid, did: dt.did, rhost: rinfo.address, rport: rinfo.port };\n }\n this.emit(\"debug\", \"discovery_t_rx\", { sid: dt.sid, cid: dt.cid, did: dt.did, rhost: rinfo.address, rport: rinfo.port });\n sendA(p.tid, rinfo.address, rinfo.port, dt);\n // After receiving T and sending A, we should be good to proceed.\n maybeFinalize(\"t_a\");\n return;\n }\n\n const parsed = parseD2cCr(p.xml);\n if (!parsed) return;\n if (parsed.rsp !== 0) return;\n // Success! Camera responded\n this.emit(\"debug\", \"discovery_success\", { retryCount, rhost: rinfo.address, rport: rinfo.port, sid: parsed.sid ?? discoveredSid, timer: parsed.timer });\n\n discoveredTid = p.tid;\n discovered = { cid: parsed.cid, did: parsed.did, rhost: rinfo.address, rport: rinfo.port };\n if (parsed.sid != null) discoveredSid = parsed.sid;\n\n // Attempt T handshake if SID is available.\n sendT(rinfo.address, rinfo.port);\n\n // Don't resolve immediately: give the camera a chance to complete T/A/CFM.\n // If it doesn't, we'll finalize shortly anyway.\n if (gotT || sentA) {\n maybeFinalize(\"cr_t\");\n } else {\n // If SID is missing, we might still proceed without T.\n // If SID is present, allow a brief window for D2C_T.\n if (discoveredSid != null) {\n if (finalizeTimer) clearTimeout(finalizeTimer);\n finalizeTimer = setTimeout(() => {\n maybeFinalize(\"cr_timeout\");\n }, 1500);\n } else {\n maybeFinalize(\"cr_no_sid\");\n }\n }\n return;\n } catch {\n // ignore\n }\n };\n sock.on(\"message\", onMsg);\n\n // Send initial discovery packet to all ports\n const sendDiscovery = () => {\n const tid = (Math.floor(Math.random() * 255) | 0) >>> 0;\n const packet = encodeDiscoveryPacket(tid, xml);\n\n const elapsedMs = Date.now() - startMs;\n const hosts = (() => {\n if (localMode === \"local-direct\") {\n if (directHost) {\n // Prefer direct unicast first, then add broadcast as fallback.\n if (directFirstWindowMs > 0 && elapsedMs < directFirstWindowMs) return [directHost];\n return [directHost, broadcastHost];\n }\n // No direct host -> degrade gracefully to broadcast.\n return [broadcastHost];\n }\n // local-broadcast: broadcast only.\n return [broadcastHost];\n })();\n\n for (const host of Array.from(new Set(hosts))) {\n for (const port of ports) {\n try {\n sock.send(packet, port, host);\n retryCount++;\n this.emit(\"debug\", \"discovery_send\", { retryCount, host, port });\n } catch (e) {\n this.emit(\"error\", e instanceof Error ? e : new Error(String(e)));\n }\n }\n }\n };\n\n // Send initial packet immediately\n sendDiscovery();\n\n // Retry sending discovery packets at regular intervals to wake up sleeping cameras\n retryTimer = setIntervalNode(() => {\n sendDiscovery();\n }, retryInterval);\n });\n\n this.clientId = reply.cid;\n this.cameraId = reply.did;\n this.sid = reply.sid;\n this.discoveryTid = reply.tid;\n // After discovery, the peer is the responder address (port may vary by model).\n this.remote = { host: reply.rhost, port: reply.rport };\n }\n\n private startTimers(): void {\n if (!this.sock || !this.remote || this.clientId == null || this.cameraId == null) {\n throw new Error(\"BCUDP not ready\");\n }\n\n // Initialize ACK packet (empty)\n this.updateAckPacket();\n\n // Send initial heartbeat immediately\n this.sendHeartbeat();\n\n // ACK every 10ms (official client behavior)\n this.ackTimer = setIntervalNode(() => {\n try {\n this.sendAckFast();\n } catch (e) {\n this.emit(\"error\", e instanceof Error ? e : new Error(String(e)));\n }\n }, 10);\n\n // resend every 500ms\n this.resendTimer = setIntervalNode(() => {\n try {\n this.resendOutstanding();\n } catch (e) {\n this.emit(\"error\", e instanceof Error ? e : new Error(String(e)));\n }\n }, 500);\n\n // Heartbeat: send every 1s with stable TID. Some cameras disconnect if the heartbeat\n // isn't sent frequently enough.\n this.hbTimer = setIntervalNode(() => {\n try {\n this.sendHeartbeat();\n } catch (e) {\n this.emit(\"error\", e instanceof Error ? e : new Error(String(e)));\n }\n }, 1000);\n }\n\n private sendHeartbeat(): void {\n if (!this.sock || !this.remote || this.clientId == null || this.cameraId == null) return;\n // Keep a stable TID for keepalive.\n const tid = this.getKeepAliveTid();\n\n let xml: string;\n // Send C2D_HB as soon as it has CID and DID, even if SID is not yet assigned (or is 0).\n // This keeps the session alive and seems to be what the camera expects after the initial handshake.\n xml = buildC2dHb({ cid: this.clientId, did: this.cameraId });\n\n const pkt = encodeDiscoveryPacket(tid, xml);\n\n this.emit(\"debug\", \"udp_hb_send\", { tid, xml, host: this.remote.host, port: this.remote.port });\n\n // Send to current remote (data port)\n this.sock.send(pkt, this.remote.port, this.remote.host);\n\n // Do NOT send heartbeats to discovery ports (2015/2018) in the main loop.\n // Only send to the connected address.\n // Sending to discovery ports can confuse some cameras and may cause the stream to drop.\n }\n\n private buildAckPayload(): { packetId: number; payload: Buffer } {\n // Some cameras will disconnect if the ACK truth-table grows to ~205 bytes.\n // Keep it comfortably below that.\n const MAX_ACK_PAYLOAD_BYTES = 200;\n\n // Match official client behavior: until we've received and consumed at least\n // one packet (packetsWant>0), send the special \"empty\" ACK.\n // This uses group_id=0xffffffff + packet_id=0xffffffff + empty payload.\n // Avoid sending packet_id=0xffffffff with a non-empty truth-table.\n if (this.packetsWant === 0) {\n return { packetId: 0xffffffff, payload: Buffer.alloc(0) };\n }\n let firstMissing = this.packetsWant;\n while (this.received.has(firstMissing)) firstMissing++;\n\n let max = firstMissing - 1;\n // Avoid spread/Math.max over iterators (can be very costly when many packets are buffered).\n for (const k of this.received.keys()) {\n if (k > max) max = k;\n }\n\n // Cap the ACK bitmap window to avoid disconnects.\n const maxAllowed = firstMissing + MAX_ACK_PAYLOAD_BYTES - 1;\n if (max > maxAllowed) max = maxAllowed;\n\n if (max < firstMissing) {\n return { packetId: (firstMissing - 1) >>> 0, payload: Buffer.alloc(0) };\n }\n\n const len = max - firstMissing + 1;\n const bytes = Buffer.allocUnsafe(len);\n for (let i = 0; i < len; i++) {\n bytes[i] = this.received.has(firstMissing + i) ? 1 : 0;\n }\n return { packetId: (firstMissing - 1) >>> 0, payload: bytes };\n }\n\n private updateAckPacket(): void {\n if (this.cameraId == null) return;\n const { packetId, payload } = this.buildAckPayload();\n this.lastAckPacket = encodeAckPacket({\n connectionId: this.cameraId, // towards camera: did\n // group_id=0xffffffff is only used for the special empty ACK.\n groupId: packetId === 0xffffffff && payload.length === 0 ? 0xffffffff : 0,\n packetId,\n maybeLatency: this.ackLatency.getValue(),\n payload,\n });\n }\n\n private sendAckFast(): void {\n if (!this.sock || !this.remote || this.lastAckPacket.length === 0) return;\n this.sock.send(this.lastAckPacket, this.remote.port, this.remote.host);\n this.ackSentCount++;\n if (this.ackSentCount % 100 === 0) {\n this.emit(\"debug\", \"ack_sent_100\", { latency: this.ackLatency.getValue() });\n }\n }\n\n private scheduleAck(reason: \"data\" | \"manual\" = \"data\"): void {\n // With the cached ACK packet + 10ms timer, we avoid sending ACKs on every\n // inbound packet to prevent overwhelming the camera.\n if (reason === \"manual\") {\n if (this.ackScheduled) return;\n this.ackScheduled = true;\n setImmediate(() => {\n this.ackScheduled = false;\n try {\n this.sendAckFast();\n } catch (e) {\n this.emit(\"error\", e instanceof Error ? e : new Error(String(e)));\n }\n });\n this.emit(\"debug\", \"ack_scheduled\", { reason });\n }\n }\n\n private resendOutstanding(): void {\n if (!this.sock || !this.remote) return;\n for (const [, entry] of this.sent) {\n this.sock.send(entry.buf, this.remote.port, this.remote.host);\n }\n }\n\n private handleAckFromCamera(packetId: number, payload: Buffer): void {\n // Camera ACKs what it received from us.\n // Remove <= packetId and those marked 1 in payload (relative to packetId).\n if (packetId !== 0xffffffff) {\n for (const k of this.sent.keys()) {\n if (k <= packetId) {\n this.sent.delete(k);\n }\n }\n for (let i = 0; i < payload.length; i++) {\n const v = payload[i] ?? 0;\n if (v > 0) {\n const pid = (packetId + 1 + i) >>> 0;\n this.sent.delete(pid);\n }\n }\n }\n this.ackLatency.feed();\n }\n\n private scheduleDrain(): void {\n if (this.drainScheduled) return;\n this.drainScheduled = true;\n setImmediate(() => {\n this.drainScheduled = false;\n try {\n for (; this.pendingDataOffset < this.pendingData.length; this.pendingDataOffset++) {\n this.emit(\"data\", this.pendingData[this.pendingDataOffset]!);\n }\n } catch (e) {\n this.emit(\"error\", e instanceof Error ? e : new Error(String(e)));\n } finally {\n // Reset buffer when fully drained.\n if (this.pendingDataOffset >= this.pendingData.length) {\n this.pendingData = [];\n this.pendingDataOffset = 0;\n }\n }\n });\n }\n\n private flushReceived(): void {\n // Move contiguous received payloads to a pending queue (fast) and drain later.\n while (this.received.has(this.packetsWant)) {\n const chunk = this.received.get(this.packetsWant)!;\n this.received.delete(this.packetsWant);\n this.packetsWant++;\n this.pendingData.push(chunk);\n }\n if (this.pendingData.length > this.pendingDataOffset) {\n this.scheduleDrain();\n }\n }\n\n private handlePacket(p: ReturnType<typeof decodeBcUdpPacket>, rhost: string, rport: number): void {\n // Bind/update remote to whoever talks to us.\n // Important for some battery cameras: discovery happens on one UDP port,\n // but the data/ack stream can come from another port. If we keep sending\n // ACK/heartbeat to the discovery port, the camera may stop streaming.\n const updateRemote = (reason: string) => {\n if (!this.remote || this.remote.host !== rhost || this.remote.port !== rport) {\n this.remote = { host: rhost, port: rport };\n this.emit(\"debug\", \"remote_update\", { reason, host: rhost, port: rport });\n }\n };\n\n if (!this.remote) updateRemote(\"first_packet\");\n\n if (p.kind === \"ack\") {\n if (this.clientId != null && p.connectionId === this.clientId) {\n updateRemote(\"ack\");\n this.handleAckFromCamera(p.packetId, p.payload);\n }\n return;\n }\n\n if (p.kind === \"data\") {\n if (this.clientId != null && p.connectionId === this.clientId) {\n updateRemote(\"data\");\n // this.ackLatency.feed(); // Removed: latency is calculated from ACKs\n if (p.packetId >= this.packetsWant) {\n this.received.set(p.packetId, p.payload);\n this.flushReceived();\n this.updateAckPacket();\n if (this.packetsWant % 100 === 0) {\n this.emit(\"debug\", \"udp_progress\", { packetsWant: this.packetsWant });\n }\n }\n }\n return;\n }\n\n // discovery packets after connect (HB, disconnect, etc.) -> ignored for now.\n if (p.kind === \"discovery\") {\n this.emit(\"debug\", \"discovery_rx_connected\", { tid: p.tid, xml: p.xml });\n\n // Some cameras send heartbeat probes (D2C_HB). Reply with a heartbeat.\n const hb = parseD2cHb(p.xml);\n if (hb && this.clientId != null && this.cameraId != null && hb.cid === this.clientId && hb.did === this.cameraId) {\n this.emit(\"debug\", \"discovery_hb_rx_connected\", { ...hb, rhost, rport });\n try {\n updateRemote(\"d2c_hb\");\n this.sendHeartbeat();\n } catch (e) {\n this.emit(\"debug\", \"discovery_hb_reply_error\", e);\n }\n return;\n }\n\n // Some cameras send D2C_T late (after we've already started using the stream).\n // If we don't reply with C2D_A, the camera may terminate the session (D2C_DISC)\n // after a short time (often ~5-10s).\n const dt = parseD2cT(p.xml);\n if (dt && this.clientId != null && this.cameraId != null && dt.cid === this.clientId && dt.did === this.cameraId) {\n try {\n updateRemote(\"d2c_t\");\n this.sid = dt.sid;\n this.emit(\"debug\", \"discovery_t_rx_connected\", { sid: dt.sid, cid: dt.cid, did: dt.did, rhost, rport });\n const now = Date.now();\n const throttleMs = 750;\n if (this.lastAcceptAtMs == null || now - this.lastAcceptAtMs >= throttleMs) {\n const aXml = buildC2dA({ sid: dt.sid, conn: dt.conn ?? \"local\", cid: dt.cid, did: dt.did, mtu: this.mtu });\n const aPkt = encodeDiscoveryPacket(p.tid, aXml);\n this.sock?.send(aPkt, rport, rhost);\n this.acceptSent = true;\n this.lastAcceptAtMs = now;\n this.emit(\"debug\", \"discovery_a_send_connected\", { sid: dt.sid, cid: dt.cid, did: dt.did, rhost, rport });\n } else {\n this.emit(\"debug\", \"discovery_a_skip_throttle\", { sinceMs: now - this.lastAcceptAtMs, throttleMs, sid: dt.sid, cid: dt.cid, did: dt.did, rhost, rport });\n }\n } catch (e) {\n this.emit(\"debug\", \"discovery_a_send_connected_error\", e);\n }\n return;\n }\n\n const cfm = parseD2cCfm(p.xml);\n if (cfm && this.clientId != null && this.cameraId != null) {\n // Some firmwares send this without us explicitly expecting it. Keep sid for completeness.\n if (cfm.cid === this.clientId && cfm.did === this.cameraId) {\n this.sid = cfm.sid;\n this.emit(\"debug\", \"discovery_cfm_rx_connected\", cfm);\n return;\n }\n }\n\n const disc = parseD2cDisc(p.xml);\n if (disc && this.clientId != null && this.cameraId != null && disc.cid === this.clientId && disc.did === this.cameraId) {\n this.emit(\"debug\", \"discovery_disc_rx_connected\", { ...disc, rhost, rport });\n // Camera terminated the session.\n this.emit(\n \"error\",\n new Error(`BCUDP disconnected by camera (D2C_DISC${this.sid != null ? ` sid=${this.sid}` : \"\"})`),\n );\n void this.close();\n return;\n }\n }\n return;\n }\n\n write(buf: Buffer): void {\n if (!this.sock || !this.remote || this.cameraId == null) throw new Error(\"BCUDP stream is not connected\");\n const maxPayload = this.mtu - BCUDP_DATA_HEADER_SIZE;\n for (let off = 0; off < buf.length; off += maxPayload) {\n const payload = buf.subarray(off, Math.min(buf.length, off + maxPayload));\n const packetId = this.sendPacketId >>> 0;\n this.sendPacketId = (this.sendPacketId + 1) >>> 0;\n const pkt = encodeDataPacket({ connectionId: this.cameraId, packetId, payload: Buffer.from(payload) });\n this.sent.set(packetId, { packetId, buf: pkt, ts: Date.now() });\n this.sock.send(pkt, this.remote.port, this.remote.host);\n }\n }\n\n async close(): Promise<void> {\n if (this.ackTimer) clearInterval(this.ackTimer);\n if (this.resendTimer) clearInterval(this.resendTimer);\n if (this.hbTimer) clearInterval(this.hbTimer);\n this.ackTimer = undefined;\n this.resendTimer = undefined;\n this.hbTimer = undefined;\n\n const s = this.sock;\n this.sock = undefined;\n if (!s) return;\n await new Promise<void>((resolve) => s.close(() => resolve()));\n }\n}\n\n","export const BCUDP_MAGIC_NEGO = 0x2a87cf3a;\nexport const BCUDP_MAGIC_ACK = 0x2a87cf20;\nexport const BCUDP_MAGIC_DATA = 0x2a87cf10;\n\nexport const BCUDP_DEFAULT_MTU = 1350;\nexport const BCUDP_DATA_HEADER_SIZE = 20;\n\n// Local discovery ports used by Reolink.\nexport const BCUDP_DISCOVERY_PORT_LOCAL_ANY = 2015;\nexport const BCUDP_DISCOVERY_PORT_LOCAL_UID = 2018;\n\n","// CRC32 used by BCUDP discovery packets.\n// CRC calculation for BCUDP packets:\n// CRC-32/ISO-HDLC table, but with init=0x00000000 and xorout=0x00000000.\n\nlet table: Uint32Array | undefined;\n\nfunction getTable(): Uint32Array {\n if (table) return table;\n const t = new Uint32Array(256);\n for (let i = 0; i < 256; i++) {\n let c = i;\n for (let k = 0; k < 8; k++) {\n c = (c & 1) ? (0xedb88320 ^ (c >>> 1)) : (c >>> 1);\n }\n t[i] = c >>> 0;\n }\n table = t;\n return t;\n}\n\nexport function bcudpCrc32(buf: Buffer): number {\n const t = getTable();\n let crc = 0x00000000;\n for (let i = 0; i < buf.length; i++) {\n const b = buf[i]!;\n crc = (t[(crc ^ b) & 0xff]! ^ (crc >>> 8)) >>> 0;\n }\n return crc >>> 0;\n}\n\n","// Reolink BCUDP XML crypto (used for discovery/heartbeat XML payloads).\n\nconst XML_KEY_U32 = Uint32Array.from([\n 0x1f2d3c4b, 0x5a6c7f8d, 0x38172e4b, 0x8271635a, 0x863f1a2b, 0xa5c6f7d8, 0x8371e1b4, 0x17f2d3a5,\n]);\n\nfunction* keystream(offset: number): Generator<number, void, void> {\n let idx = 0;\n while (true) {\n const word = (XML_KEY_U32[idx % XML_KEY_U32.length]! + (offset >>> 0)) >>> 0;\n // little-endian bytes\n yield word & 0xff;\n yield (word >>> 8) & 0xff;\n yield (word >>> 16) & 0xff;\n yield (word >>> 24) & 0xff;\n idx++;\n }\n}\n\nexport function bcudpXmlEncrypt(tid: number, plain: Buffer): Buffer {\n const out = Buffer.allocUnsafe(plain.length);\n const ks = keystream(tid >>> 0);\n for (let i = 0; i < plain.length; i++) {\n out[i] = plain[i]! ^ (ks.next().value as number);\n }\n return out;\n}\n\nexport function bcudpXmlDecrypt(tid: number, enc: Buffer): Buffer {\n // XOR symmetric\n return bcudpXmlEncrypt(tid, enc);\n}\n\n","import { BCUDP_MAGIC_ACK, BCUDP_MAGIC_DATA, BCUDP_MAGIC_NEGO } from \"./constants\";\nimport { bcudpCrc32 } from \"./crc\";\nimport { bcudpXmlDecrypt, bcudpXmlEncrypt } from \"./xmlCrypto\";\n\nexport type BcUdpPacket =\n | { kind: \"discovery\"; tid: number; xml: string }\n | { kind: \"ack\"; connectionId: number; groupId: number; packetId: number; maybeLatency: number; payload: Buffer }\n | { kind: \"data\"; connectionId: number; packetId: number; payload: Buffer };\n\nfunction readI32LE(buf: Buffer, offset: number): number {\n return buf.readInt32LE(offset);\n}\n\nexport function encodeDiscoveryPacket(tid: number, xml: string): Buffer {\n const xmlBytes = Buffer.from(xml, \"utf8\");\n const enc = bcudpXmlEncrypt(tid, xmlBytes);\n const checksum = bcudpCrc32(enc);\n\n const header = Buffer.alloc(20);\n header.writeUInt32LE(BCUDP_MAGIC_NEGO, 0);\n header.writeUInt32LE(enc.length >>> 0, 4);\n header.writeUInt32LE(1, 8);\n header.writeUInt32LE(tid >>> 0, 12);\n header.writeUInt32LE(checksum >>> 0, 16);\n return Buffer.concat([header, enc]);\n}\n\nexport function encodeAckPacket(params: {\n connectionId: number;\n groupId: number;\n packetId: number;\n maybeLatency?: number;\n payload?: Buffer;\n}): Buffer {\n const payload = params.payload ?? Buffer.alloc(0);\n const header = Buffer.alloc(28);\n header.writeUInt32LE(BCUDP_MAGIC_ACK, 0);\n header.writeInt32LE(params.connectionId | 0, 4);\n header.writeUInt32LE(0, 8);\n header.writeUInt32LE(params.groupId >>> 0, 12);\n header.writeUInt32LE(params.packetId >>> 0, 16);\n header.writeUInt32LE((params.maybeLatency ?? 0) >>> 0, 20);\n header.writeUInt32LE(payload.length >>> 0, 24);\n return Buffer.concat([header, payload]);\n}\n\nexport function encodeDataPacket(params: { connectionId: number; packetId: number; payload: Buffer }): Buffer {\n const header = Buffer.alloc(20);\n header.writeUInt32LE(BCUDP_MAGIC_DATA, 0);\n header.writeInt32LE(params.connectionId | 0, 4);\n header.writeUInt32LE(0, 8);\n header.writeUInt32LE(params.packetId >>> 0, 12);\n header.writeUInt32LE(params.payload.length >>> 0, 16);\n return Buffer.concat([header, params.payload]);\n}\n\nexport function decodeBcUdpPacket(buf: Buffer): BcUdpPacket {\n if (buf.length < 4) throw new Error(\"BCUDP packet too short\");\n const magic = buf.readUInt32LE(0);\n\n if (magic === BCUDP_MAGIC_NEGO) {\n if (buf.length < 20) throw new Error(\"BCUDP discovery header too short\");\n const payloadSize = buf.readUInt32LE(4);\n const unknownA = buf.readUInt32LE(8);\n if (unknownA !== 1) throw new Error(`BCUDP discovery unknownA expected 1, got ${unknownA}`);\n const tid = buf.readUInt32LE(12);\n const checksum = buf.readUInt32LE(16);\n const enc = buf.subarray(20);\n if (enc.length < payloadSize) throw new Error(\"BCUDP discovery payload truncated\");\n const encSlice = enc.subarray(0, payloadSize);\n const actual = bcudpCrc32(encSlice);\n if ((checksum >>> 0) !== (actual >>> 0)) throw new Error(\"BCUDP discovery CRC mismatch\");\n const plain = bcudpXmlDecrypt(tid, encSlice);\n return { kind: \"discovery\", tid, xml: plain.toString(\"utf8\") };\n }\n\n if (magic === BCUDP_MAGIC_ACK) {\n if (buf.length < 28) throw new Error(\"BCUDP ack header too short\");\n const connectionId = readI32LE(buf, 4);\n const unknownA = buf.readUInt32LE(8);\n if (unknownA !== 0) throw new Error(`BCUDP ack unknownA expected 0, got ${unknownA}`);\n const groupId = buf.readUInt32LE(12);\n const packetId = buf.readUInt32LE(16);\n const maybeLatency = buf.readUInt32LE(20);\n const payloadSize = buf.readUInt32LE(24);\n const payload = buf.subarray(28);\n if (payload.length < payloadSize) throw new Error(\"BCUDP ack payload truncated\");\n return { kind: \"ack\", connectionId, groupId, packetId, maybeLatency, payload: payload.subarray(0, payloadSize) };\n }\n\n if (magic === BCUDP_MAGIC_DATA) {\n if (buf.length < 20) throw new Error(\"BCUDP data header too short\");\n const connectionId = readI32LE(buf, 4);\n const unknownA = buf.readUInt32LE(8);\n if (unknownA !== 0) throw new Error(`BCUDP data unknownA expected 0, got ${unknownA}`);\n const packetId = buf.readUInt32LE(12);\n const payloadSize = buf.readUInt32LE(16);\n const payload = buf.subarray(20);\n if (payload.length < payloadSize) throw new Error(\"BCUDP data payload truncated\");\n return { kind: \"data\", connectionId, packetId, payload: payload.subarray(0, payloadSize) };\n }\n\n throw new Error(`Unknown BCUDP magic 0x${magic.toString(16)}`);\n}\n\n","import { xmlEscape } from \"../protocol/xml\";\n\nexport function buildP2pXml(inner: string): string {\n return `<P2P>${inner}</P2P>`;\n}\n\n/**\n * Build C2D_S message for general UDP broadcast discovery (without UID).\n * This is sent to discover any cameras on the network.\n */\nexport function buildC2dS(params: { clientPort: number }): string {\n return buildP2pXml(`<C2D_S><to><port>${params.clientPort}</port></to></C2D_S>`);\n}\n\nexport type IpPort = { ip: string; port: number };\n\n/**\n * Build C2M_Q message (client -> Reolink middle-man server) for UID lookup.\n * Sent to p2p*.reolink.com:9999.\n */\nexport function buildC2mQ(params: { uid: string; os?: string }): string {\n const os = params.os ?? \"MAC\";\n return buildP2pXml(\n `<C2M_Q>` +\n `<uid>${xmlEscape(params.uid)}</uid>` +\n `<p>${xmlEscape(os)}</p>` +\n `</C2M_Q>`,\n );\n}\n\nexport type M2cQrParsed = {\n reg?: IpPort;\n relay?: IpPort;\n log?: IpPort;\n t?: IpPort;\n};\n\nfunction parseIpPortBlock(tag: \"reg\" | \"relay\" | \"log\" | \"t\", body: string): IpPort | undefined {\n const m = new RegExp(`<${tag}>([\\\\s\\\\S]*?)</${tag}>`, \"i\").exec(body);\n if (!m) return undefined;\n const block = m[1] ?? \"\";\n const ip = /<ip>([^<]+)<\\/ip>/i.exec(block)?.[1];\n const port = /<port>(-?\\d+)<\\/port>/i.exec(block)?.[1];\n if (!ip || port == null) return undefined;\n const portNum = Number(port);\n if (!Number.isFinite(portNum) || portNum <= 0 || portNum > 65535) return undefined;\n return { ip: ip.trim(), port: portNum };\n}\n\nexport function parseM2cQr(xml: string): M2cQrParsed | undefined {\n const m = /<M2C_Q_R>([\\s\\S]*?)<\\/M2C_Q_R>/i.exec(xml);\n if (!m) return undefined;\n const body = m[1] ?? \"\";\n const reg = parseIpPortBlock(\"reg\", body);\n const relay = parseIpPortBlock(\"relay\", body);\n const log = parseIpPortBlock(\"log\", body);\n const t = parseIpPortBlock(\"t\", body);\n if (!reg && !relay && !log && !t) return undefined;\n return { ...(reg ? { reg } : {}), ...(relay ? { relay } : {}), ...(log ? { log } : {}), ...(t ? { t } : {}) };\n}\n\n/**\n * Build C2R_C message (client -> register server) to register local address.\n */\nexport function buildC2rC(params: {\n uid: string;\n cli: IpPort;\n relay: IpPort;\n cid: number;\n family: 4 | 6;\n os?: string;\n revision?: number;\n debug?: boolean;\n}): string {\n const os = params.os ?? \"MAC\";\n const debug = params.debug ?? false;\n const rev = params.revision != null ? `<r>${params.revision}</r>` : \"\";\n return buildP2pXml(\n `<C2R_C>` +\n `<uid>${xmlEscape(params.uid)}</uid>` +\n `<cli><ip>${xmlEscape(params.cli.ip)}</ip><port>${params.cli.port}</port></cli>` +\n `<relay><ip>${xmlEscape(params.relay.ip)}</ip><port>${params.relay.port}</port></relay>` +\n `<cid>${params.cid}</cid>` +\n `<debug>${debug ? \"true\" : \"false\"}</debug>` +\n `<family>${params.family}</family>` +\n `<p>${xmlEscape(os)}</p>` +\n rev +\n `</C2R_C>`,\n );\n}\n\nexport type R2cCrParsed = {\n rsp: number;\n sid?: number;\n dev?: IpPort;\n dmap?: IpPort;\n relay?: IpPort;\n relayt?: IpPort;\n};\n\nexport function parseR2cCr(xml: string): R2cCrParsed | undefined {\n const m = /<R2C_C_R>([\\s\\S]*?)<\\/R2C_C_R>/i.exec(xml);\n if (!m) return undefined;\n const body = m[1] ?? \"\";\n const rspStr = /<rsp>(-?\\d+)<\\/rsp>/i.exec(body)?.[1];\n if (rspStr == null) return undefined;\n const rsp = Number(rspStr);\n if (!Number.isFinite(rsp)) return undefined;\n const sidStr = /<sid>(-?\\d+)<\\/sid>/i.exec(body)?.[1];\n const sid = sidStr != null ? Number(sidStr) : undefined;\n\n const parseCustom = (tag: \"dev\" | \"dmap\" | \"relay\" | \"relayt\") => {\n const mm = new RegExp(`<${tag}>([\\\\s\\\\S]*?)</${tag}>`, \"i\").exec(body);\n if (!mm) return undefined;\n const block = mm[1] ?? \"\";\n const ip = /<ip>([^<]+)<\\/ip>/i.exec(block)?.[1];\n const port = /<port>(-?\\d+)<\\/port>/i.exec(block)?.[1];\n if (!ip || port == null) return undefined;\n const portNum = Number(port);\n if (!Number.isFinite(portNum) || portNum <= 0 || portNum > 65535) return undefined;\n return { ip: ip.trim(), port: portNum } satisfies IpPort;\n };\n\n const devIpPort = parseCustom(\"dev\");\n const dmapIpPort = parseCustom(\"dmap\");\n const relayIpPort = parseCustom(\"relay\");\n const relaytIpPort = parseCustom(\"relayt\");\n\n return {\n rsp,\n ...(sid != null && Number.isFinite(sid) ? { sid } : {}),\n ...(devIpPort ? { dev: devIpPort } : {}),\n ...(dmapIpPort ? { dmap: dmapIpPort } : {}),\n ...(relayIpPort ? { relay: relayIpPort } : {}),\n ...(relaytIpPort ? { relayt: relaytIpPort } : {}),\n };\n}\n\n/**\n * Build C2R_CFM (client -> register server) confirm message.\n */\nexport function buildC2rCfm(params: { sid: number; conn: string; rsp?: number; cid: number; did: number }): string {\n const rsp = params.rsp ?? 0;\n return buildP2pXml(\n `<C2R_CFM>` +\n `<sid>${params.sid}</sid>` +\n `<conn>${xmlEscape(params.conn)}</conn>` +\n `<rsp>${rsp}</rsp>` +\n `<cid>${params.cid}</cid>` +\n `<did>${params.did}</did>` +\n `</C2R_CFM>`,\n );\n}\n\nexport function buildC2dC(params: { uid: string; clientPort: number; cid: number; mtu: number; os?: string }): string {\n // Default OS is \"MAC\" for discovery\n const os = params.os ?? \"MAC\";\n return buildP2pXml(\n `<C2D_C>` +\n `<uid>${xmlEscape(params.uid)}</uid>` +\n `<cli><port>${params.clientPort}</port></cli>` +\n `<cid>${params.cid}</cid>` +\n `<mtu>${params.mtu}</mtu>` +\n `<debug>false</debug>` +\n `<p>${xmlEscape(os)}</p>` +\n `</C2D_C>`,\n );\n}\n\nexport function buildC2dHb(params: { cid: number; did: number }): string {\n return buildP2pXml(`<C2D_HB><cid>${params.cid}</cid><did>${params.did}</did></C2D_HB>`);\n}\n\nexport function buildC2dA(params: { sid: number; conn?: string; cid: number; did: number; mtu: number }): string {\n const conn = params.conn ?? \"local\";\n return buildP2pXml(\n `<C2D_A>` +\n `<sid>${params.sid}</sid>` +\n `<conn>${xmlEscape(conn)}</conn>` +\n `<cid>${params.cid}</cid>` +\n `<did>${params.did}</did>` +\n `<mtu>${params.mtu}</mtu>` +\n `</C2D_A>`\n );\n}\n\nexport function buildC2dT(params: { sid?: number; conn?: string; cid: number; mtu: number }): string {\n const conn = params.conn ?? \"local\";\n const sid = params.sid != null ? `<sid>${params.sid}</sid>` : \"\";\n return buildP2pXml(\n `<C2D_T>` +\n sid +\n `<conn>${xmlEscape(conn)}</conn>` +\n `<cid>${params.cid}</cid>` +\n `<mtu>${params.mtu}</mtu>` +\n `</C2D_T>`\n );\n}\n\nexport type D2cCrParsed = {\n rsp: number;\n cid: number;\n did: number;\n sid?: number;\n timer?: { def?: number; hb?: number; hbt?: number };\n};\n\nexport function parseD2cCr(xml: string): D2cCrParsed | undefined {\n // Minimal parser: extract <rsp>, <cid>, <did> within <D2C_C_R>...</D2C_C_R>\n const m = /<D2C_C_R>([\\s\\S]*?)<\\/D2C_C_R>/.exec(xml);\n if (!m) return undefined;\n const body = m[1] ?? \"\";\n const rsp = /<rsp>(-?\\d+)<\\/rsp>/.exec(body)?.[1];\n const cid = /<cid>(-?\\d+)<\\/cid>/.exec(body)?.[1];\n const did = /<did>(-?\\d+)<\\/did>/.exec(body)?.[1];\n if (rsp == null || cid == null || did == null) return undefined;\n\n const sid = /<sid>(-?\\d+)<\\/sid>/.exec(body)?.[1];\n\n const timerBlock = /<timer>([\\s\\S]*?)<\\/timer>/.exec(body)?.[1];\n const def = timerBlock ? /<def>(-?\\d+)<\\/def>/.exec(timerBlock)?.[1] : undefined;\n const hb = timerBlock ? /<hb>(-?\\d+)<\\/hb>/.exec(timerBlock)?.[1] : undefined;\n const hbt = timerBlock ? /<hbt>(-?\\d+)<\\/hbt>/.exec(timerBlock)?.[1] : undefined;\n const timer = def != null || hb != null || hbt != null ? {\n ...(def != null ? { def: Number(def) } : {}),\n ...(hb != null ? { hb: Number(hb) } : {}),\n ...(hbt != null ? { hbt: Number(hbt) } : {}),\n } : undefined;\n\n return {\n rsp: Number(rsp),\n cid: Number(cid),\n did: Number(did),\n ...(sid != null ? { sid: Number(sid) } : {}),\n ...(timer ? { timer } : {}),\n };\n}\n\nexport type D2cCfmParsed = { sid: number; conn?: string; rsp?: number; cid?: number; did?: number };\n\nexport function parseD2cCfm(xml: string): D2cCfmParsed | undefined {\n const m = /<D2C_CFM>([\\s\\S]*?)<\\/D2C_CFM>/.exec(xml);\n if (!m) return undefined;\n const body = m[1] ?? \"\";\n const sid = /<sid>(-?\\d+)<\\/sid>/.exec(body)?.[1];\n if (sid == null) return undefined;\n const conn = /<conn>([^<]+)<\\/conn>/.exec(body)?.[1];\n const rsp = /<rsp>(-?\\d+)<\\/rsp>/.exec(body)?.[1];\n const cid = /<cid>(-?\\d+)<\\/cid>/.exec(body)?.[1];\n const did = /<did>(-?\\d+)<\\/did>/.exec(body)?.[1];\n return {\n sid: Number(sid),\n ...(conn != null ? { conn } : {}),\n ...(rsp != null ? { rsp: Number(rsp) } : {}),\n ...(cid != null ? { cid: Number(cid) } : {}),\n ...(did != null ? { did: Number(did) } : {}),\n };\n}\n\nexport type D2cTParsed = { sid: number; conn?: string; cid: number; did: number };\n\nexport function parseD2cT(xml: string): D2cTParsed | undefined {\n const m = /<D2C_T>([\\s\\S]*?)<\\/D2C_T>/.exec(xml);\n if (!m) return undefined;\n const body = m[1] ?? \"\";\n const sid = /<sid>(-?\\d+)<\\/sid>/.exec(body)?.[1];\n const cid = /<cid>(-?\\d+)<\\/cid>/.exec(body)?.[1];\n const did = /<did>(-?\\d+)<\\/did>/.exec(body)?.[1];\n if (sid == null || cid == null || did == null) return undefined;\n const conn = /<conn>([^<]+)<\\/conn>/.exec(body)?.[1];\n return {\n sid: Number(sid),\n cid: Number(cid),\n did: Number(did),\n ...(conn != null ? { conn } : {}),\n };\n}\n\nexport type D2cDiscParsed = { cid: number; did: number };\n\nexport function parseD2cDisc(xml: string): D2cDiscParsed | undefined {\n const m = /<D2C_DISC>([\\s\\S]*?)<\\/D2C_DISC>/.exec(xml);\n if (!m) return undefined;\n const body = m[1] ?? \"\";\n const cid = /<cid>(-?\\d+)<\\/cid>/.exec(body)?.[1];\n const did = /<did>(-?\\d+)<\\/did>/.exec(body)?.[1];\n if (cid == null || did == null) return undefined;\n return { cid: Number(cid), did: Number(did) };\n}\n\nexport type D2cHbParsed = { cid: number; did: number };\n\nexport function parseD2cHb(xml: string): D2cHbParsed | undefined {\n const m = /<D2C_HB>([\\s\\S]*?)<\\/D2C_HB>/.exec(xml);\n if (!m) return undefined;\n const body = m[1] ?? \"\";\n const cid = /<cid>(-?\\d+)<\\/cid>/.exec(body)?.[1];\n const did = /<did>(-?\\d+)<\\/did>/.exec(body)?.[1];\n if (cid == null || did == null) return undefined;\n return { cid: Number(cid), did: Number(did) };\n}\n\n","export type LogLevel = \"silent\" | \"error\" | \"warn\" | \"info\" | \"debug\";\n\nexport interface Logger {\n log(message?: unknown, ...optionalParams: unknown[]): void;\n info(message?: unknown, ...optionalParams: unknown[]): void;\n warn(message?: unknown, ...optionalParams: unknown[]): void;\n error(message?: unknown, ...optionalParams: unknown[]): void;\n debug(message?: unknown, ...optionalParams: unknown[]): void;\n\n /** Create a child logger that prefixes messages with the given tag. */\n child?(tag: string): Logger;\n}\n\nexport type LoggerLike = Partial<Logger> | Console;\n\nconst noop = (): void => {\n // intentionally empty\n};\n\nfunction resolveMethod(base: any, name: keyof Logger): ((...args: any[]) => void) {\n const fn = base?.[name];\n if (typeof fn === \"function\") return fn.bind(base);\n\n const fallback = base?.log;\n if (typeof fallback === \"function\") return fallback.bind(base);\n\n return noop;\n}\n\nexport function asLogger(logger?: LoggerLike): Logger {\n const base: any = logger ?? console;\n\n const out: Logger = {\n log: resolveMethod(base, \"log\"),\n info: resolveMethod(base, \"info\"),\n warn: resolveMethod(base, \"warn\"),\n error: resolveMethod(base, \"error\"),\n debug: resolveMethod(base, \"debug\"),\n };\n\n // Add child() only if it's meaningful (keeps object small).\n out.child = (tag: string): Logger => createTaggedLogger(out, tag);\n return out;\n}\n\nexport function createNullLogger(): Logger {\n const out: Logger = {\n log: noop,\n info: noop,\n warn: noop,\n error: noop,\n debug: noop,\n };\n out.child = (): Logger => out;\n return out;\n}\n\nfunction levelToRank(level: LogLevel): number {\n switch (level) {\n case \"silent\":\n return 0;\n case \"error\":\n return 1;\n case \"warn\":\n return 2;\n case \"info\":\n return 3;\n case \"debug\":\n return 4;\n default:\n return 3;\n }\n}\n\nexport function createLogger(options?: {\n base?: LoggerLike;\n level?: LogLevel;\n tag?: string;\n}): Logger {\n const base = asLogger(options?.base);\n const level = options?.level ?? \"info\";\n const tag = options?.tag;\n\n const minRank = levelToRank(level);\n const shouldLog = (msgLevel: LogLevel): boolean => levelToRank(msgLevel) <= minRank && minRank !== 0;\n\n const prefix = tag ? `[${tag}] ` : \"\";\n\n const wrap = (fn: (...args: any[]) => void) => {\n return (message?: unknown, ...optionalParams: unknown[]) => {\n if (typeof message === \"string\") fn(`${prefix}${message}`, ...optionalParams);\n else if (message !== undefined) fn(prefix, message, ...optionalParams);\n else fn(prefix.trimEnd(), ...optionalParams);\n };\n };\n\n const out: Logger = {\n error: shouldLog(\"error\") ? wrap(base.error) : noop,\n warn: shouldLog(\"warn\") ? wrap(base.warn) : noop,\n info: shouldLog(\"info\") ? wrap(base.info) : noop,\n log: shouldLog(\"info\") ? wrap(base.log) : noop,\n debug: shouldLog(\"debug\") ? wrap(base.debug) : noop,\n };\n\n out.child = (childTag: string): Logger => {\n const combined = tag ? `${tag}:${childTag}` : childTag;\n return createLogger({ base, level, tag: combined });\n };\n\n return out;\n}\n\nexport function createTaggedLogger(base: LoggerLike | Logger, tag: string): Logger {\n // Preserve any existing level filtering on \"base\" by not re-wrapping through createLogger().\n const b = (base as Logger).log ? (base as Logger) : asLogger(base as LoggerLike);\n const prefix = `[${tag}] `;\n\n const wrap = (fn: (...args: any[]) => void) => {\n return (message?: unknown, ...optionalParams: unknown[]) => {\n if (typeof message === \"string\") fn(`${prefix}${message}`, ...optionalParams);\n else if (message !== undefined) fn(prefix, message, ...optionalParams);\n else fn(prefix.trimEnd(), ...optionalParams);\n };\n };\n\n const out: Logger = {\n log: wrap(b.log),\n info: wrap(b.info),\n warn: wrap(b.warn),\n error: wrap(b.error),\n debug: wrap(b.debug),\n };\n out.child = (childTag: string): Logger => createTaggedLogger(out, childTag);\n return out;\n}\n\n/**\n * Wrap a base logger so that `debug()` is a no-op unless `enabled`.\n * Useful to route verbosity through `DebugOptions` without touching every callsite.\n */\nexport function createDebugGateLogger(base?: LoggerLike, enabled: boolean = false): Logger {\n const b = asLogger(base);\n const out: Logger = {\n log: b.log,\n info: b.info,\n warn: b.warn,\n error: b.error,\n debug: enabled ? b.debug : noop,\n };\n out.child = (tag: string): Logger => createDebugGateLogger(createTaggedLogger(out, tag), enabled);\n return out;\n}\n\nlet globalLogger: Logger | undefined;\n\nexport function setGlobalLogger(logger: LoggerLike | Logger | undefined): void {\n globalLogger = logger ? (logger as any).log ? (logger as Logger) : asLogger(logger as LoggerLike) : undefined;\n}\n\nexport function getGlobalLogger(): Logger {\n return globalLogger ?? asLogger(console);\n}\n","import { EventEmitter } from \"node:events\";\nimport { randomUUID } from \"node:crypto\";\nimport net from \"node:net\";\nimport { BcUdpStream } from \"../bcudp/BcUdpStream\";\nimport {\n debugLog,\n eventTraceLog,\n normalizeDebugOptions,\n recordingsTraceLog,\n talkTraceLog,\n traceLog,\n type DebugConfig,\n type DebugOptions,\n type Logger,\n} from \"../debug/DebugConfig\";\nimport { createDebugGateLogger } from \"../logging/logger\";\nimport {\n BC_CLASS_LEGACY,\n BC_CLASS_FILE_DOWNLOAD,\n BC_CLASS_MODERN_24,\n BC_CMD_ID_CHANNEL_INFO_ALL,\n BC_CMD_ID_FILE_INFO_LIST_CLOSE,\n BC_CMD_ID_FILE_INFO_LIST_REPLAY,\n BC_CMD_ID_FILE_INFO_LIST_DOWNLOAD,\n BC_CMD_ID_FILE_INFO_LIST_GET,\n BC_CMD_ID_FILE_INFO_LIST_OPEN,\n BC_CMD_ID_FIND_REC_VIDEO_CLOSE,\n BC_CMD_ID_FIND_REC_VIDEO_GET,\n BC_CMD_ID_FIND_REC_VIDEO_OPEN,\n BC_CMD_ID_PING,\n BC_CMD_ID_TALK,\n BC_CMD_ID_TALK_ABILITY,\n BC_CMD_ID_TALK_CONFIG,\n BC_CMD_ID_TALK_RESET,\n BC_CMD_ID_UDP_KEEP_ALIVE,\n BC_TCP_DEFAULT_PORT,\n} from \"../protocol/constants\";\nimport {\n aesDecrypt,\n aesEncrypt,\n bcDecrypt,\n bcEncrypt,\n deriveAesKey,\n md5StrModern,\n type EncryptionProtocol,\n} from \"../protocol/crypto\";\nimport {\n BaichuanFrameParser,\n encodeHeader,\n type BaichuanFrame,\n} from \"../protocol/framing\";\nimport {\n buildBinaryExtensionXml,\n buildChannelExtensionXml,\n buildLoginXml,\n getXmlText,\n} from \"../protocol/xml\";\nimport type { ReolinkEvent } from \"../reolink/baichuan/types\";\nexport type { Logger };\n\nfunction isTalkCmd(cmdId: number): boolean {\n return (\n cmdId === BC_CMD_ID_TALK_ABILITY ||\n cmdId === BC_CMD_ID_TALK_RESET ||\n cmdId === BC_CMD_ID_TALK_CONFIG ||\n cmdId === BC_CMD_ID_TALK\n );\n}\n\nexport type BaichuanClientOptions = {\n host: string;\n port?: number;\n username: string;\n password: string;\n /** UID used for BCUDP discovery (typical for battery cameras). Required for `transport: \"udp\"` and UDP fallback in `auto`. */\n uid?: string;\n /**\n * UDP discovery method to use when connecting via `uid`.\n * Default: `local-broadcast` (LAN broadcast). Use `local-direct` to try unicast to the known host first.\n * Use `remote`/`map`/`relay` for P2P discovery via Reolink servers.\n */\n udpDiscoveryMethod?:\n | \"local-broadcast\"\n | \"local-direct\"\n | \"remote\"\n | \"map\"\n | \"relay\";\n /**\n * For NVR: logical channel index (0-based).\n * For standalone cameras: usually 0.\n */\n channel?: number;\n /** Structured debug/tracing/dump options. */\n debugOptions?: DebugOptions;\n /** Logger instance (e.g. console). If provided, debug logs will be sent here. */\n logger?: Logger;\n\n /**\n * Enable periodic polling used by higher-level APIs (e.g. simple motion/AI events).\n *\n * Default: false (push events only).\n */\n enableStatePolling?: boolean;\n\n /**\n * Idle disconnect.\n *\n * When enabled, the client will close its socket after a period of *user inactivity*\n * (no explicit API calls), as long as there are:\n * - no active video subscriptions on this client\n * - no in-flight requests\n * - no active permits\n *\n * Useful mainly for battery/BCUDP cameras to reduce the chance of keeping them awake.\n */\n idleDisconnect?: boolean;\n\n /** Idle timeout used when `idleDisconnect` is enabled. Default: 30s. */\n idleDisconnectTimeoutMs?: number;\n /**\n * Transport to use:\n * - `tcp`: Baichuan TCP (typical for wired cameras)\n * - `udp`: BCUDP (typical for battery cameras)\n * - `auto`: try `tcp`, then fallback to `udp`\n */\n transport?: \"tcp\" | \"udp\" | \"auto\";\n};\n\nexport type MaxEncryption = \"none\" | \"bc\" | \"aes\" | \"full_aes\";\n\ntype PendingKey = `${number}:${number}`; // cmdId:msgNum\n\nexport class BaichuanClient extends EventEmitter<{\n frame: [BaichuanFrame];\n push: [BaichuanFrame];\n close: [];\n error: [Error];\n debug: [string, unknown?];\n event: [ReolinkEvent]; // Parsed events (motion/AI)\n channelInfo: [string]; // Channel info XML from cmd_id 145 push\n}> {\n /**\n * Process-wide streaming activity registry.\n *\n * Why this exists:\n * - Consumers may create multiple BaichuanClient instances per device:\n * one for control/polling and one for streaming.\n * - Passive sleep inference should treat the device as awake while ANY client is actively streaming,\n * even if the current client instance is idle/disconnected.\n */\n private static readonly streamingRegistry = new Map<\n string,\n { activeStreamClients: number }\n >();\n\n private readonly opts: BaichuanClientOptions;\n private readonly debugCfg: DebugConfig;\n private readonly logger: Logger;\n\n private tcpSocket: net.Socket | undefined;\n private socketSessionId: string | undefined;\n // Default header channelId to use for host-scoped commands (when `channel` is omitted).\n // Some NVR/HomeHub firmwares use channelId=0 for host operations (PCAP-observed), while others use 250.\n private hostChannelId = 250;\n\n isStatePollingEnabled(): boolean {\n return this.opts.enableStatePolling ?? false;\n }\n private udpSocket: BcUdpStream | undefined;\n private transport: \"tcp\" | \"udp\" = \"tcp\";\n private readonly parser = new BaichuanFrameParser();\n private readonly pending = new Map<\n PendingKey,\n { resolve: (f: BaichuanFrame) => void; reject: (e: Error) => void }\n >();\n private socketClosed = false;\n\n private pendingCloseInfo:\n | {\n atMs: number;\n reason: string;\n }\n | undefined;\n\n private lastDisconnectInfo:\n | {\n atMs: number;\n transport: \"tcp\" | \"udp\";\n voluntary: boolean;\n reason: string;\n }\n | undefined;\n\n private msgNum = 0;\n loggedIn = false; // Public to allow ReolinkBaichuanApi to check login status\n subscribed = false; // Public to allow ReolinkBaichuanApi to check subscription status\n\n private keepAliveTimer: NodeJS.Timeout | undefined;\n private keepAlivePingInFlight = false;\n // Battery/BCUDP cameras may actively terminate sessions (D2C_DISC) when overwhelmed\n // or sleeping. Without a cooldown, callers can end up in tight reconnect loops.\n private udpReconnectCooldownUntilMs = 0;\n private udpReconnectBackoffMs = 0;\n private udpReconnectLastD2cDiscAtMs = 0;\n\n private idleDisconnectTimer: NodeJS.Timeout | undefined;\n private lastUserActivityAtMs: number | undefined;\n private permitSeq = 1;\n private readonly permits = new Map<\n number,\n {\n timer: NodeJS.Timeout | undefined;\n untilMs: number;\n reason: string | undefined;\n }\n >();\n\n private lastRxAtMs: number | undefined;\n private lastTxAtMs: number | undefined;\n private lastRxInfo:\n | {\n atMs: number;\n cmdId: number;\n responseCode: number;\n msgNum: number;\n channelId: number;\n streamType: number;\n }\n | undefined;\n\n private lastTxInfo:\n | {\n atMs: number;\n cmdId: number;\n responseCode: number;\n msgNum: number;\n channelId: number;\n streamType: number;\n }\n | undefined;\n\n // Ring-buffer of the last received frames (metadata only). Used for sleep inference heuristics.\n private readonly rxHistory: Array<{\n atMs: number;\n cmdId: number;\n responseCode: number;\n msgNum: number;\n channelId: number;\n streamType: number;\n }> = [];\n\n // Ring-buffer of the last transmitted frames (metadata only). Used for sleep inference heuristics.\n private readonly txHistory: Array<{\n atMs: number;\n cmdId: number;\n responseCode: number;\n msgNum: number;\n channelId: number;\n streamType: number;\n }> = [];\n\n // Recording-related command IDs (FileInfoList + findAlarmVideo).\n private static readonly recordingCmdIds = new Set<number>([\n BC_CMD_ID_FILE_INFO_LIST_DOWNLOAD,\n BC_CMD_ID_FILE_INFO_LIST_OPEN,\n BC_CMD_ID_FILE_INFO_LIST_GET,\n BC_CMD_ID_FILE_INFO_LIST_CLOSE,\n BC_CMD_ID_FIND_REC_VIDEO_OPEN,\n BC_CMD_ID_FIND_REC_VIDEO_GET,\n BC_CMD_ID_FIND_REC_VIDEO_CLOSE,\n ]);\n\n enc: EncryptionProtocol = { kind: \"none\" }; // Public to allow ReolinkBaichuanApi to access for audio decryption\n private nonce?: string;\n\n // Video stream subscriptions: map of cmdId -> Set of msgNum that are subscribed\n private videoSubscriptions = new Map<number, Set<number>>();\n\n // Tracks whether THIS client currently contributes to the global streaming registry.\n private contributesToGlobalStreamingRegistry = false;\n\n // Throttled per-stream frame tracing (rx cmd_id=3 stream frames can be extremely chatty).\n private streamTraceStats = new Map<\n number,\n { lastLogMs: number; frames: number }\n >();\n\n // Throttled global RX cmd tracing (useful to debug missing pushes/events).\n private rxCmdTraceStats = new Map<\n number,\n { lastLogMs: number; frames: number }\n >();\n\n // AlarmEventList (cmdId=33) can be very chatty (often sent every second).\n // Track last per-channel alarm state so we only emit on transitions.\n private readonly alarmEventState = new Map<\n number,\n {\n motionActive: boolean;\n visitorActive: boolean;\n dayNightActive: boolean;\n aiTypeToken: string; // lowercased token, or \"none\"\n aiFlag: boolean;\n aiTimeStamp: number | undefined; // numeric timeStamp from event payload (often 0/undefined when idle)\n }\n >();\n\n constructor(options: BaichuanClientOptions) {\n super();\n this.opts = options;\n this.debugCfg = normalizeDebugOptions(options.debugOptions);\n // Centralized verbosity control: treat `.debug()` as opt-in.\n this.logger = createDebugGateLogger(\n options.logger,\n this.debugCfg.general ||\n this.debugCfg.traceNativeStream ||\n this.debugCfg.traceRecordings ||\n this.debugCfg.traceTalk ||\n this.debugCfg.traceEvents ||\n this.debugCfg.debugRtsp,\n );\n\n // Ensure socket/transport errors never crash the process by default.\n // Consumers can still add their own `error` listeners.\n this.on(\"error\", (err) => {\n this.logFixed(\"error\", {\n message: err?.message ?? String(err),\n code: (err as any)?.code,\n });\n });\n // this.logger.log(\"BaichuanClient constructor\", { options, dgfg: this.debugCfg });\n }\n\n private newSocketSessionId(transport: \"tcp\" | \"udp\"): string {\n // Short, log-friendly id; uniqueness is best-effort.\n const short = randomUUID().split(\"-\")[0] ?? randomUUID().slice(0, 8);\n return `${transport}-${short}`;\n }\n\n private logFixed(event: string, data?: unknown): void {\n const prefix = \"[BaichuanClient]\";\n const msg = `${prefix} ${event}`;\n const l: any = this.logger as any;\n\n if (typeof l.info === \"function\") {\n l.info(msg, data);\n return;\n }\n if (typeof l.log === \"function\") {\n l.log(msg, data);\n return;\n }\n if (typeof l.warn === \"function\") {\n l.warn(msg, data);\n }\n }\n\n private getIdleDisconnectTimeoutMs(): number {\n const v = this.opts.idleDisconnectTimeoutMs;\n if (typeof v === \"number\" && Number.isFinite(v) && v > 0) return v;\n return 30_000;\n }\n\n private isIdleDisconnectEnabled(): boolean {\n return this.opts.idleDisconnect === true;\n }\n\n private clearIdleDisconnectTimer(): void {\n if (!this.idleDisconnectTimer) return;\n clearTimeout(this.idleDisconnectTimer);\n this.idleDisconnectTimer = undefined;\n }\n\n private touchUserActivity(reason: string): void {\n this.lastUserActivityAtMs = Date.now();\n this.logDebug(\"user_activity\", { reason, atMs: this.lastUserActivityAtMs });\n }\n\n private isIdleDisconnectEligibleNow(): boolean {\n if (!this.isIdleDisconnectEnabled()) return false;\n if (!this.isSocketConnected()) return false;\n if (this.pending.size > 0) return false;\n if (this.hasActiveVideoSubscriptionsInternal()) return false;\n if (this.permits.size > 0) return false;\n return true;\n }\n\n private kickIdleDisconnectTimer(): void {\n if (!this.isIdleDisconnectEnabled()) return;\n this.clearIdleDisconnectTimer();\n\n if (!this.isIdleDisconnectEligibleNow()) return;\n if (this.lastUserActivityAtMs == null) return;\n\n const timeoutMs = this.getIdleDisconnectTimeoutMs();\n const elapsedMs = Date.now() - this.lastUserActivityAtMs;\n const delayMs = Math.max(0, timeoutMs - elapsedMs);\n\n this.idleDisconnectTimer = setTimeout(() => {\n try {\n if (!this.isIdleDisconnectEligibleNow()) return;\n if (this.lastUserActivityAtMs == null) return;\n const elapsed2 = Date.now() - this.lastUserActivityAtMs;\n if (elapsed2 < timeoutMs) {\n this.kickIdleDisconnectTimer();\n return;\n }\n const sid = this.socketSessionId;\n const shortUid = this.opts.uid\n ? this.opts.uid.substring(0, 5)\n : undefined;\n this.logDebug(\"idle_disconnect\", {\n elapsedMs: elapsed2,\n timeoutMs,\n transport: this.transport,\n host: this.opts.host,\n sid,\n uid: shortUid,\n });\n this.logFixed(\"idle_disconnect\", {\n elapsedMs: elapsed2,\n timeoutMs,\n transport: this.transport,\n host: this.opts.host,\n sid,\n uid: shortUid,\n });\n void this.close({ reason: \"idle_disconnect\" });\n } catch (e) {\n this.logDebug(\"idle_disconnect_error\", e);\n }\n }, delayMs);\n this.idleDisconnectTimer.unref?.();\n }\n\n /**\n * Best-effort request to drop an idle BCUDP connection soon.\n *\n * This is primarily for battery cameras: after a stream teardown, we want to\n * close the UDP session quickly (when truly idle) without force-closing shared\n * connections that may still be in use.\n */\n requestIdleDisconnectSoon(reason = \"requested\", graceMs = 0): void {\n try {\n if (!this.isIdleDisconnectEnabled()) return;\n if (this.transport !== \"udp\") return;\n\n const timeoutMs = this.getIdleDisconnectTimeoutMs();\n const g = Math.max(0, Math.floor(graceMs));\n\n // Make the idle timer eligible as soon as possible. The eligibility check\n // still ensures no pending requests / video subscriptions / permits.\n this.lastUserActivityAtMs = Date.now() - timeoutMs + g;\n const sid = this.socketSessionId;\n const shortUid = this.opts.uid\n ? this.opts.uid.substring(0, 5)\n : undefined;\n this.logDebug(\"idle_disconnect_requested\", {\n reason,\n graceMs: g,\n timeoutMs,\n host: this.opts.host,\n sid,\n uid: shortUid,\n });\n this.kickIdleDisconnectTimer();\n } catch {\n // ignore\n }\n }\n\n /**\n * Metadata about the last TCP/UDP disconnect.\n * Useful for higher-level heuristics (e.g. disconnect storm handling).\n */\n getLastDisconnectInfo():\n | {\n atMs: number;\n transport: \"tcp\" | \"udp\";\n voluntary: boolean;\n reason: string;\n }\n | undefined {\n return this.lastDisconnectInfo;\n }\n\n /**\n * Acquire a temporary permit to keep the connection open.\n *\n * Returns a release function.\n */\n acquirePermit(\n holdMs = this.getIdleDisconnectTimeoutMs(),\n reason?: string,\n ): () => void {\n const ms = Math.max(0, Math.floor(holdMs));\n const id = this.permitSeq++;\n const untilMs = Date.now() + ms;\n\n let timer: NodeJS.Timeout | undefined;\n if (ms > 0) {\n timer = setTimeout(() => this.releasePermit(id, \"timeout\"), ms);\n timer.unref?.();\n }\n\n this.permits.set(id, { timer, untilMs, reason });\n this.logDebug(\"permit_acquired\", {\n id,\n holdMs: ms,\n untilMs,\n reason,\n permits: this.permits.size,\n });\n\n // A permit disables idle disconnect.\n this.clearIdleDisconnectTimer();\n\n return () => this.releasePermit(id, \"manual\");\n }\n\n private releasePermit(id: number, how: \"manual\" | \"timeout\" | \"close\"): void {\n const p = this.permits.get(id);\n if (!p) return;\n if (p.timer) clearTimeout(p.timer);\n this.permits.delete(id);\n this.logDebug(\"permit_released\", { id, how, permits: this.permits.size });\n this.kickIdleDisconnectTimer();\n }\n\n private getDeviceRegistryKey(): string {\n // Prefer UID when available (BCUDP/battery), but still include host as a safety net.\n const uid = (this.opts.uid ?? \"\").trim().toUpperCase();\n const host = (this.opts.host ?? \"\").trim();\n const channel = this.opts.channel ?? 0;\n return `${host}|${uid}|${channel}`;\n }\n\n private recomputeGlobalStreamingContribution(): void {\n const shouldContribute = this.hasActiveVideoSubscriptionsInternal();\n if (shouldContribute === this.contributesToGlobalStreamingRegistry) return;\n\n const key = this.getDeviceRegistryKey();\n const cur = BaichuanClient.streamingRegistry.get(key) ?? {\n activeStreamClients: 0,\n };\n const nextCount = Math.max(\n 0,\n cur.activeStreamClients + (shouldContribute ? 1 : -1),\n );\n if (nextCount === 0) BaichuanClient.streamingRegistry.delete(key);\n else\n BaichuanClient.streamingRegistry.set(key, {\n activeStreamClients: nextCount,\n });\n\n this.contributesToGlobalStreamingRegistry = shouldContribute;\n }\n\n /**\n * True if the device should be considered \"awake\" due to active streaming.\n * This includes streaming on other BaichuanClient instances within the same process.\n */\n isDeviceStreamingActive(): boolean {\n if (this.hasActiveVideoSubscriptionsInternal()) return true;\n const key = this.getDeviceRegistryKey();\n const cur = BaichuanClient.streamingRegistry.get(key);\n return (cur?.activeStreamClients ?? 0) > 0;\n }\n\n private logDebug(event: string, data?: unknown): void {\n if (this.debugCfg.general) {\n this.logger.debug(`[BaichuanClient] ${event}`, data);\n this.emit(\"debug\", event, data);\n }\n }\n\n getTransport(): \"tcp\" | \"udp\" {\n return this.transport;\n }\n\n getConfiguredChannel(): number {\n return this.opts.channel ?? 0;\n }\n\n /**\n * Recompute keepalive behavior based on current state.\n *\n * Default policy (UDP):\n * - idle (no subscriptions/streams): no periodic keepalive (allows battery cameras to sleep)\n * - subscribed to events OR streaming: periodic keepalive (improves reliability)\n */\n refreshKeepAlive(): void {\n this.stopKeepAlive();\n if (this.isSocketConnected()) this.startKeepAlive();\n }\n\n /** Timestamp (ms) of the last received Baichuan frame, if any. */\n getLastRxAtMs(): number | undefined {\n return this.lastRxAtMs;\n }\n\n /** Metadata about the last received Baichuan frame, if any. */\n getLastRxInfo():\n | {\n atMs: number;\n cmdId: number;\n responseCode: number;\n msgNum: number;\n channelId: number;\n streamType: number;\n }\n | undefined {\n return this.lastRxInfo;\n }\n\n /** Recent RX frame metadata (newest last). */\n getRxHistory(): ReadonlyArray<{\n atMs: number;\n cmdId: number;\n responseCode: number;\n msgNum: number;\n channelId: number;\n streamType: number;\n }> {\n return this.rxHistory;\n }\n\n /** Timestamp (ms) of the last transmitted Baichuan frame, if any. */\n getLastTxAtMs(): number | undefined {\n return this.lastTxAtMs;\n }\n\n /** Metadata about the last transmitted Baichuan frame, if any. */\n getLastTxInfo():\n | {\n atMs: number;\n cmdId: number;\n responseCode: number;\n msgNum: number;\n channelId: number;\n streamType: number;\n }\n | undefined {\n return this.lastTxInfo;\n }\n\n /** Recent TX frame metadata (newest last). */\n getTxHistory(): ReadonlyArray<{\n atMs: number;\n cmdId: number;\n responseCode: number;\n msgNum: number;\n channelId: number;\n streamType: number;\n }> {\n return this.txHistory;\n }\n\n private recordTx(info: {\n cmdId: number;\n responseCode: number;\n msgNum: number;\n channelId: number;\n streamType: number;\n }): void {\n const now = Date.now();\n this.lastTxAtMs = now;\n this.lastTxInfo = { atMs: now, ...info };\n this.txHistory.push(this.lastTxInfo);\n if (this.txHistory.length > 32) this.txHistory.shift();\n }\n\n /**\n * Sleep/idle inference for battery/BCUDP cameras.\n *\n * With `idleDisconnect` enabled, this library can actively close the BCUDP socket when idle.\n * In that mode we can rely on connection state instead of RX inactivity heuristics.\n *\n * Behavior:\n * - If `idleDisconnect` is enabled: rely on connection state (socket closed => sleeping).\n * - Otherwise: fallback to RX inactivity heuristics (no RX for `idleMs`).\n */\n isProbablySleeping(idleMs = 15_000): boolean {\n if (this.transport !== \"udp\") return false;\n\n // Deterministic mode: if we actively idle-disconnect, sleep maps to socket state.\n if (this.isIdleDisconnectEnabled()) {\n void idleMs; // kept for backward compatibility\n return !this.isSocketConnected();\n }\n\n // Heuristic mode (back-compat): consider the device \"probably sleeping\" when we\n // don't see RX traffic for a while. If we're disconnected, treat as sleeping.\n if (!this.isSocketConnected()) return true;\n if (this.isDeviceStreamingActive()) return false;\n if (this.lastRxAtMs == null) return false;\n return Date.now() - this.lastRxAtMs >= idleMs;\n }\n\n getDebugConfig(): DebugConfig {\n return this.debugCfg;\n }\n\n /**\n * Stable identifier for the current underlying socket session.\n * Useful for correlating logs and diagnosing duplicate event streams.\n */\n getSocketSessionId(): string | undefined {\n return this.socketSessionId;\n }\n\n /**\n * Check if the socket is connected and ready for operations.\n * For TCP: checks if socket exists and is not destroyed.\n * For UDP: checks if socket exists.\n */\n isSocketConnected(): boolean {\n if (this.transport === \"tcp\") {\n return this.tcpSocket !== undefined && !this.tcpSocket.destroyed;\n }\n if (this.transport === \"udp\") {\n return (\n this.udpSocket !== undefined && (this.udpSocket.isConnected?.() ?? true)\n );\n }\n return false;\n }\n\n get username(): string {\n return this.opts.username;\n }\n\n private startKeepAlive(): void {\n if (this.keepAliveTimer) return;\n\n // Defaults:\n // - TCP: prevent idle socket closures by camera/NAT.\n // - UDP/BCUDP: dynamic keepalive.\n // * When subscribed/streaming: send periodic keepalive to keep push/stream reliable.\n // * When idle: do not send keepalive, allowing battery cameras to sleep.\n let interval = 30_000;\n if (this.transport === \"udp\") {\n if (!this.shouldSendUdpKeepAlive()) return;\n interval = 2500;\n }\n\n if (interval <= 0) return;\n\n this.keepAliveTimer = setInterval(() => {\n // BCUDP keepalive behaves differently: some cameras don't reply to the keepalive frame,\n // so waiting for a response would stall the loop.\n if (this.transport === \"udp\") {\n try {\n this.sendUdpKeepAlive();\n } catch (e) {\n this.logDebug(\"udp_keepalive_ping_error\", e);\n }\n return;\n }\n\n // Avoid overlapping pings (they wait for a reply and can take up to timeoutMs).\n if (this.keepAlivePingInFlight) return;\n this.keepAlivePingInFlight = true;\n void (async () => {\n try {\n await this.sendPing();\n } catch (e) {\n this.logDebug(\"keepalive_ping_error\", e);\n } finally {\n this.keepAlivePingInFlight = false;\n }\n })();\n }, interval);\n // Don't keep the Node process alive only for keepalive.\n this.keepAliveTimer.unref?.();\n }\n\n private hasActiveVideoSubscriptionsInternal(): boolean {\n for (const set of this.videoSubscriptions.values()) {\n if (set.size > 0) return true;\n }\n return false;\n }\n\n /** True when there is at least one active Baichuan video subscription (cmdId/msgNum). */\n hasActiveVideoSubscriptions(): boolean {\n return this.hasActiveVideoSubscriptionsInternal();\n }\n\n private shouldSendUdpKeepAlive(): boolean {\n // Only useful when connected via BCUDP and logged in.\n if (this.transport !== \"udp\") return false;\n if (!this.udpSocket) return false;\n if (!this.loggedIn) return false;\n\n // Default: do NOT send periodic keepalive unless we're actively streaming.\n // Battery cameras should be allowed to sleep; we still reply to camera-initiated keepalive frames.\n if (this.hasActiveVideoSubscriptionsInternal()) return true;\n return false;\n }\n\n private sendUdpKeepAlive(): void {\n if (this.transport !== \"udp\") return;\n if (!this.udpSocket) return;\n // Avoid keepalives before login; some firmwares treat them as invalid.\n if (!this.loggedIn) return;\n\n const msgNum = this.nextMsgNum();\n const header = encodeHeader({\n cmdId: BC_CMD_ID_UDP_KEEP_ALIVE,\n bodyLen: 0,\n channelId: 0,\n streamType: 0,\n msgNum,\n responseCode: 0,\n messageClass: BC_CLASS_MODERN_24,\n payloadOffset: 0,\n });\n\n this.logDebug(\"udp_keepalive_ping_tx\", {\n msgNum,\n channelId: 0,\n streamType: 0,\n });\n this.recordTx({\n cmdId: BC_CMD_ID_UDP_KEEP_ALIVE,\n responseCode: 0,\n msgNum,\n channelId: 0,\n streamType: 0,\n });\n this.writeWire(header);\n }\n\n private stopKeepAlive(): void {\n if (this.keepAliveTimer) {\n clearInterval(this.keepAliveTimer);\n this.keepAliveTimer = undefined;\n }\n this.keepAlivePingInFlight = false;\n }\n\n private async sendPing(): Promise<void> {\n // Only send if connected\n if (!this.tcpSocket && !this.udpSocket) return;\n\n // Avoid pings before login; some firmwares treat them as invalid and may drop the session.\n if (!this.loggedIn) return;\n\n // TCP keepalive: MSG_ID_PING (93)\n try {\n // We use sendFrame which waits for response.\n // If it times out, it's fine, we just log it.\n await this.sendFrame({\n cmdId: BC_CMD_ID_PING,\n channel: this.opts.channel ?? 0,\n channelIdOverride: 0, // Force 0-based channel ID for Ping\n messageClass: BC_CLASS_MODERN_24,\n streamType: 0,\n extensionXml: \"\", // Keepalive has empty body\n internal: true,\n });\n } catch (e) {\n // Ignore errors, just log debug\n this.logDebug(\"keepalive_ping_failed\", e);\n }\n }\n\n async connect(): Promise<void> {\n const desired = this.opts.transport ?? \"tcp\";\n if (desired === \"tcp\") {\n await this.connectTcp();\n return;\n }\n if (desired === \"udp\") {\n await this.waitForUdpReconnectCooldown();\n await this.connectUdp();\n return;\n }\n // auto: try TCP first, then fallback to UDP\n try {\n // Use a timeout for TCP discovery (TCP_WAIT = 4 seconds)\n // We use Promise.race to timeout TCP connection attempt\n await Promise.race([\n this.connectTcp(),\n new Promise<void>((_, reject) =>\n setTimeout(\n () =>\n reject(new Error(\"TCP connection timeout (falling back to UDP)\")),\n 4000,\n ),\n ),\n ]);\n } catch (e) {\n this.logDebug(\"auto:tcp_failed\", e);\n // Fallback to UDP discovery\n // Requires UID.\n if (!this.opts.uid) {\n throw new Error(\n \"TCP connection failed and UDP fallback requires `options.uid` (BCUDP discovery UID).\",\n );\n }\n await this.waitForUdpReconnectCooldown();\n await this.connectUdp();\n }\n }\n\n private async waitForUdpReconnectCooldown(): Promise<void> {\n const now = Date.now();\n const waitMs = this.udpReconnectCooldownUntilMs - now;\n if (waitMs <= 0) return;\n const sid = this.socketSessionId;\n const shortUid = this.opts.uid ? this.opts.uid.substring(0, 5) : undefined;\n this.logFixed(\"udp_reconnect_cooldown\", {\n transport: \"udp\",\n host: this.opts.host,\n sid,\n uid: shortUid,\n waitMs,\n });\n await new Promise<void>((resolve) => setTimeout(resolve, waitMs));\n }\n\n private async connectTcp(): Promise<void> {\n if (this.tcpSocket && !this.tcpSocket.destroyed) {\n this.transport = \"tcp\";\n return;\n }\n const port = this.opts.port ?? BC_TCP_DEFAULT_PORT;\n const sock = net.createConnection({ host: this.opts.host, port });\n this.tcpSocket = sock;\n this.transport = \"tcp\";\n this.socketClosed = false;\n this.socketSessionId = this.newSocketSessionId(\"tcp\");\n\n // TCP keep-alive at OS level (helps prevent idle disconnects from NAT/camera).\n try {\n sock.setKeepAlive(true, 30_000);\n } catch (e) {\n this.logDebug(\"tcp_setKeepAlive_failed\", e);\n }\n\n sock.on(\"data\", (chunk) => {\n const frames = this.parser.push(chunk);\n for (const f of frames) this.handleFrame(f);\n });\n sock.on(\"close\", () => {\n const sid = this.socketSessionId;\n // Ensure socket is completely destroyed and listeners are removed\n if (sock === this.tcpSocket) {\n this.tcpSocket = undefined;\n }\n\n // Remove all listeners to prevent memory leaks\n sock.removeAllListeners();\n\n // Ensure socket is destroyed\n if (!sock.destroyed) {\n sock.destroy();\n }\n\n this.stopKeepAlive();\n this.socketClosed = true;\n\n const pending = this.pendingCloseInfo;\n this.pendingCloseInfo = undefined;\n this.lastDisconnectInfo = {\n atMs: Date.now(),\n transport: \"tcp\",\n voluntary: pending != null,\n reason: pending?.reason ?? \"socket_closed\",\n };\n\n const tcpDisconnectParts: string[] = [\n `transport=tcp`,\n `host=${this.opts.host}`,\n ];\n if (sid) tcpDisconnectParts.push(`sid=${sid}`);\n const tcpPort = this.opts.port ?? BC_TCP_DEFAULT_PORT;\n if (tcpPort != null) tcpDisconnectParts.push(`port=${tcpPort}`);\n if (this.lastRxInfo?.cmdId != null)\n tcpDisconnectParts.push(`lastRxCmdId=${this.lastRxInfo.cmdId}`);\n if (this.lastTxInfo?.cmdId != null)\n tcpDisconnectParts.push(`lastTxCmdId=${this.lastTxInfo.cmdId}`);\n this.logFixed(\"disconnected\", tcpDisconnectParts.join(\" \"));\n\n // End socket session.\n this.socketSessionId = undefined;\n\n // Reset state flags\n this.loggedIn = false;\n this.subscribed = false;\n\n this.emit(\"close\");\n // Reject all pending promises asynchronously to allow catch handlers to be attached\n // This prevents unhandled rejections when the socket closes\n const pendingEntries = Array.from(this.pending.entries());\n this.pending.clear();\n for (const [, p] of pendingEntries) {\n // Use setImmediate to allow catch handlers to be attached before rejection\n // setImmediate runs after the current event loop, giving catch handlers time to attach\n setImmediate(() => {\n try {\n p.reject(new Error(\"Baichuan socket closed\"));\n } catch (e) {\n // Ignore errors from rejecting promises (they may already be handled)\n }\n });\n }\n });\n sock.on(\"error\", (err) => this.emit(\"error\", err));\n\n await new Promise<void>((resolve, reject) => {\n sock.once(\"connect\", () => resolve());\n sock.once(\"error\", (e) => reject(e));\n });\n\n const sid = this.socketSessionId;\n // NOTE: `remote` here refers to the local endpoint used to initialize the socket (client side).\n // The camera endpoint is logged as `peer`.\n const localAddress = sock.localAddress;\n const localFamily = sock.localFamily;\n const localPort = sock.localPort;\n const remote = localAddress\n ? `${localAddress}${localFamily ? `/${localFamily}` : \"\"}${localPort ? `:${localPort}` : \"\"}`\n : undefined;\n\n const peerAddress = sock.remoteAddress;\n const peerFamily = sock.remoteFamily;\n const peerPort = sock.remotePort;\n const peer = peerAddress\n ? `${peerAddress}${peerFamily ? `/${peerFamily}` : \"\"}${peerPort ? `:${peerPort}` : \"\"}`\n : undefined;\n this.logFixed(\n \"connected\",\n `transport=tcp host=${this.opts.host} port=${port}` +\n `${sid ? ` sid=${sid}` : \"\"}` +\n `${remote ? ` remote=${remote}` : \"\"}` +\n `${peer ? ` peer=${peer}` : \"\"}`,\n );\n\n this.startKeepAlive();\n this.kickIdleDisconnectTimer();\n }\n\n private async connectUdp(): Promise<void> {\n if (this.udpSocket) {\n // If the stream object exists but the underlying socket/remote is gone (e.g. after D2C_DISC),\n // drop it so we can create a fresh connection.\n if (this.udpSocket.isConnected?.()) {\n this.transport = \"udp\";\n return;\n }\n try {\n await this.udpSocket.close();\n } catch {\n // ignore\n }\n this.udpSocket = undefined;\n }\n if (!this.opts.uid) {\n throw new Error(\n \"Baichuan UDP requested but `options.uid` is not set (required for BCUDP discovery).\",\n );\n }\n const sock = new BcUdpStream({\n mode: \"uid\",\n uid: this.opts.uid,\n // If the camera IP/hostname is known, allow local-direct to try unicast before broadcast.\n ...(this.opts.host?.trim() ? { directHost: this.opts.host.trim() } : {}),\n ...(this.opts.udpDiscoveryMethod\n ? { discoveryMethod: this.opts.udpDiscoveryMethod }\n : {}),\n });\n this.udpSocket = sock;\n this.transport = \"udp\";\n this.socketClosed = false;\n this.socketSessionId = this.newSocketSessionId(\"udp\");\n\n sock.on(\"data\", (chunk) => {\n const frames = this.parser.push(chunk);\n for (const f of frames) this.handleFrame(f);\n });\n sock.on(\"close\", () => {\n const sid = this.socketSessionId;\n // Ensure socket is completely cleaned up\n if (sock === this.udpSocket) {\n this.udpSocket = undefined;\n }\n\n // Remove all listeners to prevent memory leaks\n if (sock.removeAllListeners) {\n sock.removeAllListeners();\n }\n\n this.stopKeepAlive();\n this.socketClosed = true;\n\n const pending = this.pendingCloseInfo;\n this.pendingCloseInfo = undefined;\n this.lastDisconnectInfo = {\n atMs: Date.now(),\n transport: \"udp\",\n voluntary: pending != null,\n reason: pending?.reason ?? \"socket_closed\",\n };\n\n const udpDisconnectParts: string[] = [\n `transport=udp`,\n `host=${this.opts.host}`,\n ];\n if (sid) udpDisconnectParts.push(`sid=${sid}`);\n if (this.opts.uid) {\n const shortUid = this.opts.uid.substring(0, 5);\n udpDisconnectParts.push(`uid=${shortUid}`);\n }\n if (this.lastRxInfo?.cmdId != null)\n udpDisconnectParts.push(`lastRxCmdId=${this.lastRxInfo.cmdId}`);\n if (this.lastTxInfo?.cmdId != null)\n udpDisconnectParts.push(`lastTxCmdId=${this.lastTxInfo.cmdId}`);\n this.logFixed(\"disconnected\", udpDisconnectParts.join(\" \"));\n\n // End socket session.\n this.socketSessionId = undefined;\n // Mark session state as invalid; a new connect/login is required.\n this.loggedIn = false;\n this.subscribed = false;\n // If this client was contributing streaming state, clear it.\n if (this.contributesToGlobalStreamingRegistry) {\n this.videoSubscriptions.clear();\n this.recomputeGlobalStreamingContribution();\n }\n this.emit(\"close\");\n // Reject all pending promises asynchronously to allow catch handlers to be attached\n // This prevents unhandled rejections when the socket closes\n const pendingEntries = Array.from(this.pending.entries());\n this.pending.clear();\n for (const [, p] of pendingEntries) {\n // Use setImmediate to allow catch handlers to be attached before rejection\n // setImmediate runs after the current event loop, giving catch handlers time to attach\n setImmediate(() => {\n try {\n p.reject(new Error(\"Baichuan UDP stream closed\"));\n } catch (e) {\n // Ignore errors from rejecting promises (they may already be handled)\n }\n });\n }\n });\n sock.on(\"error\", (err) => {\n // If the camera terminates the BCUDP session (D2C_DISC), the stream will close.\n // Make sure we don't keep a stale handle around.\n if (err?.message?.includes(\"D2C_DISC\")) {\n const now = Date.now();\n const sid = this.socketSessionId;\n const shortUid = this.opts.uid\n ? this.opts.uid.substring(0, 5)\n : undefined;\n this.logFixed(\"d2c_disc\", {\n transport: \"udp\",\n host: this.opts.host,\n sid,\n uid: shortUid,\n message: err.message,\n });\n\n // Apply exponential backoff to avoid reconnect storms.\n // If D2C_DISC repeats frequently, ramp up cooldown to give the camera time.\n const withinWindow = now - this.udpReconnectLastD2cDiscAtMs < 60_000;\n const baseMs = 2_000;\n const maxMs = 30_000;\n const nextBackoffMs = withinWindow\n ? Math.min(\n maxMs,\n Math.max(\n baseMs,\n this.udpReconnectBackoffMs > 0\n ? this.udpReconnectBackoffMs * 2\n : baseMs,\n ),\n )\n : baseMs;\n this.udpReconnectLastD2cDiscAtMs = now;\n this.udpReconnectBackoffMs = nextBackoffMs;\n this.udpReconnectCooldownUntilMs = Math.max(\n this.udpReconnectCooldownUntilMs,\n now + nextBackoffMs,\n );\n this.logFixed(\"d2c_disc_backoff\", {\n transport: \"udp\",\n host: this.opts.host,\n sid,\n uid: shortUid,\n backoffMs: nextBackoffMs,\n cooldownUntilMs: this.udpReconnectCooldownUntilMs,\n });\n\n this.stopKeepAlive();\n this.loggedIn = false;\n this.subscribed = false;\n // Camera terminated the session; clear streaming contribution for this client.\n if (this.contributesToGlobalStreamingRegistry) {\n this.videoSubscriptions.clear();\n this.recomputeGlobalStreamingContribution();\n }\n }\n this.emit(\"error\", err);\n });\n\n // Forward BcUdpStream debug events\n sock.on(\"debug\", (event: string, data?: unknown) => {\n this.logDebug(`udp_${event}`, data);\n });\n\n await sock.connect();\n\n const shortUid = this.opts.uid ? this.opts.uid.substring(0, 5) : \"\";\n const udpDiscoveryMethod =\n (this.opts.udpDiscoveryMethod as string | undefined) ?? \"local-direct\";\n const sid = this.socketSessionId;\n const udpRemoteHost: string | undefined = (sock as any)?.remote?.host;\n this.logFixed(\n \"connected\",\n `transport=udp host=${this.opts.host}${sid ? ` sid=${sid}` : \"\"}${udpRemoteHost ? ` remote=${udpRemoteHost}` : \"\"} uid=${shortUid} udpDiscoveryMethod=${udpDiscoveryMethod}`,\n );\n this.startKeepAlive();\n this.kickIdleDisconnectTimer();\n }\n\n async close(options?: { reason?: string }): Promise<void> {\n const hasSocket = Boolean(\n (this.tcpSocket && !this.tcpSocket.destroyed) || this.udpSocket,\n );\n const reason = (options?.reason ?? \"manual_close\").trim() || \"manual_close\";\n if (hasSocket) {\n this.pendingCloseInfo = {\n atMs: Date.now(),\n reason,\n };\n }\n\n // Always log close triggers (even if the socket close event is swallowed by the runtime).\n // This helps diagnose cases where we see repeated \"connected ... sid=...\" without an obvious disconnect.\n try {\n const sid = this.socketSessionId;\n const transport = this.transport;\n const host = this.opts.host;\n const port = this.opts.port ?? BC_TCP_DEFAULT_PORT;\n const shortUid = this.opts.uid\n ? this.opts.uid.substring(0, 5)\n : undefined;\n this.logFixed(\"closing\", {\n transport,\n host,\n ...(transport === \"tcp\" ? { port } : {}),\n ...(sid ? { sid } : {}),\n ...(shortUid ? { uid: shortUid } : {}),\n reason,\n hasSocket,\n socketConnected: this.isSocketConnected(),\n loggedIn: this.loggedIn,\n subscribed: this.subscribed,\n pending: this.pending.size,\n permits: this.permits.size,\n videoSubscriptions: this.videoSubscriptions.size,\n });\n\n // Optional: emit a trimmed stack trace to locate the caller.\n // Enable via env var to avoid noisy logs in production.\n if ((process.env.BAICHUAN_LOG_CLOSE_STACK ?? \"\").trim() === \"1\") {\n const stack = new Error(\"BaichuanClient.close() stack\").stack;\n if (stack) {\n const lines = stack.split(\"\\n\").slice(0, 10).join(\"\\n\");\n this.logFixed(\"closing_stack\", lines);\n }\n }\n } catch {\n // ignore\n }\n\n this.stopKeepAlive();\n this.clearIdleDisconnectTimer();\n\n // Drop permits on close.\n for (const id of Array.from(this.permits.keys())) {\n this.releasePermit(id, \"close\");\n }\n\n // Ensure we drop any global streaming contribution before tearing down sockets.\n if (this.contributesToGlobalStreamingRegistry) {\n this.videoSubscriptions.clear();\n this.recomputeGlobalStreamingContribution();\n }\n\n // IMPORTANT:\n // Don't remove listeners here: the per-transport socket \"close\" handler is responsible for:\n // - logging \"disconnected\"\n // - clearing socket/session state\n // - rejecting pending requests\n // If we remove listeners here, we lose observability and can leave state inconsistent.\n\n // Trigger TCP close (if any) and wait for the \"close\" event.\n const tcp = this.tcpSocket;\n if (tcp && !tcp.destroyed) {\n try {\n // Best-effort graceful end; most firmwares just close anyway.\n tcp.end();\n } catch {\n // ignore\n }\n try {\n tcp.destroy();\n } catch {\n // ignore\n }\n\n await new Promise<void>((resolve) => {\n if (tcp.destroyed) {\n resolve();\n return;\n }\n tcp.once(\"close\", () => resolve());\n setTimeout(() => resolve(), 250);\n });\n }\n\n // Trigger UDP close (if any). BcUdpStream.close() should emit \"close\" and run our handler.\n const udp = this.udpSocket;\n if (udp) {\n try {\n await udp.close();\n } catch (e) {\n this.logDebug(\"udp_close_error\", e);\n }\n }\n }\n\n private handleFrame(frame: BaichuanFrame): void {\n const now = Date.now();\n this.lastRxAtMs = now;\n this.lastRxInfo = {\n atMs: now,\n cmdId: frame.header.cmdId,\n responseCode: frame.header.responseCode,\n msgNum: frame.header.msgNum,\n channelId: frame.header.channelId,\n streamType: frame.header.streamType,\n };\n\n this.rxHistory.push(this.lastRxInfo);\n if (this.rxHistory.length > 32) this.rxHistory.shift();\n\n // Temporary global RX logging: show every received cmdId.\n // Throttle cmdId=3 (stream) to avoid flooding logs.\n if (this.debugCfg.general) {\n if (frame.header.cmdId === 3) {\n const s = this.rxCmdTraceStats.get(3) ?? { lastLogMs: now, frames: 0 };\n s.frames++;\n if (now - s.lastLogMs >= 1000) {\n debugLog(\n this.debugCfg,\n this.logger,\n \"BaichuanRx\",\n `rx cmdId=3 frames=${s.frames} lastMsgNum=${frame.header.msgNum} lastResponseCode=${frame.header.responseCode} lastChannelId=${frame.header.channelId} lastStreamType=${frame.header.streamType} lastBodyLen=${frame.body.length} lastPayloadLen=${frame.payload.length}`,\n );\n s.lastLogMs = now;\n s.frames = 0;\n }\n this.rxCmdTraceStats.set(3, s);\n } else {\n debugLog(\n this.debugCfg,\n this.logger,\n \"BaichuanRx\",\n `rx cmdId=${frame.header.cmdId} msgNum=${frame.header.msgNum} responseCode=${frame.header.responseCode} channelId=${frame.header.channelId} streamType=${frame.header.streamType} bodyLen=${frame.body.length} payloadLen=${frame.payload.length} payloadOffset=${frame.header.payloadOffset ?? 0}`,\n );\n }\n }\n\n // Battery cameras (BCUDP) expect the client to respond to UDP keep-alive frames.\n // Always reply with response_code=200 using the same msg_num/channel_id/stream_type\n // and do not special-case the incoming response_code.\n // Some firmwares send these with response_code=200 already; we still reply to keep the session alive.\n if (\n this.transport === \"udp\" &&\n frame.header.cmdId === BC_CMD_ID_UDP_KEEP_ALIVE\n ) {\n try {\n const header = encodeHeader({\n cmdId: frame.header.cmdId,\n bodyLen: 0,\n channelId: frame.header.channelId,\n streamType: frame.header.streamType,\n msgNum: frame.header.msgNum,\n responseCode: 200,\n messageClass: frame.header.messageClass,\n payloadOffset: 0,\n });\n\n this.logDebug(\"udp_keepalive_rx\", {\n msgNum: frame.header.msgNum,\n channelId: frame.header.channelId,\n streamType: frame.header.streamType,\n responseCode: frame.header.responseCode,\n });\n this.recordTx({\n cmdId: frame.header.cmdId,\n responseCode: 200,\n msgNum: frame.header.msgNum,\n channelId: frame.header.channelId,\n streamType: frame.header.streamType,\n });\n this.writeWire(header);\n this.logDebug(\"udp_keepalive_tx\", {\n msgNum: frame.header.msgNum,\n channelId: frame.header.channelId,\n streamType: frame.header.streamType,\n });\n } catch (e) {\n // Keepalive failures shouldn't crash the client; log when debug is enabled.\n this.logDebug(\"udp_keepalive_error\", e);\n }\n // Keepalive frames are handled here.\n return;\n }\n\n if (this.debugCfg.traceTalk && isTalkCmd(frame.header.cmdId)) {\n talkTraceLog(\n this.debugCfg,\n this.logger,\n \"BaichuanTalk\",\n `rx cmdId=${frame.header.cmdId} msgNum=${frame.header.msgNum} responseCode=${frame.header.responseCode} channelId=${frame.header.channelId} bodyLen=${frame.body.length} payloadLen=${frame.payload.length} payloadOffset=${frame.header.payloadOffset ?? 0}`,\n );\n }\n\n this.emit(\"frame\", frame);\n\n const key: PendingKey = `${frame.header.cmdId}:${frame.header.msgNum}`;\n const pending = this.pending.get(key);\n if (pending) {\n this.pending.delete(key);\n pending.resolve(frame);\n return;\n }\n\n // Check if this frame matches a video stream subscription\n // Frames with matching cmdId and msgNum\n const subscribedMsgNums = this.videoSubscriptions.get(frame.header.cmdId);\n if (subscribedMsgNums && subscribedMsgNums.size > 0) {\n // If there are active subscriptions for this cmdId (typically MSG_ID_VIDEO=3),\n // emit only frames that match msgNum. This prevents mixing old/parallel streams.\n if (subscribedMsgNums.has(frame.header.msgNum)) {\n if (this.debugCfg.traceNativeStream && frame.header.cmdId === 3) {\n const now = Date.now();\n const key = frame.header.msgNum;\n const s = this.streamTraceStats.get(key) ?? {\n lastLogMs: now,\n frames: 0,\n };\n s.frames++;\n\n // Throttle per-frame logs to keep BCUDP ACK/keepalive responsive.\n if (now - s.lastLogMs >= 500) {\n traceLog(\n this.debugCfg,\n this.logger,\n \"BaichuanTrace\",\n `rx stream frames cmdId=3 msgNum=${frame.header.msgNum} frames=${s.frames} lastChannelId=${frame.header.channelId} lastBodyLen=${frame.body.length} lastPayloadLen=${frame.payload.length} lastPayloadOffset=${frame.header.payloadOffset ?? 0}`,\n );\n s.lastLogMs = now;\n s.frames = 0;\n }\n\n this.streamTraceStats.set(key, s);\n }\n this.emit(\"push\", frame);\n }\n return;\n }\n\n // Handle cmd_id 145 (channel info push from NVR)\n if (frame.header.cmdId === BC_CMD_ID_CHANNEL_INFO_ALL) {\n try {\n // Decrypt the body using the same method as sendXml\n const xml = this.tryDecryptXml(\n frame.body,\n frame.header.channelId,\n this.enc,\n );\n if (xml && xml.startsWith(\"<?xml\")) {\n // Emit a special event with the parsed channel info\n this.emit(\"channelInfo\", xml);\n // this.logFixed(\"channel_info_push\", {\n // cmdId: frame.header.cmdId,\n // msgNum: frame.header.msgNum,\n // channelId: frame.header.channelId,\n // bodyLen: frame.body.length,\n // xmlLen: xml.length,\n // });\n } else {\n this.logDebug(\"channel_info_push_decrypt_failed\", {\n bodyLen: frame.body.length,\n xmlPreview: xml?.substring(0, 100),\n });\n }\n } catch (e) {\n this.logDebug(\"channel_info_push_error\", e);\n }\n }\n\n // No subscription: behave as before (generic push)\n this.emit(\"push\", frame);\n\n // Parse events (cmd_id 33 = AlarmEventList push)\n if (frame.header.cmdId === 33) {\n try {\n const sid = this.socketSessionId;\n const eventLogTag = sid ? `BaichuanEvent sid=${sid}` : \"BaichuanEvent\";\n const events = this.parseEvents(frame);\n for (const event of events) {\n eventTraceLog(\n this.debugCfg,\n this.logger,\n eventLogTag,\n `dispatch cmdId=33 msgNum=${frame.header.msgNum} channelId=${frame.header.channelId} type=${event.type} eventChannel=${event.channel}` +\n (event.type === \"ai\"\n ? ` aiType=${(event.ai as any)?.type ?? \"unknown\"} detected=${(event.ai as any)?.detected ?? \"\"}`\n : \"\") +\n (event.type === \"motion\"\n ? ` source=${(event.motion as any)?.source ?? \"\"}`\n : \"\"),\n );\n this.emit(\"event\", event);\n }\n } catch (error) {\n this.logDebug(\"event_parse_error\", error);\n }\n }\n }\n\n /**\n * Subscribe to video stream frames with a specific cmdId and msgNum.\n *\n * @param cmdId - Command ID to subscribe to (e.g., 3 for MSG_ID_VIDEO)\n * @param msgNum - Message number to subscribe to\n */\n subscribeVideoStream(cmdId: number, msgNum: number): void {\n if (!this.videoSubscriptions.has(cmdId)) {\n this.videoSubscriptions.set(cmdId, new Set());\n }\n this.videoSubscriptions.get(cmdId)!.add(msgNum);\n if (cmdId === 3 && !this.streamTraceStats.has(msgNum)) {\n this.streamTraceStats.set(msgNum, { lastLogMs: Date.now(), frames: 0 });\n }\n\n // Streaming requires keeping the BCUDP session alive.\n if (this.transport === \"udp\") this.refreshKeepAlive();\n\n // Keep global streaming registry in sync.\n this.recomputeGlobalStreamingContribution();\n\n // Re-evaluate idle disconnect eligibility (streaming changes it).\n this.kickIdleDisconnectTimer();\n }\n\n /**\n * Unsubscribe from video stream frames.\n *\n * @param cmdId - Command ID to unsubscribe from\n * @param msgNum - Message number to unsubscribe from (optional, if not provided, unsubscribes from all msgNum for this cmdId)\n */\n unsubscribeVideoStream(cmdId: number, msgNum?: number): void {\n const subscribedMsgNums = this.videoSubscriptions.get(cmdId);\n if (!subscribedMsgNums) return;\n\n if (msgNum !== undefined) {\n subscribedMsgNums.delete(msgNum);\n if (subscribedMsgNums.size === 0) {\n this.videoSubscriptions.delete(cmdId);\n }\n if (cmdId === 3) this.streamTraceStats.delete(msgNum);\n } else {\n this.videoSubscriptions.delete(cmdId);\n if (cmdId === 3) this.streamTraceStats.clear();\n }\n\n if (this.transport === \"udp\") this.refreshKeepAlive();\n\n // Keep global streaming registry in sync.\n this.recomputeGlobalStreamingContribution();\n\n // If streaming stopped, we may now be eligible for idle disconnect.\n this.kickIdleDisconnectTimer();\n }\n\n /**\n * Parses event frame (cmd_id 33) into one or more ReolinkEvent.\n * Primary format: <AlarmEventList><AlarmEvent>...</AlarmEvent>...</AlarmEventList>\n * Fallback format (seen on some firmwares): <Event>...</Event>\n */\n private parseEvents(frame: BaichuanFrame): ReolinkEvent[] {\n const body = frame.body;\n if (body.length === 0) return [];\n\n const sid = this.socketSessionId;\n const eventRawLogTag = sid\n ? `BaichuanEventRaw sid=${sid}`\n : \"BaichuanEventRaw\";\n\n const xml = this.tryDecryptXml(body, frame.header.channelId, this.enc);\n if (!xml || !xml.startsWith(\"<?xml\")) return [];\n\n if (this.debugCfg.traceEvents) {\n const snippet = xml.length > 500 ? `${xml.slice(0, 500)}...` : xml;\n eventTraceLog(\n this.debugCfg,\n this.logger,\n eventRawLogTag,\n `rx cmdId=${frame.header.cmdId} msgNum=${frame.header.msgNum} responseCode=${frame.header.responseCode} channelId=${frame.header.channelId} xml=${JSON.stringify(snippet)}`,\n );\n }\n\n // Default channel from frame header (channelId 250 = host, 1+ = channels)\n const fallbackChannelId = frame.header.channelId;\n const fallbackChannel =\n fallbackChannelId === 250 ? 0 : Math.max(0, fallbackChannelId - 1);\n\n const now = Date.now();\n\n // 1) Format: AlarmEventList\n if (xml.includes(\"<AlarmEventList\")) {\n const out: ReolinkEvent[] = [];\n const alarmEventMatches = xml.matchAll(\n /<AlarmEvent\\b[^>]*>([\\s\\S]*?)<\\/AlarmEvent>/g,\n );\n for (const match of alarmEventMatches) {\n const alarmXml = match[1] ?? \"\";\n const channelText = getXmlText(alarmXml, \"channelId\");\n const channel =\n channelText !== undefined ? Number(channelText) : fallbackChannel;\n const status = (getXmlText(alarmXml, \"status\") ?? \"\").trim();\n const statusUpper = status.toUpperCase();\n\n // Some firmwares may attach AI type in different tag names.\n const aiTypeRaw = (\n getXmlText(alarmXml, \"AItype\") ??\n getXmlText(alarmXml, \"aiType\") ??\n getXmlText(alarmXml, \"aitype\") ??\n \"\"\n ).trim();\n\n // Some firmwares include a numeric timeStamp in AlarmEvent; when present and non-zero it can\n // be used to disambiguate real detections from static configured AI types.\n const timeStampRaw = (\n getXmlText(alarmXml, \"timeStamp\") ??\n getXmlText(alarmXml, \"timestamp\") ??\n \"\"\n ).trim();\n const timeStampNum = timeStampRaw.length\n ? Number(timeStampRaw)\n : undefined;\n const alarmTimeStamp = Number.isFinite(timeStampNum as number)\n ? (timeStampNum as number)\n : undefined;\n\n if (this.debugCfg.traceEvents) {\n eventTraceLog(\n this.debugCfg,\n this.logger,\n eventRawLogTag,\n `AlarmEvent channel=${channel} status=${JSON.stringify(status)} aiType=${JSON.stringify(aiTypeRaw)}`,\n );\n }\n\n // Unlike older implementations, a single AlarmEvent may encode multiple independent states\n // (e.g. motion + ai + visitor). Emit all applicable events.\n\n // Motion inference: treat as motion start when status != \"none\".\n // Battery cams often use status \"other\" for PIR-based motion.\n // IMPORTANT: Do not infer motion solely from AItype when status is \"none\"; some firmwares\n // report the configured AI type even when there is no detection.\n const statusLower = status.toLowerCase();\n const statusTokens = statusLower\n .split(\",\")\n .map((t) => t.trim())\n .filter((t) => t.length > 0);\n\n const visitorActive = statusTokens.includes(\"visitor\");\n const dayNightActive =\n statusTokens.includes(\"dn\") || statusTokens.includes(\"daynight\");\n\n // Default motion heuristic: status != none, excluding known non-motion flags.\n const statusIndicatesMotion = statusTokens.some(\n (t) =>\n t !== \"none\" && t !== \"visitor\" && t !== \"dn\" && t !== \"daynight\",\n );\n const aiTypeTokenRaw = aiTypeRaw\n ? aiTypeRaw\n .split(\",\")\n .map((t) => t.trim())\n .find((t) => t.length > 0 && t.toLowerCase() !== \"none\")\n : undefined;\n\n const aiFlag = statusUpper.includes(\"AI\");\n const aiTypeToken = (\n aiTypeTokenRaw ?? (aiFlag ? \"other\" : \"none\")\n ).toLowerCase();\n\n const prev = this.alarmEventState.get(channel);\n const curr = {\n motionActive: statusIndicatesMotion,\n visitorActive,\n dayNightActive,\n aiTypeToken,\n aiFlag,\n aiTimeStamp: alarmTimeStamp,\n };\n\n // First observation: initialize state, don't emit (avoids spurious startup detections).\n if (!prev) {\n this.alarmEventState.set(channel, curr);\n\n // However, some firmwares report an active AI detection immediately upon subscription without\n // toggling state. If a non-zero timeStamp is present, treat it as a concrete detection.\n if (\n curr.aiTypeToken !== \"none\" &&\n curr.aiTimeStamp &&\n curr.aiTimeStamp !== 0\n ) {\n const t = curr.aiTypeToken;\n const aiTypeMap: Record<\n string,\n \"people\" | \"vehicle\" | \"dog_cat\" | \"face\" | \"package\" | \"other\"\n > = {\n people: \"people\",\n person: \"people\",\n human: \"people\",\n vehicle: \"vehicle\",\n car: \"vehicle\",\n dog_cat: \"dog_cat\",\n dog: \"dog_cat\",\n cat: \"dog_cat\",\n pet: \"dog_cat\",\n face: \"face\",\n package: \"package\",\n };\n out.push({\n channel,\n type: \"ai\",\n ai: {\n channel,\n type: aiTypeMap[t] ?? \"other\",\n detected: true,\n timestamp: now,\n },\n timestamp: now,\n });\n }\n\n // Doorbell/visitor and day/night are discrete flags.\n if (curr.visitorActive)\n out.push({ channel, type: \"visitor\", timestamp: now });\n if (curr.dayNightActive)\n out.push({ channel, type: \"daynight\", timestamp: now });\n\n continue;\n }\n\n // Motion: emit only on rising edge.\n if (!prev.motionActive && curr.motionActive) {\n const source = statusUpper.includes(\"MD\")\n ? \"md\"\n : statusUpper.includes(\"PIR\") || statusUpper.includes(\"OTHER\")\n ? \"pir\"\n : \"unknown\";\n out.push({\n channel,\n type: \"motion\",\n motion: { channel, state: true, timestamp: now, source },\n timestamp: now,\n });\n }\n\n // AI: many firmwares (notably hubs) toggle AItype between \"none\" and a token while status stays \"none\".\n // Emit on AI type transition to a non-none value.\n // Additionally, if a non-zero timeStamp is present, emit when it changes (some firmwares keep AItype\n // constant like \"other\" while timeStamp increments per detection).\n const aiTypeChanged = prev.aiTypeToken !== curr.aiTypeToken;\n const aiTimeStampChanged =\n curr.aiTimeStamp !== undefined &&\n curr.aiTimeStamp !== 0 &&\n curr.aiTimeStamp !== prev.aiTimeStamp;\n\n if (\n (aiTypeChanged && curr.aiTypeToken !== \"none\") ||\n (aiTimeStampChanged && curr.aiTypeToken !== \"none\")\n ) {\n const t = curr.aiTypeToken;\n const aiTypeMap: Record<\n string,\n \"people\" | \"vehicle\" | \"dog_cat\" | \"face\" | \"package\" | \"other\"\n > = {\n people: \"people\",\n person: \"people\",\n human: \"people\",\n vehicle: \"vehicle\",\n car: \"vehicle\",\n dog_cat: \"dog_cat\",\n dog: \"dog_cat\",\n cat: \"dog_cat\",\n pet: \"dog_cat\",\n face: \"face\",\n package: \"package\",\n };\n out.push({\n channel,\n type: \"ai\",\n ai: {\n channel,\n type: aiTypeMap[t] ?? \"other\",\n detected: true,\n timestamp: now,\n },\n timestamp: now,\n });\n }\n\n // Doorbell/visitor and day/night: emit only on rising edge.\n if (!prev.visitorActive && curr.visitorActive) {\n out.push({ channel, type: \"visitor\", timestamp: now });\n }\n\n if (!prev.dayNightActive && curr.dayNightActive) {\n out.push({ channel, type: \"daynight\", timestamp: now });\n }\n\n // Update state after processing.\n this.alarmEventState.set(channel, curr);\n }\n return out;\n }\n\n // 2) Fallback format: <Event>\n const eventMatch = xml.match(/<Event\\b[^>]*>([\\s\\S]*?)<\\/Event>/);\n if (!eventMatch) return [];\n\n const eventXml = eventMatch[1] ?? \"\";\n const status = (getXmlText(eventXml, \"status\") ?? \"\").trim();\n const statusUpper = status.toUpperCase();\n const aiTypeRaw = (\n getXmlText(eventXml, \"AItype\") ??\n getXmlText(eventXml, \"aiType\") ??\n getXmlText(eventXml, \"aitype\") ??\n \"\"\n ).trim();\n\n const timeStampRaw = (\n getXmlText(eventXml, \"timeStamp\") ??\n getXmlText(eventXml, \"timestamp\") ??\n \"\"\n ).trim();\n const timeStampNum = timeStampRaw.length ? Number(timeStampRaw) : undefined;\n const alarmTimeStamp = Number.isFinite(timeStampNum as number)\n ? (timeStampNum as number)\n : undefined;\n\n const out: ReolinkEvent[] = [];\n\n const statusLower = status.toLowerCase();\n const statusTokens = statusLower\n .split(\",\")\n .map((t) => t.trim())\n .filter((t) => t.length > 0);\n\n const visitorActive = statusTokens.includes(\"visitor\");\n const dayNightActive =\n statusTokens.includes(\"dn\") || statusTokens.includes(\"daynight\");\n const statusIndicatesMotion = statusTokens.some(\n (t) => t !== \"none\" && t !== \"visitor\" && t !== \"dn\" && t !== \"daynight\",\n );\n\n const aiTypeTokenRaw = aiTypeRaw\n ? aiTypeRaw\n .split(\",\")\n .map((t) => t.trim())\n .find((t) => t.length > 0 && t.toLowerCase() !== \"none\")\n : undefined;\n const aiFlag = statusUpper.includes(\"AI\");\n const aiTypeToken = (\n aiTypeTokenRaw ?? (aiFlag ? \"other\" : \"none\")\n ).toLowerCase();\n\n const prev = this.alarmEventState.get(fallbackChannel);\n const curr = {\n motionActive: statusIndicatesMotion,\n visitorActive,\n dayNightActive,\n aiTypeToken,\n aiFlag,\n aiTimeStamp: alarmTimeStamp,\n };\n\n if (!prev) {\n this.alarmEventState.set(fallbackChannel, curr);\n\n if (\n curr.aiTypeToken !== \"none\" &&\n curr.aiTimeStamp &&\n curr.aiTimeStamp !== 0\n ) {\n const t = curr.aiTypeToken;\n const aiTypeMap: Record<\n string,\n \"people\" | \"vehicle\" | \"dog_cat\" | \"face\" | \"package\" | \"other\"\n > = {\n people: \"people\",\n person: \"people\",\n human: \"people\",\n vehicle: \"vehicle\",\n car: \"vehicle\",\n dog_cat: \"dog_cat\",\n dog: \"dog_cat\",\n cat: \"dog_cat\",\n pet: \"dog_cat\",\n face: \"face\",\n package: \"package\",\n };\n out.push({\n channel: fallbackChannel,\n type: \"ai\",\n ai: {\n channel: fallbackChannel,\n type: aiTypeMap[t] ?? \"other\",\n detected: true,\n timestamp: now,\n },\n timestamp: now,\n });\n }\n\n if (curr.visitorActive)\n out.push({ channel: fallbackChannel, type: \"visitor\", timestamp: now });\n if (curr.dayNightActive)\n out.push({\n channel: fallbackChannel,\n type: \"daynight\",\n timestamp: now,\n });\n\n return out;\n }\n\n if (!prev.motionActive && curr.motionActive) {\n const source = statusUpper.includes(\"MD\")\n ? \"md\"\n : statusUpper.includes(\"PIR\") || statusUpper.includes(\"OTHER\")\n ? \"pir\"\n : \"unknown\";\n out.push({\n channel: fallbackChannel,\n type: \"motion\",\n motion: {\n channel: fallbackChannel,\n state: true,\n timestamp: now,\n source,\n },\n timestamp: now,\n });\n }\n\n const aiTypeChanged = prev.aiTypeToken !== curr.aiTypeToken;\n const aiTimeStampChanged =\n curr.aiTimeStamp !== undefined &&\n curr.aiTimeStamp !== 0 &&\n curr.aiTimeStamp !== prev.aiTimeStamp;\n\n if (\n (aiTypeChanged && curr.aiTypeToken !== \"none\") ||\n (aiTimeStampChanged && curr.aiTypeToken !== \"none\")\n ) {\n const t = curr.aiTypeToken;\n const aiTypeMap: Record<\n string,\n \"people\" | \"vehicle\" | \"dog_cat\" | \"face\" | \"package\" | \"other\"\n > = {\n people: \"people\",\n person: \"people\",\n human: \"people\",\n vehicle: \"vehicle\",\n car: \"vehicle\",\n dog_cat: \"dog_cat\",\n dog: \"dog_cat\",\n cat: \"dog_cat\",\n pet: \"dog_cat\",\n face: \"face\",\n package: \"package\",\n };\n out.push({\n channel: fallbackChannel,\n type: \"ai\",\n ai: {\n channel: fallbackChannel,\n type: aiTypeMap[t] ?? \"other\",\n detected: true,\n timestamp: now,\n },\n timestamp: now,\n });\n }\n\n if (!prev.visitorActive && curr.visitorActive)\n out.push({ channel: fallbackChannel, type: \"visitor\", timestamp: now });\n if (!prev.dayNightActive && curr.dayNightActive)\n out.push({ channel: fallbackChannel, type: \"daynight\", timestamp: now });\n\n this.alarmEventState.set(fallbackChannel, curr);\n\n return out;\n }\n\n private nextMsgNum(): number {\n this.msgNum = (this.msgNum + 1) & 0xffff;\n return this.msgNum;\n }\n\n /**\n * Atomically allocate the next msgNum.\n *\n * IMPORTANT: `peekNextMsgNum()` is not safe under concurrency: two parallel callers can observe\n * the same value and end up starting two streams with the same msgNum, which causes media packet\n * mixups when sharing a single socket.\n */\n public reserveNextMsgNum(): number {\n return this.nextMsgNum();\n }\n\n /**\n * Get the next message number that will be used (without incrementing).\n * Useful for subscribing to video streams before sending the command.\n * Public to allow ReolinkBaichuanApi to subscribe before sending video stream commands.\n */\n public peekNextMsgNum(): number {\n return (this.msgNum + 1) & 0xffff;\n }\n\n private requireSocket(): net.Socket {\n if (this.transport !== \"tcp\")\n throw new Error(\"Internal: requireSocket called while not using TCP\");\n if (!this.tcpSocket || this.tcpSocket.destroyed)\n throw new Error(\"Baichuan TCP socket is not connected\");\n return this.tcpSocket;\n }\n\n private writeWire(wire: Buffer): void {\n this.lastTxAtMs = Date.now();\n if (this.transport === \"tcp\") {\n this.requireSocket().write(wire);\n return;\n }\n if (!this.udpSocket)\n throw new Error(\"Baichuan UDP stream is not connected\");\n this.udpSocket.write(wire);\n }\n\n private encodeBodyXml(\n extXml: string,\n payloadXml: string,\n channelId: number,\n enc: EncryptionProtocol,\n ): Buffer {\n const extBuf = Buffer.from(extXml, \"utf8\");\n const payloadBuf = Buffer.from(payloadXml, \"utf8\");\n\n if (enc.kind === \"none\") return Buffer.concat([extBuf, payloadBuf]);\n if (enc.kind === \"bc\")\n return Buffer.concat([\n bcEncrypt(extBuf, channelId),\n bcEncrypt(payloadBuf, channelId),\n ]);\n if (enc.kind === \"aes\" || enc.kind === \"full_aes\")\n return Buffer.concat([\n aesEncrypt(extBuf, enc.key),\n aesEncrypt(payloadBuf, enc.key),\n ]);\n // exhaustive\n return Buffer.concat([extBuf, payloadBuf]);\n }\n\n private encodeBodyBinary(\n extXml: string,\n payload: Buffer,\n channelId: number,\n enc: EncryptionProtocol,\n ): Buffer {\n const extBuf = Buffer.from(extXml, \"utf8\");\n\n // Binary payloads are sent unencrypted, while the Extension is still encrypted.\n if (enc.kind === \"none\") return Buffer.concat([extBuf, payload]);\n if (enc.kind === \"bc\")\n return Buffer.concat([bcEncrypt(extBuf, channelId), payload]);\n if (enc.kind === \"aes\" || enc.kind === \"full_aes\")\n return Buffer.concat([aesEncrypt(extBuf, enc.key), payload]);\n return Buffer.concat([extBuf, payload]);\n }\n\n /**\n * Sends a Baichuan command with a binary payload (Extension XML + raw binary payload).\n *\n * This is required for Talk (cmdId=202): the payload is BcMedia ADPCM and should NOT be encrypted,\n * while the Extension still follows the session encryption.\n *\n * Note: many cameras do not send a reply for Talk packets, so this is fire-and-forget.\n */\n async sendBinaryPayloadNoReply(params: {\n cmdId: number;\n payload: Buffer;\n channel?: number;\n /** Override the header channelId (and encryption channelId) for this request. */\n channelIdOverride?: number;\n /** If omitted, uses a binary Extension with <binaryData>1</binaryData> + channelId. */\n extensionXml?: string;\n messageClass?: number;\n streamType?: number;\n encryption?: EncryptionProtocol;\n /** Internal operations should not count as user activity for idle disconnect. */\n internal?: boolean;\n }): Promise<void> {\n const internal = params.internal === true;\n if (!internal)\n this.touchUserActivity(`sendBinaryPayloadNoReply cmdId=${params.cmdId}`);\n await this.connect();\n if (!internal) this.kickIdleDisconnectTimer();\n\n const channel = params.channel ?? this.opts.channel ?? 0;\n const channelId =\n params.channelIdOverride ??\n (params.channel == null ? this.hostChannelId : channel + 1);\n\n const msgNum = this.nextMsgNum();\n const cmdId = params.cmdId;\n\n const extXml = params.extensionXml ?? buildBinaryExtensionXml(channel);\n const payloadOffset = Buffer.byteLength(extXml, \"utf8\");\n const bodyLen = payloadOffset + params.payload.length;\n\n const messageClass = params.messageClass ?? BC_CLASS_MODERN_24;\n\n const header = encodeHeader({\n cmdId,\n bodyLen,\n channelId,\n streamType: params.streamType ?? 0,\n msgNum,\n responseCode: 0,\n messageClass,\n payloadOffset,\n });\n\n const enc = params.encryption ?? this.enc;\n const bodyBytes = this.encodeBodyBinary(\n extXml,\n params.payload,\n channelId,\n enc,\n );\n const wire = Buffer.concat([header, bodyBytes]);\n\n this.logDebug(\"tx\", {\n cmdId,\n msgNum,\n channelId,\n messageClass,\n bodyLen,\n binaryPayload: true,\n });\n if (this.debugCfg.traceTalk && isTalkCmd(cmdId)) {\n talkTraceLog(\n this.debugCfg,\n this.logger,\n \"BaichuanTalk\",\n `tx cmdId=${cmdId} msgNum=${msgNum} channelId=${channelId} streamType=${params.streamType ?? 0} class=0x${messageClass.toString(16)} bodyLen=${bodyLen} payloadOffset=${payloadOffset} binaryPayloadLen=${params.payload.length}`,\n );\n }\n this.recordTx({\n cmdId,\n responseCode: 0,\n msgNum,\n channelId,\n streamType: params.streamType ?? 0,\n });\n this.writeWire(wire);\n\n if (!internal) this.kickIdleDisconnectTimer();\n }\n\n tryDecryptXml(\n buf: Buffer,\n channelId: number,\n preferred: EncryptionProtocol,\n ): string {\n const tryDecode = (b: Buffer) => b.toString(\"utf8\");\n\n const tryAs = (enc: EncryptionProtocol): string | undefined => {\n let dec: Buffer;\n try {\n if (enc.kind === \"none\") dec = buf;\n else if (enc.kind === \"bc\") dec = bcDecrypt(buf, channelId);\n else dec = aesDecrypt(buf, enc.key);\n } catch {\n return undefined;\n }\n const s = tryDecode(dec);\n return s.startsWith(\"<?xml\") ? s : undefined;\n };\n\n return (\n tryAs(preferred) ??\n (preferred.kind !== \"bc\" ? tryAs({ kind: \"bc\" }) : undefined) ??\n tryAs({ kind: \"none\" }) ??\n tryDecode(buf)\n );\n }\n\n /**\n * Sends a Baichuan command and returns the XML reply (if any).\n * If the reply has no body, returns an empty string.\n */\n async sendXml(params: {\n cmdId: number;\n channel?: number;\n /** Override the header channelId (and encryption channelId) for this request. */\n channelIdOverride?: number;\n payloadXml?: string;\n extensionXml?: string;\n /** Header class; defaults to modern 24-byte header (0x6414). */\n messageClass?: number;\n /** Stream type in header: 0 for main/ext, 1 for sub (used for video streaming). */\n streamType?: number;\n /** Force a specific encryption protocol for this call. */\n encryption?: EncryptionProtocol;\n /** Timeout ms. */\n timeoutMs?: number;\n /** Internal operations (keepalive/ping) should not count as user activity for idle disconnect. */\n internal?: boolean;\n }): Promise<string> {\n const internal = params.internal === true;\n if (!internal) this.touchUserActivity(`sendXml cmdId=${params.cmdId}`);\n await this.connect();\n if (!internal) this.kickIdleDisconnectTimer();\n\n const channel = params.channel ?? this.opts.channel ?? 0;\n const channelId =\n params.channelIdOverride ??\n (params.channel == null ? this.hostChannelId : channel + 1);\n\n const msgNum = this.nextMsgNum();\n const cmdId = params.cmdId;\n\n const extXml =\n params.extensionXml ??\n (params.channel != null ? buildChannelExtensionXml(channel) : \"\");\n const payloadXml = params.payloadXml ?? \"\";\n\n const messageClass = params.messageClass ?? BC_CLASS_MODERN_24;\n const payloadOffset = Buffer.byteLength(extXml, \"utf8\");\n const bodyLen = payloadOffset + Buffer.byteLength(payloadXml, \"utf8\");\n\n const header = encodeHeader({\n cmdId,\n bodyLen,\n channelId,\n streamType: params.streamType ?? 0,\n msgNum,\n responseCode: 0,\n messageClass,\n payloadOffset,\n });\n const pendingKey: PendingKey = `${cmdId}:${msgNum}`;\n\n const enc = params.encryption ?? this.enc;\n const bodyBytes = this.encodeBodyXml(extXml, payloadXml, channelId, enc);\n const wire = Buffer.concat([header, bodyBytes]);\n\n const timeoutMs = params.timeoutMs ?? 10_000;\n let rejectFn: ((e: Error) => void) | undefined;\n const framePromise = new Promise<BaichuanFrame>((resolve, reject) => {\n rejectFn = reject;\n const t = setTimeout(() => {\n this.pending.delete(pendingKey);\n reject(new Error(`Baichuan timeout cmdId=${cmdId} msgNum=${msgNum}`));\n }, timeoutMs);\n this.pending.set(pendingKey, {\n resolve: (f) => {\n clearTimeout(t);\n resolve(f);\n },\n reject: (e) => {\n clearTimeout(t);\n reject(e);\n },\n });\n });\n\n // CRITICAL: Add catch handler IMMEDIATELY after promise creation, BEFORE any operations\n // This must happen synchronously, before writeWire or any async operations\n // This prevents unhandled rejections when socket closes during writeWire\n framePromise.catch(() => {\n // Silently handle rejections from socket closures\n // The caller will handle the error when they await the promise\n });\n\n this.logDebug(\"tx\", { cmdId, msgNum, channelId, messageClass, bodyLen });\n if (this.debugCfg.traceNativeStream && (cmdId === 3 || cmdId === 4)) {\n traceLog(\n this.debugCfg,\n this.logger,\n \"BaichuanTrace\",\n `tx cmdId=${cmdId} msgNum=${msgNum} channelId=${channelId} streamType=${params.streamType ?? 0} class=0x${messageClass.toString(16)} bodyLen=${bodyLen} payloadOffset=${payloadOffset}`,\n );\n }\n if (this.debugCfg.traceTalk && isTalkCmd(cmdId)) {\n talkTraceLog(\n this.debugCfg,\n this.logger,\n \"BaichuanTalk\",\n `tx cmdId=${cmdId} msgNum=${msgNum} channelId=${channelId} streamType=${params.streamType ?? 0} class=0x${messageClass.toString(16)} bodyLen=${bodyLen} payloadOffset=${payloadOffset}`,\n );\n }\n this.recordTx({\n cmdId,\n responseCode: 0,\n msgNum,\n channelId,\n streamType: params.streamType ?? 0,\n });\n this.writeWire(wire);\n\n const frame = await framePromise;\n this.logDebug(\"rx\", {\n cmdId: frame.header.cmdId,\n responseCode: frame.header.responseCode,\n msgNum: frame.header.msgNum,\n });\n if (this.debugCfg.traceNativeStream && (cmdId === 3 || cmdId === 4)) {\n traceLog(\n this.debugCfg,\n this.logger,\n \"BaichuanTrace\",\n `rx cmdId=${frame.header.cmdId} msgNum=${frame.header.msgNum} responseCode=${frame.header.responseCode} channelId=${frame.header.channelId} bodyLen=${frame.body.length} payloadLen=${frame.payload.length} payloadOffset=${frame.header.payloadOffset ?? 0}`,\n );\n }\n if (this.debugCfg.traceTalk && isTalkCmd(cmdId)) {\n talkTraceLog(\n this.debugCfg,\n this.logger,\n \"BaichuanTalk\",\n `rx cmdId=${frame.header.cmdId} msgNum=${frame.header.msgNum} responseCode=${frame.header.responseCode} channelId=${frame.header.channelId} bodyLen=${frame.body.length} payloadLen=${frame.payload.length} payloadOffset=${frame.header.payloadOffset ?? 0}`,\n );\n }\n\n // Check responseCode for errors (400 = bad request/auth failure, 200 = success)\n // Some cameras return 400 with empty body when authentication fails\n if (frame.header.responseCode === 400) {\n // Try to decrypt anyway in case there's an error message, but don't fail if body is empty\n const body = frame.body;\n if (body.length === 0) {\n // Empty body with 400 typically means authentication failure\n throw new Error(\n \"Baichuan authentication failed (responseCode 400, empty body) - check username/password\",\n );\n }\n // If body is not empty, try to decrypt and return it (might contain error details)\n }\n\n // split + decrypt (extension/payload concatenated as in body)\n const body = frame.body;\n if (body.length === 0) {\n if (!internal) this.kickIdleDisconnectTimer();\n return \"\";\n }\n\n // For modern 24-byte frames: extension+payload; we decrypt full body as one stream just like references do.\n // (In practice extension and payload are separately encrypted but concatenation preserves it.)\n const xml = this.tryDecryptXml(body, frame.header.channelId, enc);\n if (!internal) this.kickIdleDisconnectTimer();\n return xml;\n }\n\n /**\n * Sends a Baichuan command and returns the frame (for checking response_code).\n * Similar to sendXml but returns the full frame instead of just the XML body.\n */\n async sendFrame(params: {\n cmdId: number;\n channel?: number;\n /** Override the header channelId (and encryption channelId) for this request. */\n channelIdOverride?: number;\n /** Override the header msgNum for this request (advanced; used to match start/stop stream msgNum). */\n msgNumOverride?: number;\n payloadXml?: string;\n extensionXml?: string;\n messageClass?: number;\n streamType?: number;\n encryption?: EncryptionProtocol;\n timeoutMs?: number;\n /** Internal operations (keepalive/ping) should not count as user activity for idle disconnect. */\n internal?: boolean;\n }): Promise<BaichuanFrame> {\n const internal = params.internal === true;\n if (!internal) this.touchUserActivity(`sendFrame cmdId=${params.cmdId}`);\n await this.connect();\n if (!internal) this.kickIdleDisconnectTimer();\n\n const channel = params.channel ?? this.opts.channel ?? 0;\n const channelId =\n params.channelIdOverride ??\n (params.channel == null ? this.hostChannelId : channel + 1);\n\n const msgNum = params.msgNumOverride ?? this.nextMsgNum();\n const cmdId = params.cmdId;\n\n const extXml =\n params.extensionXml ??\n (params.channel != null ? buildChannelExtensionXml(channel) : \"\");\n const payloadXml = params.payloadXml ?? \"\";\n\n const messageClass = params.messageClass ?? BC_CLASS_MODERN_24;\n const payloadOffset = Buffer.byteLength(extXml, \"utf8\");\n const bodyLen = payloadOffset + Buffer.byteLength(payloadXml, \"utf8\");\n\n const header = encodeHeader({\n cmdId,\n bodyLen,\n channelId,\n streamType: params.streamType ?? 0,\n msgNum,\n responseCode: 0,\n messageClass,\n payloadOffset,\n });\n const pendingKey: PendingKey = `${cmdId}:${msgNum}`;\n\n const enc = params.encryption ?? this.enc;\n const bodyBytes = this.encodeBodyXml(extXml, payloadXml, channelId, enc);\n const wire = Buffer.concat([header, bodyBytes]);\n\n const timeoutMs = params.timeoutMs ?? 10_000;\n let rejectFn: ((e: Error) => void) | undefined;\n const framePromise = new Promise<BaichuanFrame>((resolve, reject) => {\n rejectFn = reject;\n const t = setTimeout(() => {\n this.pending.delete(pendingKey);\n reject(new Error(`Baichuan timeout cmdId=${cmdId} msgNum=${msgNum}`));\n }, timeoutMs);\n this.pending.set(pendingKey, {\n resolve: (f) => {\n clearTimeout(t);\n resolve(f);\n },\n reject: (e) => {\n clearTimeout(t);\n reject(e);\n },\n });\n });\n\n // CRITICAL: Add catch handler IMMEDIATELY after promise creation, BEFORE any operations\n // This must happen synchronously, before writeWire or any async operations\n // This prevents unhandled rejections when socket closes during writeWire\n framePromise.catch(() => {\n // Silently handle rejections from socket closures\n // The caller will handle the error when they await the promise\n });\n\n this.logDebug(\"tx\", { cmdId, msgNum, channelId, messageClass, bodyLen });\n if (BaichuanClient.recordingCmdIds.has(cmdId)) {\n recordingsTraceLog(\n this.debugCfg,\n this.logger,\n \"BaichuanRecordings\",\n `tx recording cmdId=${cmdId} msgNum=${msgNum} channelId=${channelId} streamType=${params.streamType ?? 0} bodyLen=${bodyLen}`,\n );\n }\n if (this.debugCfg.traceNativeStream && (cmdId === 3 || cmdId === 4)) {\n traceLog(\n this.debugCfg,\n this.logger,\n \"BaichuanTrace\",\n `tx cmdId=${cmdId} msgNum=${msgNum} channelId=${channelId} streamType=${params.streamType ?? 0} class=0x${messageClass.toString(16)} bodyLen=${bodyLen} payloadOffset=${payloadOffset}`,\n );\n }\n if (this.debugCfg.traceTalk && isTalkCmd(cmdId)) {\n talkTraceLog(\n this.debugCfg,\n this.logger,\n \"BaichuanTalk\",\n `tx cmdId=${cmdId} msgNum=${msgNum} channelId=${channelId} streamType=${params.streamType ?? 0} class=0x${messageClass.toString(16)} bodyLen=${bodyLen} payloadOffset=${payloadOffset}`,\n );\n }\n this.recordTx({\n cmdId,\n responseCode: 0,\n msgNum,\n channelId,\n streamType: params.streamType ?? 0,\n });\n this.writeWire(wire);\n\n const frame = await framePromise;\n this.logDebug(\"rx\", {\n cmdId: frame.header.cmdId,\n responseCode: frame.header.responseCode,\n msgNum: frame.header.msgNum,\n });\n if (BaichuanClient.recordingCmdIds.has(frame.header.cmdId)) {\n recordingsTraceLog(\n this.debugCfg,\n this.logger,\n \"BaichuanRecordings\",\n `rx recording cmdId=${frame.header.cmdId} msgNum=${frame.header.msgNum} responseCode=${frame.header.responseCode} channelId=${frame.header.channelId} bodyLen=${frame.body.length} payloadLen=${frame.payload.length} payloadOffset=${frame.header.payloadOffset ?? 0}`,\n );\n }\n if (this.debugCfg.traceNativeStream && (cmdId === 3 || cmdId === 4)) {\n traceLog(\n this.debugCfg,\n this.logger,\n \"BaichuanTrace\",\n `rx cmdId=${frame.header.cmdId} msgNum=${frame.header.msgNum} responseCode=${frame.header.responseCode} channelId=${frame.header.channelId} bodyLen=${frame.body.length} payloadLen=${frame.payload.length} payloadOffset=${frame.header.payloadOffset ?? 0}`,\n );\n }\n if (this.debugCfg.traceTalk && isTalkCmd(cmdId)) {\n talkTraceLog(\n this.debugCfg,\n this.logger,\n \"BaichuanTalk\",\n `rx cmdId=${frame.header.cmdId} msgNum=${frame.header.msgNum} responseCode=${frame.header.responseCode} channelId=${frame.header.channelId} bodyLen=${frame.body.length} payloadLen=${frame.payload.length} payloadOffset=${frame.header.payloadOffset ?? 0}`,\n );\n }\n if (!internal) this.kickIdleDisconnectTimer();\n return frame;\n }\n\n /**\n * Sends a Baichuan command and returns the binary reply (for commands like Snap that return binary data).\n * Similar to sendXml but returns raw Buffer instead of XML string.\n */\n async sendBinary(params: {\n cmdId: number;\n channel?: number;\n /** Override the header channelId (and encryption channelId) for this request. */\n channelIdOverride?: number;\n /** Override the header msgNum for this request (advanced). */\n msgNumOverride?: number;\n payloadXml?: string;\n extensionXml?: string;\n messageClass?: number;\n streamType?: number;\n encryption?: EncryptionProtocol;\n timeoutMs?: number;\n /** Internal operations should not count as user activity for idle disconnect. */\n internal?: boolean;\n }): Promise<Buffer> {\n const internal = params.internal === true;\n if (!internal) this.touchUserActivity(`sendBinary cmdId=${params.cmdId}`);\n // Snapshot (cmdId=109) is special: many firmwares deliver the binary payload via unsolicited \"push\" frames\n // and do not necessarily reply on the request's cmdId:msgNum pending slot. In that case, waiting on\n // `pending` will timeout. Handle it by sending without pending and collecting push chunks.\n if (params.cmdId === 109) {\n const res = await this.sendBinarySnapshot109(params);\n if (!internal) this.kickIdleDisconnectTimer();\n return res;\n }\n\n // FileInfoList replay (cmdId=5) is commonly delivered as a sequence of binary chunks.\n // PCAP: response Extension has <binaryData>1</binaryData> and then many payload-only frames.\n if (params.cmdId === BC_CMD_ID_FILE_INFO_LIST_REPLAY) {\n const res = await this.sendBinaryFileInfoListReplay5(params);\n if (!internal) this.kickIdleDisconnectTimer();\n return res;\n }\n\n // File download (class=0x6482) is often delivered as a sequence of binary chunks.\n // Handle it similarly to snapshot: send without pending and collect frames until completion.\n if (\n (params.messageClass ?? BC_CLASS_MODERN_24) === BC_CLASS_FILE_DOWNLOAD\n ) {\n const res = await this.sendBinaryFileDownload6482(params);\n if (!internal) this.kickIdleDisconnectTimer();\n return res;\n }\n\n await this.connect();\n if (!internal) this.kickIdleDisconnectTimer();\n\n const channel = params.channel ?? this.opts.channel ?? 0;\n const channelId =\n params.channelIdOverride ??\n (params.channel == null ? this.hostChannelId : channel + 1);\n\n const msgNum = this.nextMsgNum();\n const cmdId = params.cmdId;\n\n const extXml =\n params.extensionXml ??\n (params.channel != null\n ? buildBinaryExtensionXml(channel)\n : buildBinaryExtensionXml(undefined));\n const payloadXml = params.payloadXml ?? \"\";\n\n const messageClass = params.messageClass ?? BC_CLASS_MODERN_24;\n const payloadOffset = Buffer.byteLength(extXml, \"utf8\");\n const bodyLen = payloadOffset + Buffer.byteLength(payloadXml, \"utf8\");\n\n const header = encodeHeader({\n cmdId,\n bodyLen,\n channelId,\n streamType: 0,\n msgNum,\n responseCode: 0,\n messageClass,\n payloadOffset,\n });\n const pendingKey: PendingKey = `${cmdId}:${msgNum}`;\n\n const enc = params.encryption ?? this.enc;\n const bodyBytes = this.encodeBodyXml(extXml, payloadXml, channelId, enc);\n const wire = Buffer.concat([header, bodyBytes]);\n\n const timeoutMs = params.timeoutMs ?? 10_000;\n let rejectFn: ((e: Error) => void) | undefined;\n const framePromise = new Promise<BaichuanFrame>((resolve, reject) => {\n rejectFn = reject;\n const t = setTimeout(() => {\n this.pending.delete(pendingKey);\n reject(new Error(`Baichuan timeout cmdId=${cmdId} msgNum=${msgNum}`));\n }, timeoutMs);\n this.pending.set(pendingKey, {\n resolve: (f) => {\n clearTimeout(t);\n resolve(f);\n },\n reject: (e) => {\n clearTimeout(t);\n reject(e);\n },\n });\n });\n\n // CRITICAL: Add catch handler IMMEDIATELY after promise creation, BEFORE any operations\n // This must happen synchronously, before writeWire or any async operations\n // This prevents unhandled rejections when socket closes during writeWire\n framePromise.catch(() => {\n // Silently handle rejections from socket closures\n // The caller will handle the error when they await the promise\n });\n\n this.logDebug(\"tx\", {\n cmdId,\n msgNum,\n channelId,\n messageClass,\n bodyLen,\n binary: true,\n });\n this.recordTx({ cmdId, responseCode: 0, msgNum, channelId, streamType: 0 });\n this.writeWire(wire);\n\n const frame = await framePromise;\n this.logDebug(\"rx\", {\n cmdId: frame.header.cmdId,\n responseCode: frame.header.responseCode,\n msgNum: frame.header.msgNum,\n binary: true,\n });\n\n if (frame.header.responseCode === 400) {\n const body = frame.body;\n if (body.length === 0) {\n throw new Error(\n \"Baichuan binary request failed (responseCode 400, empty body)\",\n );\n }\n }\n\n // IMPORTANT: `body` can include an XML Extension prefix; for binary data use `payload`.\n const payload = frame.payload;\n if (payload.length === 0) return Buffer.alloc(0);\n\n const decrypted = this.tryDecryptBinary(\n payload,\n frame.header.channelId,\n enc,\n );\n if (!internal) this.kickIdleDisconnectTimer();\n return decrypted;\n }\n\n private async sendBinaryFileDownload6482(params: {\n cmdId: number;\n channel?: number;\n /** Override the header channelId (and encryption channelId) for this request. */\n channelIdOverride?: number;\n payloadXml?: string;\n extensionXml?: string;\n messageClass?: number;\n streamType?: number;\n encryption?: EncryptionProtocol;\n timeoutMs?: number;\n }): Promise<Buffer> {\n await this.connect();\n\n const channel = params.channel ?? this.opts.channel ?? 0;\n const channelId =\n params.channelIdOverride ??\n (params.channel == null ? this.hostChannelId : channel + 1);\n\n const msgNum = this.nextMsgNum();\n const cmdId = params.cmdId;\n\n // For binary downloads, request Extension should include <binaryData>1</binaryData>.\n const extXml = params.extensionXml ?? buildBinaryExtensionXml(channel);\n const payloadXml = params.payloadXml ?? \"\";\n\n const messageClass = params.messageClass ?? BC_CLASS_FILE_DOWNLOAD;\n const payloadOffset = Buffer.byteLength(extXml, \"utf8\");\n const bodyLen = payloadOffset + Buffer.byteLength(payloadXml, \"utf8\");\n\n const header = encodeHeader({\n cmdId,\n bodyLen,\n channelId,\n streamType: params.streamType ?? 0,\n msgNum,\n responseCode: 0,\n messageClass,\n payloadOffset,\n });\n\n const enc = params.encryption ?? this.enc;\n const bodyBytes = this.encodeBodyXml(extXml, payloadXml, channelId, enc);\n const wire = Buffer.concat([header, bodyBytes]);\n\n const timeoutMs = params.timeoutMs ?? 60_000;\n const chunks: Buffer[] = [];\n let receivedBytes = 0;\n let lastProgressLogAt = 0;\n let streamMsgNum: number | undefined;\n let lockedChannelId: number | undefined;\n let lockedStreamType: number | undefined;\n\n const expectedStreamType = params.streamType ?? 0;\n\n return await new Promise<Buffer>((resolve, reject) => {\n let timeout: NodeJS.Timeout | undefined;\n let done = false;\n\n const cleanup = () => {\n this.off(\"frame\", onFrame);\n if (timeout) clearTimeout(timeout);\n };\n\n const finish = (buf: Buffer) => {\n if (done) return;\n done = true;\n cleanup();\n resolve(buf);\n };\n\n const fail = (e: unknown) => {\n if (done) return;\n done = true;\n cleanup();\n reject(e instanceof Error ? e : new Error(String(e)));\n };\n\n const looksLikeXml = (buf: Buffer): boolean => {\n // Skip leading whitespace/NULs\n let i = 0;\n while (\n i < buf.length &&\n (buf[i] === 0x00 ||\n buf[i] === 0x09 ||\n buf[i] === 0x0a ||\n buf[i] === 0x0d ||\n buf[i] === 0x20)\n )\n i++;\n if (i >= buf.length) return false;\n return buf[i] === 0x3c; // '<'\n };\n\n const decryptBinaryForDownload = (\n payload: Buffer,\n frameChannelId: number,\n encryptLen?: number,\n ): Buffer => {\n // If encryptLen is provided, only decrypt the first encryptLen bytes\n // and keep the rest as-is. This is how Reolink partial encryption works.\n if (\n encryptLen !== undefined &&\n encryptLen > 0 &&\n encryptLen < payload.length\n ) {\n const encryptedPart = payload.subarray(0, encryptLen);\n const clearPart = payload.subarray(encryptLen);\n const decryptedPart = this.tryDecryptBinary(\n encryptedPart,\n frameChannelId,\n enc,\n );\n return Buffer.concat([decryptedPart, clearPart]);\n }\n\n // Helper to score how \"BcMedia-like\" a buffer is\n const scoreBcMediaLike = (b: Buffer): number => {\n if (b.length < 4) return -1;\n const maxScan = Math.min(64 * 1024, b.length - 4);\n let count = 0;\n let first = -1;\n for (let i = 0; i <= maxScan; i++) {\n const magic = b.readUInt32LE(i);\n const isInfoV1 = magic === 0x31303031;\n const isInfoV2 = magic === 0x32303031;\n const isIFrame = magic >= 0x63643030 && magic <= 0x63643039;\n const isPFrame = magic >= 0x63643130 && magic <= 0x63643139;\n const isAac = magic === 0x62773530;\n const isAdpcm = magic === 0x62773130;\n if (\n isInfoV1 ||\n isInfoV2 ||\n isIFrame ||\n isPFrame ||\n isAac ||\n isAdpcm\n ) {\n count++;\n if (first < 0) first = i;\n if (count > 32 && first === 0) break;\n }\n }\n return count * 1000 - (first < 0 ? 50000 : first);\n };\n\n if (enc.kind !== \"bc\") {\n // In AES/full_aes mode, video data may NOT be encrypted.\n // Compare raw vs decrypted and choose the one that looks more like BcMedia.\n const decrypted = this.tryDecryptBinary(payload, frameChannelId, enc);\n const rawScore = scoreBcMediaLike(payload);\n const decScore = scoreBcMediaLike(decrypted);\n // Use >= so we prefer raw if scores are equal (avoid unnecessary decrypt)\n return rawScore >= decScore ? payload : decrypted;\n }\n\n const candidates = [frameChannelId, channelId, 250, 0, 1].filter(\n (n, i, a) => Number.isFinite(n) && a.indexOf(n) === i,\n );\n const decrypted = candidates.map((cid) =>\n this.tryDecryptBinary(payload, cid, enc),\n );\n\n // Prefer a candidate that doesn't look like XML (chunks are binary).\n for (const d of decrypted) {\n if (d.length > 0 && !looksLikeXml(d)) return d;\n }\n return decrypted[0] ?? Buffer.alloc(0);\n };\n\n const onFrame = (frame: BaichuanFrame) => {\n if (frame.header.cmdId !== cmdId) return;\n\n if (\n lockedStreamType !== undefined &&\n frame.header.streamType !== lockedStreamType\n ) {\n return;\n }\n\n // Filter by request context (avoid mixing concurrent downloads on the same socket).\n // Some NVR firmwares reply with a different channelId than the request.\n if (\n lockedChannelId !== undefined &&\n frame.header.channelId !== lockedChannelId\n ) {\n return;\n }\n\n // Once we lock onto a stream msgNum, ignore everything else.\n if (\n streamMsgNum !== undefined &&\n frame.header.msgNum !== streamMsgNum\n ) {\n return;\n }\n\n // Fail fast if the request was rejected.\n if (\n frame.header.msgNum === msgNum &&\n frame.header.responseCode >= 400\n ) {\n fail(\n new Error(\n `Baichuan file download request rejected (cmdId=${cmdId} reqChannelId=${channelId} rspChannelId=${frame.header.channelId} msgNum=${msgNum} responseCode=${frame.header.responseCode})`,\n ),\n );\n return;\n }\n\n try {\n // Some firmwares do NOT mark file download chunks with <binaryData>1</binaryData>.\n // Prefer the marker when present, otherwise fall back to a payload heuristic.\n let markedBinary = false;\n let encryptLen: number | undefined;\n if (frame.extension.length > 0) {\n try {\n const extDec = this.tryDecryptXml(\n frame.extension,\n frame.header.channelId,\n enc,\n );\n if (extDec.includes(\"<binaryData>1</binaryData>\"))\n markedBinary = true;\n // Extract encryptLen if present - only first N bytes of payload are encrypted\n const encryptLenMatch = extDec.match(\n /<encryptLen>(\\d+)<\\/encryptLen>/i,\n );\n if (encryptLenMatch && encryptLenMatch[1]) {\n encryptLen = parseInt(encryptLenMatch[1], 10);\n }\n } catch {\n // ignore\n }\n }\n\n const decrypted = decryptBinaryForDownload(\n frame.payload,\n frame.header.channelId,\n encryptLen,\n );\n if (decrypted.length === 0) return;\n\n if (!markedBinary) {\n // If the payload looks like XML, it's probably an ACK/info frame, not a chunk.\n if (looksLikeXml(decrypted)) return;\n }\n\n // Lock onto the msgNum that is actually carrying binary chunks.\n if (streamMsgNum === undefined) {\n streamMsgNum = frame.header.msgNum;\n lockedChannelId = frame.header.channelId;\n lockedStreamType = frame.header.streamType;\n }\n\n chunks.push(decrypted);\n receivedBytes += decrypted.length;\n\n // Debug-only progress hint for long downloads (avoid noisy logs).\n const now = Date.now();\n if (this.debugCfg.general && now - lastProgressLogAt >= 2_000) {\n lastProgressLogAt = now;\n this.logDebug(\"file_download_progress\", {\n cmdId,\n msgNum,\n bytes: receivedBytes,\n });\n }\n\n // Completion commonly uses responseCode=201 for the final chunk.\n if (frame.header.responseCode === 201) {\n finish(Buffer.concat(chunks));\n }\n } catch (e) {\n fail(e);\n }\n };\n\n timeout = setTimeout(() => {\n fail(\n new Error(\n `Baichuan timeout waiting file download chunks cmdId=${cmdId} msgNum=${msgNum}`,\n ),\n );\n }, timeoutMs);\n\n // Attach listener BEFORE sending request.\n this.on(\"frame\", onFrame);\n\n try {\n this.logDebug(\"tx\", {\n cmdId,\n msgNum,\n channelId,\n messageClass,\n bodyLen,\n binary: true,\n fileDownload: true,\n });\n this.recordTx({\n cmdId,\n responseCode: 0,\n msgNum,\n channelId,\n streamType: expectedStreamType,\n });\n this.writeWire(wire);\n } catch (e) {\n fail(e);\n }\n });\n }\n\n private async sendBinaryFileInfoListReplay5(params: {\n cmdId: number;\n channel?: number;\n /** Override the header channelId (and encryption channelId) for this request. */\n channelIdOverride?: number;\n /** Override the header msgNum for this request (advanced). */\n msgNumOverride?: number;\n payloadXml?: string;\n extensionXml?: string;\n messageClass?: number;\n streamType?: number;\n encryption?: EncryptionProtocol;\n timeoutMs?: number;\n }): Promise<Buffer> {\n await this.connect();\n\n const channel = params.channel ?? this.opts.channel ?? 0;\n // PCAP analysis shows: channelId in header is a SESSION COUNTER that increments,\n // similar to CoverPreview (cmdId=298). Some H265 cameras reject channelId=0 or\n // channel+1 with responseCode=400 but accept a session counter value.\n // Use a separate session counter for channelId (independent from msgNum).\n const sessionCounter = this.nextMsgNum();\n const channelId = params.channelIdOverride ?? sessionCounter;\n // PCAP shows msgNum is always 0 for FileInfoListReplay (cmdId=5), like CoverPreview.\n const msgNum = params.msgNumOverride ?? 0;\n const cmdId = params.cmdId;\n\n // PCAP: request uses empty extension (payloadOffset=0).\n const extXml = params.extensionXml ?? \"\";\n const payloadXml = params.payloadXml ?? \"\";\n\n const messageClass = params.messageClass ?? BC_CLASS_MODERN_24;\n const payloadOffset = Buffer.byteLength(extXml, \"utf8\");\n const bodyLen = payloadOffset + Buffer.byteLength(payloadXml, \"utf8\");\n const expectedStreamType = params.streamType ?? 0;\n const header = encodeHeader({\n cmdId,\n bodyLen,\n channelId,\n streamType: expectedStreamType,\n msgNum,\n responseCode: 0,\n messageClass,\n payloadOffset,\n });\n\n const enc = params.encryption ?? this.enc;\n const bodyBytes = this.encodeBodyXml(extXml, payloadXml, channelId, enc);\n const wire = Buffer.concat([header, bodyBytes]);\n\n const timeoutMs = params.timeoutMs ?? 120_000;\n // Idle timeout: finish after no data received for this duration.\n // Cameras may send data in bursts with pauses between GOP boundaries.\n // Use longer timeout to ensure full file is received.\n const idleTimeoutMs = 15_000;\n const chunks: Buffer[] = [];\n let streamMsgNum: number | undefined;\n let lockedChannelId: number | undefined;\n let lockedStreamType: number | undefined;\n let idleTimer: NodeJS.Timeout | undefined;\n const looksLikeXml = (buf: Buffer): boolean => {\n let i = 0;\n while (\n i < buf.length &&\n (buf[i] === 0x00 ||\n buf[i] === 0x09 ||\n buf[i] === 0x0a ||\n buf[i] === 0x0d ||\n buf[i] === 0x20)\n )\n i++;\n if (i >= buf.length) return false;\n return buf[i] === 0x3c;\n };\n\n const decryptBinaryForReplay = (\n payload: Buffer,\n frameChannelId: number,\n encryptLen?: number,\n ) => {\n // If encryptLen is provided, only decrypt the first encryptLen bytes\n // and keep the rest as-is. This is how Reolink partial encryption works.\n if (\n encryptLen !== undefined &&\n encryptLen > 0 &&\n encryptLen < payload.length\n ) {\n const encryptedPart = payload.subarray(0, encryptLen);\n const clearPart = payload.subarray(encryptLen);\n const decryptedPart = this.tryDecryptBinary(\n encryptedPart,\n frameChannelId,\n enc,\n );\n return Buffer.concat([decryptedPart, clearPart]);\n }\n\n // Helper to score how \"BcMedia-like\" a buffer is\n const scoreBcMediaLike = (b: Buffer): number => {\n if (b.length < 4) return -1;\n const maxScan = Math.min(64 * 1024, b.length - 4);\n let count = 0;\n let first = -1;\n for (let i = 0; i <= maxScan; i++) {\n const magic = b.readUInt32LE(i);\n const isInfoV1 = magic === 0x31303031;\n const isInfoV2 = magic === 0x32303031;\n const isIFrame = magic >= 0x63643030 && magic <= 0x63643039;\n const isPFrame = magic >= 0x63643130 && magic <= 0x63643139;\n const isAac = magic === 0x62773530;\n const isAdpcm = magic === 0x62773130;\n if (\n isInfoV1 ||\n isInfoV2 ||\n isIFrame ||\n isPFrame ||\n isAac ||\n isAdpcm\n ) {\n count++;\n if (first < 0) first = i;\n if (count > 32 && first === 0) break;\n }\n }\n return count * 1000 - (first < 0 ? 50000 : first);\n };\n\n if (enc.kind !== \"bc\") {\n // In AES/full_aes mode, video data may NOT be encrypted.\n // Compare raw vs decrypted and choose the one that looks more like BcMedia.\n const decrypted = this.tryDecryptBinary(payload, frameChannelId, enc);\n const rawScore = scoreBcMediaLike(payload);\n const decScore = scoreBcMediaLike(decrypted);\n // Use >= so we prefer raw if scores are equal (avoid unnecessary decrypt)\n return rawScore >= decScore ? payload : decrypted;\n }\n\n const candidates = [frameChannelId, channelId, 250, 0, 1].filter(\n (n, i, a) => Number.isFinite(n) && a.indexOf(n) === i,\n );\n const decrypted = candidates.map((cid) =>\n this.tryDecryptBinary(payload, cid, enc),\n );\n\n for (const d of decrypted) {\n if (d.length > 0 && !looksLikeXml(d)) return d;\n }\n return decrypted[0] ?? Buffer.alloc(0);\n };\n\n return await new Promise<Buffer>((resolve, reject) => {\n let timeout: NodeJS.Timeout | undefined;\n let done = false;\n\n const cleanup = () => {\n this.off(\"frame\", onFrame);\n if (timeout) clearTimeout(timeout);\n if (idleTimer) clearTimeout(idleTimer);\n };\n\n const finish = (buf: Buffer) => {\n if (done) return;\n done = true;\n cleanup();\n resolve(buf);\n };\n\n const fail = (e: unknown) => {\n if (done) return;\n done = true;\n cleanup();\n reject(e instanceof Error ? e : new Error(String(e)));\n };\n\n const armIdleFinish = () => {\n if (idleTimer) clearTimeout(idleTimer);\n idleTimer = setTimeout(() => {\n if (chunks.length > 0) finish(Buffer.concat(chunks));\n }, idleTimeoutMs);\n };\n\n const onFrame = (frame: BaichuanFrame) => {\n if (frame.header.cmdId !== cmdId) return;\n\n // Some firmwares reply with a different streamType than the request.\n // Lock streamType once we see the stream header / first binary chunk.\n if (\n lockedStreamType !== undefined &&\n frame.header.streamType !== lockedStreamType\n ) {\n return;\n }\n\n // Some NVR firmwares reply with a different channelId than the request.\n // Lock channelId once we see the first binary chunk.\n if (\n lockedChannelId !== undefined &&\n frame.header.channelId !== lockedChannelId\n ) {\n return;\n }\n\n if (\n streamMsgNum !== undefined &&\n frame.header.msgNum !== streamMsgNum\n ) {\n return;\n }\n\n // PCAP: replay binary chunks can use responseCode=60052 (and similar 60k codes).\n // Only treat classic 4xx/5xx-like codes as a hard error, and prefer tying it to\n // the request msgNum to avoid cross-talk from other in-flight attempts.\n const rc = frame.header.responseCode;\n const isHardError = rc >= 400 && rc < 60_000;\n if (isHardError && frame.header.msgNum === msgNum) {\n fail(\n new Error(\n `Baichuan FileInfoList replay rejected (cmdId=${cmdId} reqChannelId=${channelId} rspChannelId=${frame.header.channelId} streamType=${expectedStreamType} msgNum=${frame.header.msgNum} responseCode=${rc})`,\n ),\n );\n return;\n }\n\n try {\n let markedBinary = false;\n let encryptLen: number | undefined;\n if (frame.extension.length > 0) {\n try {\n const extDec = this.tryDecryptXml(\n frame.extension,\n frame.header.channelId,\n enc,\n );\n if (extDec.includes(\"<binaryData>1</binaryData>\")) {\n markedBinary = true;\n }\n // Extract encryptLen if present - only first N bytes of payload are encrypted\n const encryptLenMatch = extDec.match(\n /<encryptLen>(\\d+)<\\/encryptLen>/i,\n );\n if (encryptLenMatch && encryptLenMatch[1]) {\n encryptLen = parseInt(encryptLenMatch[1], 10);\n }\n } catch {\n // ignore\n }\n }\n\n const decrypted = decryptBinaryForReplay(\n frame.payload,\n frame.header.channelId,\n encryptLen,\n );\n if (decrypted.length === 0) return;\n\n if (!markedBinary && looksLikeXml(decrypted)) return;\n\n if (streamMsgNum === undefined) {\n streamMsgNum = frame.header.msgNum;\n lockedChannelId = frame.header.channelId;\n lockedStreamType = frame.header.streamType;\n }\n\n chunks.push(decrypted);\n armIdleFinish();\n\n if (frame.header.responseCode === 201) {\n finish(Buffer.concat(chunks));\n }\n } catch (e) {\n fail(e);\n }\n };\n\n timeout = setTimeout(() => {\n if (chunks.length > 0) {\n finish(Buffer.concat(chunks));\n return;\n }\n fail(\n new Error(\n `Baichuan timeout waiting FileInfoList replay binary chunks cmdId=${cmdId} channelId=${channelId} streamType=${expectedStreamType}`,\n ),\n );\n }, timeoutMs);\n\n this.on(\"frame\", onFrame);\n\n try {\n this.logDebug(\"tx\", {\n cmdId,\n msgNum,\n channelId,\n messageClass,\n bodyLen,\n binary: true,\n fileInfoListReplay: true,\n });\n this.recordTx({\n cmdId,\n responseCode: 0,\n msgNum,\n channelId,\n streamType: expectedStreamType,\n });\n this.writeWire(wire);\n } catch (e) {\n fail(e);\n }\n });\n }\n\n private async sendBinarySnapshot109(params: {\n cmdId: number;\n channel?: number;\n /** Override the header channelId (and encryption channelId) for this request. */\n channelIdOverride?: number;\n payloadXml?: string;\n extensionXml?: string;\n messageClass?: number;\n streamType?: number;\n encryption?: EncryptionProtocol;\n timeoutMs?: number;\n }): Promise<Buffer> {\n await this.connect();\n\n const channel = params.channel ?? this.opts.channel ?? 0;\n const channelId =\n params.channelIdOverride ??\n (params.channel == null ? this.hostChannelId : channel + 1);\n\n const msgNum = this.nextMsgNum();\n const cmdId = params.cmdId;\n\n // For Snap (cmdId=109) the request uses only channelId (no <binaryData>1</binaryData>).\n // I chunk binari in risposta saranno marcati con <binaryData>1</binaryData> nella Extension.\n const extXml =\n params.extensionXml ??\n (params.channel != null ? buildChannelExtensionXml(channel) : \"\");\n const payloadXml = params.payloadXml ?? \"\";\n\n const messageClass = params.messageClass ?? BC_CLASS_MODERN_24;\n const payloadOffset = Buffer.byteLength(extXml, \"utf8\");\n const bodyLen = payloadOffset + Buffer.byteLength(payloadXml, \"utf8\");\n\n const header = encodeHeader({\n cmdId,\n bodyLen,\n channelId,\n streamType: params.streamType ?? 0,\n msgNum,\n responseCode: 0,\n messageClass,\n payloadOffset,\n });\n\n const enc = params.encryption ?? this.enc;\n const bodyBytes = this.encodeBodyXml(extXml, payloadXml, channelId, enc);\n const wire = Buffer.concat([header, bodyBytes]);\n\n const timeoutMs = params.timeoutMs ?? 15_000;\n const chunks: Buffer[] = [];\n let seenJpegStart = false;\n\n const indexOfJpegSoi = (buf: Buffer): number => {\n // JPEG SOI: FF D8\n for (let i = 0; i + 1 < buf.length; i++) {\n if (buf[i] === 0xff && buf[i + 1] === 0xd8) return i;\n }\n return -1;\n };\n\n const endsWithJpegEoi = (buf: Buffer): boolean => {\n // JPEG EOI: FF D9\n for (let i = 0; i + 1 < buf.length; i++) {\n if (buf[i] === 0xff && buf[i + 1] === 0xd9) return true;\n }\n return false;\n };\n\n return await new Promise<Buffer>((resolve, reject) => {\n let timeout: NodeJS.Timeout | undefined;\n let done = false;\n\n const cleanup = () => {\n this.off(\"frame\", onFrame);\n if (timeout) clearTimeout(timeout);\n };\n\n const finish = (buf: Buffer) => {\n if (done) return;\n done = true;\n cleanup();\n resolve(buf);\n };\n\n const fail = (e: unknown) => {\n if (done) return;\n done = true;\n cleanup();\n reject(e instanceof Error ? e : new Error(String(e)));\n };\n\n const onFrame = (frame: BaichuanFrame) => {\n if (frame.header.cmdId !== cmdId) return;\n\n // If the request itself was rejected, fail fast instead of timing out.\n // Some firmwares respond with an empty-body error for snapshot.\n if (\n frame.header.msgNum === msgNum &&\n frame.header.responseCode >= 400\n ) {\n fail(\n new Error(\n `Baichuan snapshot request rejected (cmdId=${cmdId} msgNum=${msgNum} responseCode=${frame.header.responseCode})`,\n ),\n );\n return;\n }\n\n try {\n // Snapshot flow:\n // - reply 1: XML body (no binaryData)\n // - reply 2..n: Extension has <binaryData>1</binaryData>, payload is binary chunks (responseCode 200/201)\n let isBinaryChunk = false;\n if (frame.extension.length > 0) {\n try {\n const extDec = this.tryDecryptXml(\n frame.extension,\n frame.header.channelId,\n enc,\n );\n if (extDec.includes(\"<binaryData>1</binaryData>\")) {\n isBinaryChunk = true;\n }\n } catch {\n // PCAP: some firmwares send a non-XML (and not AES-decryptable) extension.\n // Treat as \"not marked\" and rely on payload heuristics.\n }\n }\n\n // If extension isn't present/parseable, fallback to heuristic: payload contains JPEG SOI.\n const decrypted = this.tryDecryptBinary(\n frame.payload,\n frame.header.channelId,\n enc,\n );\n if (decrypted.length === 0) return;\n\n const head = decrypted\n .subarray(0, Math.min(16, decrypted.length))\n .toString(\"utf8\");\n const looksLikeXml =\n head.startsWith(\"<?xml\") || head.trimStart().startsWith(\"<\");\n if (!isBinaryChunk && looksLikeXml) return;\n\n let toAppend = decrypted;\n if (!seenJpegStart) {\n const soi = indexOfJpegSoi(decrypted);\n if (soi === -1) {\n // Not JPEG yet; ignore unless this is a declared binary chunk (could start mid-stream).\n if (!isBinaryChunk) return;\n // If it's a binary chunk but doesn't contain SOI, append as-is.\n toAppend = decrypted;\n chunks.push(toAppend);\n const combined = Buffer.concat(chunks);\n if (frame.header.responseCode === 201) finish(combined);\n return;\n }\n seenJpegStart = true;\n toAppend = decrypted.subarray(soi);\n }\n\n chunks.push(toAppend);\n const combined = Buffer.concat(chunks);\n\n // Prefer marker-based completion; some firmwares don't use responseCode 201 reliably.\n if (endsWithJpegEoi(combined) || frame.header.responseCode === 201) {\n // Trim to the first EOI if present.\n const eoiIdx = combined.indexOf(Buffer.from([0xff, 0xd9]));\n if (eoiIdx !== -1) {\n finish(combined.subarray(0, eoiIdx + 2));\n return;\n }\n finish(combined);\n }\n } catch (e) {\n fail(e);\n }\n };\n\n timeout = setTimeout(() => {\n fail(\n new Error(\n `Baichuan timeout waiting snapshot push cmdId=${cmdId} msgNum=${msgNum}`,\n ),\n );\n }, timeoutMs);\n\n // Attach listener BEFORE sending request to avoid missing the first chunk.\n // Use \"frame\" (not \"push\") so we also see frames that would otherwise be consumed by `pending`.\n this.on(\"frame\", onFrame);\n\n try {\n this.logDebug(\"tx\", {\n cmdId,\n msgNum,\n channelId,\n messageClass,\n bodyLen,\n binary: true,\n snapshot: true,\n });\n this.recordTx({\n cmdId,\n responseCode: 0,\n msgNum,\n channelId,\n streamType: params.streamType ?? 0,\n });\n this.writeWire(wire);\n } catch (e) {\n fail(e);\n }\n });\n }\n\n /**\n * Send CoverPreview command (cmd_id=298) to get an I-frame from a past recording.\n * Similar to sendBinarySnapshot109 but handles the stream header + frame format\n * instead of JPEG.\n */\n async sendBinaryCoverPreview(params: {\n cmdId: number;\n channel?: number;\n /** Override the header channelId (and encryption channelId) for this request. */\n channelIdOverride?: number;\n /** Override the header msgNum for this request (advanced). */\n msgNumOverride?: number;\n payloadXml?: string;\n extensionXml?: string;\n messageClass?: number;\n streamType?: number;\n encryption?: EncryptionProtocol;\n timeoutMs?: number;\n /** Maximum retry attempts on 400 rejection (default: 5). PCAP analysis shows camera often rejects first few requests. */\n maxRetries?: number;\n /** Delay between retries in ms (default: 1000). */\n retryDelayMs?: number;\n }): Promise<Buffer> {\n const maxRetries = params.maxRetries ?? 5;\n const retryDelayMs = params.retryDelayMs ?? 1000;\n\n let lastError: Error | undefined;\n\n for (let attempt = 0; attempt < maxRetries; attempt++) {\n try {\n return await this._sendBinaryCoverPreviewOnce(params);\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n lastError = e instanceof Error ? e : new Error(msg);\n\n // Check if it's a 400 rejection that might be recoverable with retry\n const is400Rejection =\n msg.includes(\"rejected\") &&\n (msg.includes(\"responseCode=400\") || msg.includes(\"resp_code=400\"));\n\n if (is400Rejection && attempt < maxRetries - 1) {\n console.log(\n `[CoverPreview] Attempt ${attempt + 1} got 400, retrying in ${retryDelayMs}ms...`,\n );\n await new Promise((resolve) => setTimeout(resolve, retryDelayMs));\n continue;\n }\n\n throw lastError;\n }\n }\n\n throw lastError ?? new Error(\"CoverPreview failed after all retries\");\n }\n\n /**\n * Internal: single attempt for sendBinaryCoverPreview\n */\n private async _sendBinaryCoverPreviewOnce(params: {\n cmdId: number;\n channel?: number;\n channelIdOverride?: number;\n msgNumOverride?: number;\n payloadXml?: string;\n extensionXml?: string;\n messageClass?: number;\n streamType?: number;\n encryption?: EncryptionProtocol;\n timeoutMs?: number;\n }): Promise<Buffer> {\n await this.connect();\n\n const channel = params.channel ?? this.opts.channel ?? 0;\n\n // For CoverPreview, PCAP analysis shows:\n // - channelId in header is a session counter (29,30,32...) - use nextMsgNum()\n // - msgNum in header is always 0\n // So we use the message counter for channelId, but fix msgNum to 0.\n const sessionCounter = this.nextMsgNum();\n const channelId = params.channelIdOverride ?? sessionCounter;\n const msgNum = params.msgNumOverride ?? 0; // PCAP shows msgNum is always 0 for CoverPreview\n\n const cmdId = params.cmdId;\n\n // PCAP: CoverPreview requests use empty extension (payloadOffset=0).\n const extXml = params.extensionXml ?? \"\";\n const payloadXml = params.payloadXml ?? \"\";\n\n const messageClass = params.messageClass ?? BC_CLASS_MODERN_24;\n const payloadOffset = Buffer.byteLength(extXml, \"utf8\");\n const bodyLen = payloadOffset + Buffer.byteLength(payloadXml, \"utf8\");\n\n const header = encodeHeader({\n cmdId,\n bodyLen,\n channelId,\n streamType: params.streamType ?? 0,\n msgNum,\n responseCode: 0,\n messageClass,\n payloadOffset,\n });\n\n const enc = params.encryption ?? this.enc;\n const bodyBytes = this.encodeBodyXml(extXml, payloadXml, channelId, enc);\n const wire = Buffer.concat([header, bodyBytes]);\n\n const timeoutMs = params.timeoutMs ?? 30_000;\n const chunks: Buffer[] = [];\n let seenStreamHeader = false;\n let streamMsgNum: number | undefined;\n let lockedChannelId: number | undefined;\n let lockedStreamType: number | undefined;\n let lastXmlReply: string | undefined;\n const supportedMagics = new Set([\"1001\", \"1002\"]);\n\n const expectedStreamType = params.streamType ?? 0;\n\n const looksLikeAnnexB = (buf: Buffer): boolean => {\n // Look for 0x000001 or 0x00000001 start codes early in the payload.\n const maxScan = Math.min(Math.max(buf.length - 4, 0), 64 * 1024);\n for (let i = 0; i < maxScan; i++) {\n if (buf[i] !== 0x00 || buf[i + 1] !== 0x00) continue;\n if (buf[i + 2] === 0x01) return true;\n if (buf[i + 2] === 0x00 && buf[i + 3] === 0x01) return true;\n }\n return false;\n };\n\n const decryptBinaryForCover = (\n payload: Buffer,\n frameChannelId: number,\n encryptLen?: number,\n ) => {\n // If encryptLen is provided, only decrypt the first encryptLen bytes\n // and keep the rest as-is. This is how Reolink partial encryption works.\n if (\n encryptLen !== undefined &&\n encryptLen > 0 &&\n encryptLen < payload.length\n ) {\n const encryptedPart = payload.subarray(0, encryptLen);\n const clearPart = payload.subarray(encryptLen);\n const decryptedPart = this.tryDecryptBinary(\n encryptedPart,\n frameChannelId,\n enc,\n );\n return Buffer.concat([decryptedPart, clearPart]);\n }\n\n // Helper to score how \"BcMedia-like\" a buffer is\n const scoreBcMediaLike = (b: Buffer): number => {\n if (b.length < 4) return -1;\n const maxScan = Math.min(64 * 1024, b.length - 4);\n let count = 0;\n let first = -1;\n for (let i = 0; i <= maxScan; i++) {\n const magic = b.readUInt32LE(i);\n const isInfoV1 = magic === 0x31303031;\n const isInfoV2 = magic === 0x32303031;\n const isIFrame = magic >= 0x63643030 && magic <= 0x63643039;\n const isPFrame = magic >= 0x63643130 && magic <= 0x63643139;\n const isAac = magic === 0x62773530;\n const isAdpcm = magic === 0x62773130;\n if (\n isInfoV1 ||\n isInfoV2 ||\n isIFrame ||\n isPFrame ||\n isAac ||\n isAdpcm\n ) {\n count++;\n if (first < 0) first = i;\n if (count > 32 && first === 0) break;\n }\n }\n return count * 1000 - (first < 0 ? 50000 : first);\n };\n\n // For BC XOR encryption, some firmwares appear to use an offset different from\n // the response header channelId. Try a small set of plausible offsets.\n if (enc.kind !== \"bc\") {\n // In AES/full_aes mode, video data may NOT be encrypted.\n // Compare raw vs decrypted and choose the one that looks more like BcMedia.\n const decrypted = this.tryDecryptBinary(payload, frameChannelId, enc);\n const rawScore = scoreBcMediaLike(payload);\n const decScore = scoreBcMediaLike(decrypted);\n return decScore > rawScore ? decrypted : payload;\n }\n\n const candidates = [frameChannelId, channelId, 250, 0, 1].filter(\n (n, i, a) => Number.isFinite(n) && a.indexOf(n) === i,\n );\n const decrypted = candidates.map((cid) =>\n this.tryDecryptBinary(payload, cid, enc),\n );\n for (const d of decrypted) {\n if (d.length >= 4) {\n const magic = d.subarray(0, 4).toString(\"ascii\");\n if (supportedMagics.has(magic)) return d;\n }\n }\n return decrypted[0] ?? Buffer.alloc(0);\n };\n\n return await new Promise<Buffer>((resolve, reject) => {\n let timeout: NodeJS.Timeout | undefined;\n let done = false;\n\n const cleanup = () => {\n this.off(\"frame\", onFrame);\n if (timeout) clearTimeout(timeout);\n };\n\n const finish = (buf: Buffer) => {\n if (done) return;\n done = true;\n cleanup();\n resolve(buf);\n };\n\n const fail = (e: unknown) => {\n if (done) return;\n done = true;\n cleanup();\n reject(e instanceof Error ? e : new Error(String(e)));\n };\n\n const onFrame = (frame: BaichuanFrame) => {\n // Accept responses with the same cmdId as the request,\n // OR with cmdId=138 (BC_CMD_ID_COVER_RESPONSE) which is used by standalone cameras\n // for cover data when requesting with cmdId 458-462.\n // Also accept cmdId=0 which is used as ACK in standalone protocol.\n const standaloneCoverCmds = new Set([458, 459, 460, 461, 462]);\n const isStandaloneCover = standaloneCoverCmds.has(cmdId);\n const acceptedCmdIds = isStandaloneCover\n ? new Set([cmdId, 138, 0]) // Standalone: request cmdId, data response (138), ACK (0)\n : new Set([cmdId]);\n\n if (!acceptedCmdIds.has(frame.header.cmdId)) {\n return;\n }\n\n if (\n lockedStreamType !== undefined &&\n frame.header.streamType !== lockedStreamType\n ) {\n return;\n }\n\n // Filter by request context (avoid mixing concurrent CoverPreview attempts).\n // Some NVR firmwares reply with a different channelId than the request.\n if (\n lockedChannelId !== undefined &&\n frame.header.channelId !== lockedChannelId\n ) {\n return;\n }\n\n // Once we lock onto a stream msgNum, ignore everything else.\n // NOTE: For standalone cameras using cmdId 458-462, the correlation is done\n // via response_code field (which contains the original request msgNum) instead\n // of the header msgNum field. So we check both.\n if (streamMsgNum !== undefined) {\n const matchesByMsgNum = frame.header.msgNum === streamMsgNum;\n const matchesByResponseCode =\n isStandaloneCover && frame.header.responseCode === streamMsgNum;\n if (!matchesByMsgNum && !matchesByResponseCode) {\n return;\n }\n }\n\n // If the request itself was rejected, fail fast.\n // For standalone protocol, check both msgNum and responseCode for correlation.\n // ext1=400 (0x190) in cmd_id=0 response seems to indicate rejection.\n const isRejected =\n (frame.header.responseCode >= 400 &&\n frame.header.msgNum === msgNum) ||\n (isStandaloneCover &&\n frame.header.cmdId === 0 &&\n frame.header.payloadOffset === 400 &&\n frame.header.responseCode === msgNum);\n\n if (isRejected) {\n fail(\n new Error(\n `Baichuan CoverPreview request rejected (cmdId=${cmdId} reqChannelId=${channelId} rspChannelId=${frame.header.channelId} reqStreamType=${expectedStreamType} rspStreamType=${frame.header.streamType} msgNum=${frame.header.msgNum} responseCode=${frame.header.responseCode} payloadOffset=${frame.header.payloadOffset})`,\n ),\n );\n return;\n }\n\n try {\n // CoverPreview flow:\n // - reply 1: XML body (no binaryData)\n // - reply 2..n: Extension has <binaryData>1</binaryData>, payload is binary chunks\n let isBinaryChunk = false;\n let encryptLen: number | undefined;\n if (frame.extension.length > 0) {\n const extDec = this.tryDecryptXml(\n frame.extension,\n frame.header.channelId,\n enc,\n );\n if (extDec.includes(\"<binaryData>1</binaryData>\")) {\n isBinaryChunk = true;\n }\n // Extract encryptLen if present - only first N bytes of payload are encrypted\n const encryptLenMatch = extDec.match(\n /<encryptLen>(\\d+)<\\/encryptLen>/i,\n );\n if (encryptLenMatch && encryptLenMatch[1]) {\n encryptLen = parseInt(encryptLenMatch[1], 10);\n }\n }\n\n const decrypted = decryptBinaryForCover(\n frame.payload,\n frame.header.channelId,\n encryptLen,\n );\n\n // Check for end-of-stream marker BEFORE checking payload length,\n // since the end marker frame has bodyLen=0\n const isEndMarker =\n frame.header.responseCode === 201 ||\n frame.header.responseCode === 300;\n const isEndMarkerStandalone =\n isStandaloneCover &&\n frame.header.cmdId === 0 &&\n frame.header.payloadOffset === 300;\n\n if (isEndMarker || isEndMarkerStandalone) {\n const combined = Buffer.concat(chunks);\n finish(combined);\n return;\n }\n\n if (decrypted.length === 0) return;\n\n // Skip XML responses\n const head = decrypted\n .subarray(0, Math.min(16, decrypted.length))\n .toString(\"utf8\");\n const looksLikeXml =\n head.startsWith(\"<?xml\") || head.trimStart().startsWith(\"<\");\n if (!isBinaryChunk && looksLikeXml) {\n // Keep the last XML reply for diagnostics (some firmwares ACK with XML and never send chunks).\n // Limit size to avoid huge errors.\n const asText = decrypted.toString(\"utf8\");\n lastXmlReply =\n asText.length > 2000 ? `${asText.slice(0, 2000)}…` : asText;\n return;\n }\n\n // For CoverPreview, look for stream header magic \"1001\"/\"1002\".\n // Lock onto the msgNum that carries the stream header to avoid mixing\n // frames from other in-flight/previous attempts.\n // For standalone cameras (cmdId 458-462), the correlation is via response_code.\n if (!seenStreamHeader) {\n const streamMagic = decrypted.subarray(0, 4).toString(\"ascii\");\n if (supportedMagics.has(streamMagic)) {\n lockedChannelId = frame.header.channelId;\n lockedStreamType = frame.header.streamType;\n // For standalone protocol, lock on response_code instead of msgNum\n streamMsgNum = isStandaloneCover\n ? frame.header.responseCode\n : frame.header.msgNum;\n seenStreamHeader = true;\n chunks.push(decrypted);\n } else if (looksLikeAnnexB(decrypted)) {\n // Some firmwares appear to send a raw Annex-B payload without a 1001/1002 stream header.\n // Treat the first Annex-B chunk as the start of the stream.\n lockedChannelId = frame.header.channelId;\n lockedStreamType = frame.header.streamType;\n streamMsgNum = isStandaloneCover\n ? frame.header.responseCode\n : frame.header.msgNum;\n seenStreamHeader = true;\n chunks.push(decrypted);\n }\n } else {\n chunks.push(decrypted);\n }\n } catch (e) {\n fail(e);\n }\n };\n\n timeout = setTimeout(() => {\n // If we have data, return what we have instead of failing\n if (chunks.length > 0) {\n const combined = Buffer.concat(chunks);\n finish(combined);\n } else {\n const extra = lastXmlReply\n ? `; lastXmlReply=${JSON.stringify(lastXmlReply)}`\n : \"; lastXmlReply=(none)\";\n fail(\n new Error(\n `Baichuan timeout waiting CoverPreview push cmdId=${cmdId} msgNum=${msgNum}${extra}`,\n ),\n );\n }\n }, timeoutMs);\n\n // Attach listener BEFORE sending request\n this.on(\"frame\", onFrame);\n\n try {\n this.logDebug(\"tx\", {\n cmdId,\n msgNum,\n channelId,\n messageClass,\n bodyLen,\n binary: true,\n coverPreview: true,\n });\n this.recordTx({\n cmdId,\n responseCode: 0,\n msgNum,\n channelId,\n streamType: params.streamType ?? 0,\n });\n this.writeWire(wire);\n } catch (e) {\n fail(e);\n }\n });\n }\n\n /**\n * Decrypts binary data (similar to tryDecryptXml but for binary responses).\n * Public method to allow ReolinkBaichuanApi to decrypt audio frames.\n */\n tryDecryptBinary(\n buf: Buffer,\n channelId: number,\n preferred: EncryptionProtocol,\n ): Buffer {\n if (buf.length === 0) return buf;\n\n const tryAs = (enc: EncryptionProtocol): Buffer | null => {\n try {\n if (enc.kind === \"none\") return buf;\n if (enc.kind === \"bc\") {\n return bcDecrypt(buf, channelId);\n }\n if (enc.kind === \"aes\" || enc.kind === \"full_aes\") {\n return aesDecrypt(buf, enc.key);\n }\n return null;\n } catch {\n return null;\n }\n };\n\n // Try preferred encryption first, then fallback to current encryption, then BC, then none\n return (\n tryAs(preferred) ??\n (preferred.kind !== \"bc\" &&\n this.enc.kind !== \"none\" &&\n (this.enc.kind === \"aes\" || this.enc.kind === \"full_aes\")\n ? tryAs(this.enc)\n : null) ??\n (preferred.kind !== \"bc\" ? tryAs({ kind: \"bc\" }) : null) ??\n tryAs({ kind: \"none\" }) ??\n buf // Return as-is if decryption fails\n );\n }\n\n /**\n * Login flow: legacy \"upgrade\" -> receive nonce/encryption -> modern login.\n */\n async login(maxEncryption: MaxEncryption = \"full_aes\"): Promise<void> {\n if (this.loggedIn) return;\n\n const maxAttempts = 3;\n let lastError: unknown;\n\n // Some NVR/HomeHub firmwares are picky about encryption negotiation.\n // If the nonce/encryption negotiation fails (socket close / timeout), automatically\n // downgrade the requested encryption to keep the connection stable.\n let effectiveMaxEncryption: MaxEncryption = maxEncryption;\n let effectiveHostChannelId = this.hostChannelId;\n\n const sleep = (ms: number) =>\n new Promise<void>((resolve) => setTimeout(resolve, ms));\n\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n try {\n // 1) legacy header-only login upgrade to obtain nonce + encryption type\n\n // 1) legacy header-only login upgrade to obtain nonce + encryption type\n // Encryption negotiation request byte (0xDC??):\n // - 0xDC00: none\n // - 0xDC01: bc\n // - 0xDC02: aes\n // - 0xDC12: full_aes\n // Some firmwares will reset the socket if you request an unsupported mode.\n const encByte =\n effectiveMaxEncryption === \"none\"\n ? 0xdc00\n : effectiveMaxEncryption === \"bc\"\n ? 0xdc01\n : effectiveMaxEncryption === \"aes\"\n ? 0xdc02\n : /* full_aes */ 0xdc12;\n\n await this.connect();\n // legacy login is supported on both transports\n\n const msgNum = this.nextMsgNum();\n const cmdId = 1;\n const channelId = effectiveHostChannelId;\n\n const header = encodeHeader({\n cmdId,\n bodyLen: 0,\n channelId,\n streamType: 0,\n msgNum,\n responseCode: encByte,\n messageClass: BC_CLASS_LEGACY,\n });\n const pendingKey: PendingKey = `${cmdId}:${msgNum}`;\n\n const framePromise = new Promise<BaichuanFrame>((resolve, reject) => {\n const t = setTimeout(() => {\n this.pending.delete(pendingKey);\n reject(new Error(\"Baichuan timeout waiting for nonce\"));\n }, 10_000);\n this.pending.set(pendingKey, {\n resolve: (f) => {\n clearTimeout(t);\n resolve(f);\n },\n reject: (e) => {\n clearTimeout(t);\n reject(e);\n },\n });\n });\n\n // CRITICAL: Add catch handler IMMEDIATELY after promise creation, BEFORE writeWire\n // This must happen synchronously, before any operations that might cause socket to close\n // This prevents unhandled rejections when socket closes during writeWire\n framePromise.catch(() => {\n // Silently handle rejections from socket closures\n // The caller will handle the error when they await the promise\n });\n\n this.writeWire(header); // header-only\n const nonceFrame = await framePromise;\n\n // This reply contains <Encryption><nonce>...</nonce></Encryption> and response_code 0xDD??\n const resp = nonceFrame.header.responseCode;\n if (resp >>> 8 !== 0xdd)\n throw new Error(\n `Baichuan login: expected encryption info (0xDDxx), got 0x${resp.toString(16)}`,\n );\n const encType = resp & 0xff;\n\n // During negotiation the payload is at most BCEncrypt (even if AES is supported).\n const nonceXml = this.tryDecryptXml(\n nonceFrame.body,\n nonceFrame.header.channelId,\n encType === 0x00 ? { kind: \"none\" } : { kind: \"bc\" },\n );\n const nonce = getXmlText(nonceXml, \"nonce\");\n if (!nonce) throw new Error(\"Baichuan login: nonce not found in XML\");\n this.nonce = nonce;\n\n // set encryption mode for post-login traffic\n if (encType === 0x00) this.enc = { kind: \"none\" };\n else if (encType === 0x01) this.enc = { kind: \"bc\" };\n else if (encType === 0x02)\n this.enc = {\n kind: \"aes\",\n key: deriveAesKey(nonce, this.opts.password),\n };\n else if (encType === 0x12)\n this.enc = {\n kind: \"full_aes\",\n key: deriveAesKey(nonce, this.opts.password),\n };\n else\n throw new Error(\n `Baichuan login: unknown encType=0x${encType.toString(16)}`,\n );\n\n // 2) modern login with username/password hashes\n const userHash = md5StrModern(`${this.opts.username}${nonce}`);\n const passHash = md5StrModern(`${this.opts.password}${nonce}`);\n const loginXml = buildLoginXml(userHash, passHash);\n\n this.logDebug(\"login_hash\", {\n username: this.opts.username,\n nonce,\n userHash,\n passHashLength: passHash.length,\n loginXmlLength: loginXml.length,\n loginXmlPreview: loginXml.substring(0, 200),\n });\n\n // For login, explicitly use channelId 250 (host) and no extension XML\n // This ensures correct BCEncrypt channelId offset for firmwares that depend on it.\n // Use sendFrame directly (not sendXml from ReolinkBaichuanApi) to avoid recursion\n // since sendXml might call login() which would cause infinite recursion\n // Don't pass channel to use host channelId (device-specific: 250 or 0)\n // For the login message itself, some firmwares expect BCEncrypt even if AES is supported.\n // However if negotiation says \"none\" (0xDD00), sending BC-encrypted login will fail.\n const loginEnc: EncryptionProtocol =\n encType === 0x00 ? { kind: \"none\" } : { kind: \"bc\" };\n\n const replyFrame = await this.sendFrame({\n cmdId: 1,\n payloadXml: loginXml,\n extensionXml: \"\",\n messageClass: BC_CLASS_MODERN_24,\n channelIdOverride: effectiveHostChannelId,\n encryption: loginEnc,\n timeoutMs: 10_000,\n });\n\n const replyXml = this.tryDecryptXml(\n replyFrame.body,\n replyFrame.header.channelId,\n loginEnc,\n );\n\n // If login succeeded, camera replies with 200 in responseCode on modern frames.\n // responseCode 400 typically means authentication failed (bad credentials)\n // responseCode 200 means success\n this.logDebug(\"login_reply\", {\n replyLength: replyXml.length,\n replyPreview: replyXml.substring(0, 200),\n startsWithXml: replyXml.startsWith(\"<?xml\"),\n });\n\n // Check if reply is empty.\n // This is commonly seen on sleeping/waking battery cameras, but can also indicate bad credentials.\n if (replyXml.length === 0) {\n throw new Error(\n \"Baichuan login failed: empty reply (camera may be sleeping/waking, session may be stale, or username/password may be invalid)\",\n );\n }\n\n if (!replyXml.startsWith(\"<?xml\")) {\n const preview =\n replyXml.length > 0 ? replyXml.substring(0, 100) : \"(empty)\";\n throw new Error(\n `Baichuan login: unexpected non-XML reply (length: ${replyXml.length}, preview: ${preview})`,\n );\n }\n\n this.loggedIn = true;\n // Persist the host channelId variant that worked, so future host-scoped commands use it.\n this.hostChannelId = effectiveHostChannelId;\n return;\n } catch (e) {\n lastError = e;\n this.loggedIn = false;\n\n const msg =\n e && typeof e === \"object\" && \"message\" in e\n ? String((e as any).message)\n : String(e);\n const isNetworkUnreachable =\n msg.includes(\"EHOSTUNREACH\") || msg.includes(\"ENETUNREACH\");\n const looksLikeNegotiationFailure =\n msg.includes(\"timeout waiting for nonce\") ||\n msg.includes(\"expected encryption info\") ||\n msg.includes(\"Baichuan socket closed\") ||\n msg.includes(\"ECONNRESET\") ||\n msg.includes(\"EPIPE\");\n\n // Some NVR/HomeHub firmwares expect host channelId=0 (PCAP-observed). If negotiation fails,\n // automatically try the alternate host channelId on the next attempt.\n if (looksLikeNegotiationFailure && effectiveHostChannelId === 250) {\n effectiveHostChannelId = 0;\n }\n\n // If negotiation is failing, try a less aggressive encryption mode on next attempt.\n // Prefer stepping down full_aes -> aes -> bc -> none.\n if (looksLikeNegotiationFailure) {\n if (effectiveMaxEncryption === \"full_aes\") {\n effectiveMaxEncryption = \"aes\";\n } else if (effectiveMaxEncryption === \"aes\") {\n effectiveMaxEncryption = \"bc\";\n } else if (effectiveMaxEncryption === \"bc\") {\n effectiveMaxEncryption = \"none\";\n }\n }\n try {\n await this.close({\n reason: isNetworkUnreachable\n ? \"tcp_unreachable\"\n : looksLikeNegotiationFailure\n ? \"login_negotiation_failed\"\n : \"login_failed\",\n });\n } catch {\n // ignore\n }\n\n // If we can't route to the host, retrying encryption negotiation won't help.\n // Fail fast so callers like autodetect can fallback to UDP/relay quickly.\n if (isNetworkUnreachable) {\n throw e;\n }\n\n if (attempt < maxAttempts) {\n // Backoff: give the camera time to wake up.\n await sleep(1500);\n continue;\n }\n throw e;\n }\n }\n\n // Should be unreachable.\n throw lastError instanceof Error ? lastError : new Error(String(lastError));\n }\n}\n","/**\n * Helper functions for video streaming and two-way audio integration.\n * Based on implementations from various plugins (wyze, tapo, onvif).\n */\n\nimport type { ReolinkBaichuanApi } from \"../reolink/baichuan/ReolinkBaichuanApi\";\nimport type { NativeVideoStreamVariant, ReolinkEvent, StreamProfile } from \"../reolink/baichuan/types\";\nimport { buildRtspUrl } from \"../rtsp/urls\";\nimport { spawn } from \"node:child_process\";\nimport { BaichuanVideoStream } from \"../baichuan/stream/BaichuanVideoStream\";\n\n/**\n * VideoStream options\n */\nexport interface VideoStreamOptions {\n channel: number;\n profile: StreamProfile; // \"main\" | \"sub\" | \"ext\"\n api: ReolinkBaichuanApi;\n rtspHost: string;\n rtspPort?: number;\n rtspUsername: string;\n rtspPassword: string;\n}\n\n/**\n * MediaStream response\n */\nexport interface MediaStream {\n url?: string;\n container?: string;\n video?: {\n codec: string;\n width?: number;\n height?: number;\n fps?: number;\n };\n audio?: {\n codec: string;\n sampleRate?: number;\n };\n}\n\n/**\n * ResponseMediaStreamOptions - Stream profile metadata\n */\nexport interface ResponseMediaStreamOptions {\n id: string;\n name: string;\n description?: string;\n video?: {\n codec?: string;\n width?: number;\n height?: number;\n fps?: number;\n };\n audio?: {\n codec?: string;\n sampleRate?: number;\n };\n container?: string;\n tool?: string;\n userConfigurable?: boolean;\n}\n\n/**\n * Get video stream metadata and RTSP URL.\n * Similar to wyze getVideoStream implementation.\n * \n * Returns a MediaStream object with stream information and RTSP URL.\n */\nexport async function getVideoStream(options: VideoStreamOptions): Promise<MediaStream> {\n const { channel, profile, api, rtspHost, rtspPort, rtspUsername, rtspPassword } = options;\n\n // Get stream metadata to determine codec and resolution\n const metadata = await api.getStreamMetadata(channel);\n const stream = metadata.streams.find((s) => s.profile === profile);\n\n if (!stream) {\n throw new Error(`Stream profile ${profile} not available for channel ${channel}`);\n }\n\n // Build RTSP URL\n const rtspUrl = buildRtspUrl({\n host: rtspHost,\n ...(rtspPort !== undefined ? { port: rtspPort } : {}),\n username: rtspUsername,\n password: rtspPassword,\n channel,\n stream: profile,\n });\n\n // Map video codec to standard format\n const videoCodecMap: Record<string, string> = {\n \"H.264\": \"h264\",\n \"H.265\": \"hevc\",\n \"MJPEG\": \"mjpeg\",\n \"MPEG4\": \"mpeg4\",\n };\n\n const videoCodec = videoCodecMap[stream.videoEncType] ?? stream.videoEncType.toLowerCase();\n\n const result: MediaStream = {\n url: rtspUrl,\n container: \"rtsp\",\n video: {\n codec: videoCodec,\n width: stream.width,\n height: stream.height,\n fps: stream.frameRate,\n },\n };\n\n // Add audio if enabled\n if (stream.audio === 1) {\n // Reolink typically uses AAC or G.711 for audio\n result.audio = {\n codec: \"aac\", // Default, may need to be determined from stream metadata\n sampleRate: 8000, // Typical for Reolink, may vary\n };\n }\n\n return result;\n}\n\n/**\n * Get constructed video stream options for all available profiles.\n * \n * Returns all available stream profiles (main, sub, ext) with their metadata.\n */\nexport async function getConstructedVideoStreamOptions(\n channel: number,\n api: ReolinkBaichuanApi,\n rtspHost: string,\n rtspPort: number = 554,\n rtspUsername: string = \"admin\",\n rtspPassword: string = \"\"\n): Promise<ResponseMediaStreamOptions[]> {\n // Get stream metadata for all available profiles\n const metadata = await api.getStreamMetadata(channel);\n const options: ResponseMediaStreamOptions[] = [];\n\n // Map video codec to standard format\n const videoCodecMap: Record<string, string> = {\n \"H.264\": \"h264\",\n \"H.265\": \"hevc\",\n \"MJPEG\": \"mjpeg\",\n \"MPEG4\": \"mpeg4\",\n };\n\n // Build options for each available stream\n for (const stream of metadata.streams) {\n const videoCodec = videoCodecMap[stream.videoEncType] ?? stream.videoEncType.toLowerCase();\n const profileName = stream.profile.charAt(0).toUpperCase() + stream.profile.slice(1);\n\n const option: ResponseMediaStreamOptions = {\n id: stream.profile,\n name: `${profileName} Stream`,\n description: `${profileName} stream - ${stream.width}x${stream.height} @ ${stream.frameRate}fps`,\n video: {\n codec: videoCodec,\n width: stream.width,\n height: stream.height,\n fps: stream.frameRate,\n },\n container: \"rtsp\",\n userConfigurable: true,\n };\n\n // Add audio info if enabled\n if (stream.audio === 1) {\n option.audio = {\n codec: \"aac\", // Default, may need to be determined from stream metadata\n sampleRate: 8000, // Typical for Reolink, may vary\n };\n }\n\n options.push(option);\n }\n\n return options;\n}\n\n/**\n * Two-way audio intercom options\n */\nexport interface IntercomOptions {\n channel: number;\n api: ReolinkBaichuanApi;\n}\n\n/**\n * Intercom - Two-way audio support for Reolink cameras via Baichuan protocol.\n * \n * Audio Format Requirements (for sending audio TO camera):\n * =========================================================\n * \n * Reolink Baichuan talk-back expects ADPCM (DVI4/IMA-style) in fixed-size blocks.\n * The exact parameters come from `TalkAbility` (cmd_id=10) and are applied via\n * `TalkConfig` (cmd_id=201). Audio data is then sent as BcMedia ADPCM packets\n * inside Talk (cmd_id=202) payloads.\n *\n * This helper assumes the caller (e.g., ffmpeg) is responsible for producing the correct\n * ADPCM byte stream. No encoding is performed here.\n * \n * Note: Audio reception is handled via the video stream, not through this intercom interface.\n */\nexport class Intercom {\n private api: ReolinkBaichuanApi;\n private channel: number;\n private active = false;\n private session: Awaited<ReturnType<ReolinkBaichuanApi[\"createTalkSession\"]>> | undefined;\n\n constructor(options: IntercomOptions) {\n this.api = options.api;\n this.channel = options.channel;\n }\n\n /**\n * Start two-way audio session.\n * Similar to tapo startIntercom() and ONVIF intercom.\n */\n async start(): Promise<void> {\n if (this.active) {\n throw new Error(\"Intercom session already active\");\n }\n\n // `createTalkSession` validates TalkAbility and sends TalkConfig.\n this.session = await this.api.createTalkSession(this.channel);\n this.active = true;\n }\n\n /**\n * Send audio data to camera.\n * \n * @param audioData - ADPCM byte stream produced by the caller (e.g., ffmpeg).\n * No encoding is performed - data is sent directly to the camera.\n */\n async sendAudio(audioData: Buffer): Promise<void> {\n if (!this.active) {\n throw new Error(\"Intercom session not active\");\n }\n\n if (!this.session) {\n throw new Error(\"Intercom session missing (internal state error)\");\n }\n await this.session.sendAudio(audioData);\n }\n\n /**\n * Stop two-way audio session.\n */\n async stop(): Promise<void> {\n if (!this.active) return;\n\n await this.session?.stop();\n this.session = undefined;\n this.active = false;\n }\n\n isActive(): boolean {\n return this.active;\n }\n}\n\n/**\n * Event handler for Baichuan events.\n * Subscribes to events and emits them in a standard format.\n */\nexport class BaichuanEventEmitter {\n private api: ReolinkBaichuanApi;\n private subscribed = false;\n private onEventHandler: ((event: ReolinkEvent) => void) | undefined;\n\n constructor(api: ReolinkBaichuanApi) {\n this.api = api;\n }\n\n /**\n * Subscribe to events and start emitting them.\n */\n async subscribe(onEvent: (event: ReolinkEvent) => void): Promise<void> {\n if (this.subscribed) return;\n\n // Subscribe to events via Baichuan\n await this.api.subscribeEvents();\n\n // Listen for parsed events\n this.onEventHandler = (event: ReolinkEvent) => onEvent(event);\n this.api.client.on(\"event\", this.onEventHandler);\n\n this.subscribed = true;\n }\n\n /**\n * Unsubscribe from events.\n */\n async unsubscribe(): Promise<void> {\n if (!this.subscribed) return;\n\n await this.api.unsubscribeEvents();\n if (this.onEventHandler) {\n this.api.client.removeListener(\"event\", this.onEventHandler);\n this.onEventHandler = undefined;\n }\n this.subscribed = false;\n }\n\n isSubscribed(): boolean {\n return this.subscribed;\n }\n}\n\n/**\n * Stream frame data for rebroadcast.\n * Similar to Wyze forkAndStream() implementation.\n * \n * Returns an async generator that yields:\n * - audio: boolean (true for audio, false for video)\n * - data: Buffer (raw frame data)\n * - codec: string | null (audio codec name, null for video)\n * - sampleRate: number | null (audio sample rate, null for video)\n * \n * Usage example:\n * ```typescript\n * async *forkAndStream(profile: StreamProfile) {\n * const gen = createNativeStream(this.api, this.channel, profile);\n * for await (const { audio, data, codec, sampleRate } of gen) {\n * yield { audio, data, codec, sampleRate };\n * }\n * }\n * ```\n */\nexport async function* createNativeStream(\n api: ReolinkBaichuanApi,\n channel: number,\n profile: StreamProfile,\n options?: {\n variant?: NativeVideoStreamVariant;\n }\n): AsyncGenerator<{\n audio: boolean;\n data: Buffer;\n codec: string | null;\n sampleRate: number | null;\n microseconds: number | null;\n videoType?: \"H264\" | \"H265\";\n isKeyframe?: boolean;\n}, void, unknown> {\n const videoStream = new BaichuanVideoStream({\n client: api.client,\n api,\n channel,\n profile,\n ...(options?.variant !== undefined ? { variant: options.variant } : {}),\n logger: api.logger,\n });\n\n let videoCodecInfo: { sps: Buffer | null; pps: Buffer | null } | null = null;\n let audioCodec: string | null = null;\n let audioSampleRate: number | null = null;\n let streamStarted = false;\n let closed = false;\n\n const onError = (_error: Error) => {\n closed = true;\n // Do not throw from an event callback: it can crash the process asynchronously.\n // Consumers will observe stream termination.\n };\n\n const onClose = () => {\n closed = true;\n };\n\n try {\n // Handle errors early (start() can fail/timeout asynchronously).\n videoStream.on(\"error\", onError);\n videoStream.on(\"close\", onClose);\n\n // Start the video stream\n await videoStream.start();\n\n // Collect video codec info (SPS/PPS for H.264) from first keyframe\n // Similar to Wyze implementation that writes SPS/PPS to ffmpeg\n const videoCodecInfoPromise = new Promise<{ sps: Buffer | null; pps: Buffer | null }>((resolve) => {\n const handler = (unit: { data: Buffer; isKeyframe: boolean; videoType: \"H264\" | \"H265\" }) => {\n if (unit.isKeyframe && !videoCodecInfo) {\n // Extract SPS/PPS from H.264 keyframe (if available)\n // For H.265, we'd need VPS/SPS/PPS but for now we'll pass raw data\n // The rebroadcast server will handle codec detection\n videoCodecInfo = { sps: null, pps: null };\n videoStream.removeListener(\"videoAccessUnit\" as any, handler);\n resolve(videoCodecInfo);\n }\n };\n videoStream.on(\"videoAccessUnit\" as any, handler);\n\n // Timeout after 5 seconds\n setTimeout(() => {\n if (!videoCodecInfo) {\n videoCodecInfo = { sps: null, pps: null };\n videoStream.removeListener(\"videoAccessUnit\" as any, handler);\n resolve(videoCodecInfo);\n }\n }, 5000);\n });\n\n // Wait for codec info (with timeout)\n await Promise.race([\n videoCodecInfoPromise,\n new Promise((resolve) => setTimeout(resolve, 5000)),\n ]);\n\n // Stream video and audio frames\n const frameQueue: Array<{\n audio: boolean;\n data: Buffer;\n codec: string | null;\n sampleRate: number | null;\n microseconds: number | null;\n videoType?: \"H264\" | \"H265\";\n isKeyframe?: boolean;\n }> = [];\n\n // Prevent unbounded growth if the consumer pauses or is slower than the camera.\n // Live video streaming can safely drop older frames under backpressure.\n const MAX_FRAME_QUEUE = 200;\n\n // When we fall behind on inter-frame codecs (H.264/H.265), dropping arbitrary frames\n // can break decoder reference chains (missing refs / duplicate POC). Instead, when\n // we detect sustained backpressure, we resync by discarding queued frames and then\n // dropping non-keyframes until the next keyframe/IDR arrives.\n let needKeyframeResync = false;\n let lastResyncLogAt = 0;\n\n let frameResolve: (() => void) | null = null;\n\n // Video frame handler\n videoStream.on(\"videoAccessUnit\", (unit: {\n data: Buffer;\n isKeyframe: boolean;\n videoType: \"H264\" | \"H265\";\n microseconds: number;\n }) => {\n if (closed) return;\n\n if (needKeyframeResync && !unit.isKeyframe) {\n return;\n }\n\n if (needKeyframeResync && unit.isKeyframe) {\n needKeyframeResync = false;\n }\n\n frameQueue.push({\n audio: false,\n data: unit.data,\n codec: null,\n sampleRate: null,\n microseconds: unit.microseconds,\n videoType: unit.videoType,\n isKeyframe: unit.isKeyframe,\n });\n\n if (frameQueue.length > MAX_FRAME_QUEUE) {\n frameQueue.length = 0;\n needKeyframeResync = true;\n\n const now = Date.now();\n if (now - lastResyncLogAt > 5000) {\n lastResyncLogAt = now;\n api.logger?.warn?.(\n `[createNativeStream] backpressure overflow (channel=${channel} profile=${profile}); resyncing on next keyframe`,\n );\n }\n }\n\n if (frameResolve) {\n frameResolve();\n frameResolve = null;\n }\n });\n\n // Audio frame handler\n videoStream.on(\"audioFrame\", (frame: Buffer) => {\n if (closed) return;\n\n // Default audio codec for Reolink (typically AAC)\n // This could be enhanced to detect actual codec from stream metadata\n if (!audioCodec) {\n audioCodec = \"aac\"; // Default, may need detection\n audioSampleRate = 8000; // Default, may need detection\n }\n\n frameQueue.push({\n audio: true,\n data: frame,\n codec: audioCodec,\n sampleRate: audioSampleRate,\n microseconds: null,\n });\n\n if (frameQueue.length > MAX_FRAME_QUEUE) {\n frameQueue.splice(0, frameQueue.length - MAX_FRAME_QUEUE);\n }\n\n if (frameResolve) {\n frameResolve();\n frameResolve = null;\n }\n });\n\n streamStarted = true;\n\n // Yield frames as they arrive\n while (!closed) {\n if (frameQueue.length > 0) {\n const frame = frameQueue.shift()!;\n yield frame;\n } else {\n // Wait for next frame\n await new Promise<void>((resolve) => {\n frameResolve = resolve;\n // Timeout after 1 second to check if stream is still active\n setTimeout(() => {\n if (frameResolve === resolve) {\n frameResolve = null;\n resolve();\n }\n }, 1000);\n });\n }\n }\n } finally {\n // Cleanup\n closed = true;\n try {\n await videoStream.stop();\n } catch {\n // Ignore stop errors\n }\n videoStream.removeListener(\"error\", onError);\n videoStream.removeListener(\"close\", onClose);\n }\n}\n\n","/**\n * Baichuan RTSP Server - Builds an RTSP server that serves a Baichuan video stream.\n *\n * Structure:\n * - RTSP server uses ffmpeg -rtsp_flags listen to create RTSP server from stdin\n * - Native stream starts only when at least one client is connected\n * - Native stream stops when no clients are connected\n * - Tracks connected clients\n * - Passes native frames directly to ffmpeg without repacketization\n */\n\nimport { EventEmitter } from \"node:events\";\nimport { spawn } from \"node:child_process\";\nimport * as net from \"node:net\";\nimport * as dgram from \"node:dgram\";\nimport * as crypto from \"node:crypto\";\nimport type { StreamProfile } from \"../../reolink/baichuan/types\";\nimport type { ReolinkBaichuanApi } from \"../../reolink/baichuan/ReolinkBaichuanApi\";\nimport type { NativeVideoStreamVariant } from \"../../reolink/baichuan/types\";\nimport type { Logger } from \"../../debug/DebugConfig\";\nimport { createNativeStream } from \"../../rfc/helpers\";\nimport { createRtspFlow, type RtspFlow, type RtspVideoType } from \"./rtspFlow\";\nimport { convertToAnnexB as convertH264ToAnnexB } from \"./H264Converter\";\nimport {\n convertToAnnexB as convertH265ToAnnexB,\n isH265Irap,\n splitAnnexBToNalPayloads,\n} from \"./H265Converter\";\n\nclass AsyncBoundedQueue<T> {\n private readonly maxItems: number;\n private readonly queue: T[] = [];\n private waiting:\n | {\n resolve: (r: IteratorResult<T>) => void;\n }\n | undefined;\n private closed = false;\n\n constructor(maxItems: number) {\n this.maxItems = Math.max(1, maxItems | 0);\n }\n\n push(item: T): void {\n if (this.closed) return;\n if (this.waiting) {\n const { resolve } = this.waiting;\n this.waiting = undefined;\n resolve({ value: item, done: false });\n return;\n }\n this.queue.push(item);\n if (this.queue.length > this.maxItems) {\n this.queue.splice(0, this.queue.length - this.maxItems);\n }\n }\n\n close(): void {\n if (this.closed) return;\n this.closed = true;\n if (this.waiting) {\n const { resolve } = this.waiting;\n this.waiting = undefined;\n resolve({ value: undefined as any, done: true });\n }\n }\n\n async next(): Promise<IteratorResult<T>> {\n if (this.closed) return { value: undefined as any, done: true };\n const item = this.queue.shift();\n if (item !== undefined) return { value: item, done: false };\n return await new Promise<IteratorResult<T>>((resolve) => {\n this.waiting = { resolve };\n });\n }\n}\n\ntype FanoutOptions<T> = {\n maxQueueItems: number;\n createSource: () => AsyncGenerator<T, void, unknown>;\n onFrame?: (frame: T) => void;\n onError?: (error: unknown) => void;\n};\n\nclass NativeStreamFanout<T> {\n private readonly opts: FanoutOptions<T>;\n private readonly queues = new Map<string, AsyncBoundedQueue<T>>();\n private source: AsyncGenerator<T, void, unknown> | null = null;\n private running = false;\n private pumpPromise: Promise<void> | null = null;\n\n constructor(opts: FanoutOptions<T>) {\n this.opts = opts;\n }\n\n start(): void {\n if (this.running) return;\n this.running = true;\n this.source = this.opts.createSource();\n\n this.pumpPromise = (async () => {\n try {\n for await (const frame of this.source!) {\n try {\n this.opts.onFrame?.(frame);\n } catch {\n // ignore observer errors\n }\n for (const q of this.queues.values()) {\n q.push(frame);\n }\n }\n } catch (e) {\n this.opts.onError?.(e);\n } finally {\n for (const q of this.queues.values()) q.close();\n this.queues.clear();\n }\n })();\n }\n\n subscribe(id: string): AsyncGenerator<T, void, unknown> {\n const q = new AsyncBoundedQueue<T>(this.opts.maxQueueItems);\n this.queues.set(id, q);\n const self = this;\n return (async function* () {\n try {\n while (true) {\n const r = await q.next();\n if (r.done) return;\n yield r.value;\n }\n } finally {\n q.close();\n self.queues.delete(id);\n }\n })();\n }\n\n async stop(): Promise<void> {\n if (!this.running) return;\n this.running = false;\n const src = this.source;\n this.source = null;\n for (const q of this.queues.values()) q.close();\n this.queues.clear();\n try {\n await src?.return(undefined as any);\n } catch {\n // ignore\n }\n try {\n await this.pumpPromise;\n } catch {\n // ignore\n }\n this.pumpPromise = null;\n }\n}\n\nfunction envBool(value: string | undefined, defaultValue: boolean): boolean {\n if (value == null) return defaultValue;\n const v = value.trim().toLowerCase();\n if (v === \"1\" || v === \"true\" || v === \"yes\" || v === \"on\") return true;\n if (v === \"0\" || v === \"false\" || v === \"no\" || v === \"off\") return false;\n return defaultValue;\n}\n\nexport interface BaichuanRtspServerOptions {\n /** API instance (required) */\n api: ReolinkBaichuanApi;\n /** Channel number (required) */\n channel: number;\n /** Stream profile (required) */\n profile: StreamProfile;\n /** Native-only: TrackMix tele/autotrack variants (usually on NVR/Hub). */\n variant?: NativeVideoStreamVariant;\n listenHost?: string; // Host to listen on (default: \"127.0.0.1\")\n listenPort?: number; // Port to listen on (default: 8554)\n path?: string; // RTSP path (e.g. \"/main\" or \"/sub\")\n logger?: Logger;\n\n /**\n * Framing used when sending RTP packets over a TCP stream.\n * - \"rtsp-interleaved\": RTSP interleaved framing: '$' + channel + 2-byte length + RTP packet\n * - \"rfc4571\": RFC4571 framing: 2-byte length + RTP packet\n */\n tcpRtpFraming?: \"rtsp-interleaved\" | \"rfc4571\";\n\n /** Credentials for RTSP authentication (optional) */\n credentials?: Array<{ username: string; password: string }>;\n /** Require authentication for RTSP connections (default: false if no credentials set) */\n requireAuth?: boolean;\n}\n\n/**\n * BaichuanRtspServer - RTSP server that serves a Baichuan video stream.\n *\n * Uses ffmpeg as RTSP server that reads from stdin, passing native frames directly.\n * This approach is simpler and more reliable than manual RTP repacketization.\n *\n * Lifecycle:\n * - Server starts immediately (ffmpeg RTSP server)\n * - Native stream starts only when first client connects\n * - Native stream stops when last client disconnects\n */\nexport class BaichuanRtspServer extends EventEmitter<{\n client: [string]; // Client connesso\n clientDisconnected: [string]; // Client disconnesso\n error: [Error];\n close: [];\n}> {\n private api: ReolinkBaichuanApi;\n private channel: number;\n private profile: StreamProfile;\n private variant: NativeVideoStreamVariant;\n private listenHost: string;\n private listenPort: number;\n private path: string;\n private logger: Logger;\n private tcpRtpFraming: \"rtsp-interleaved\" | \"rfc4571\";\n private active = false;\n private flow: RtspFlow;\n\n // Authentication\n private authCredentials: Array<{ username: string; password: string }> = [];\n private requireAuth: boolean;\n private authNonces = new Map<string, { nonce: string; timestamp: number }>(); // Track nonces per client\n private readonly AUTH_REALM = \"BaichuanRtspServer\";\n private readonly NONCE_TIMEOUT_MS = 300000; // 5 minutes\n\n // Client tracking\n private connectedClients = new Set<string>(); // Set of client IDs (IP:port)\n private nativeStreamActive = false; // Whether the native stream is currently active\n private clientConnectionServer: net.Server | undefined; // TCP server to track connections\n private streamMetadata: {\n frameRate: number;\n width: number;\n height: number;\n } | null = null;\n // Track all client resources for cleanup\n private clientResources = new Map<\n string,\n {\n ffmpeg: ReturnType<typeof spawn> | undefined;\n udpSocket: dgram.Socket | null;\n udpSocketAudio: dgram.Socket | null;\n rtspSocket: net.Socket | null;\n pipelineStarted?: boolean;\n seenFirstVideoKeyframe?: boolean;\n h265WaitStartMs?: number;\n setupTrack0: boolean;\n setupTrack1: boolean;\n isPlaying: boolean;\n track0RtpChannel?: number;\n track0RtcpChannel?: number;\n track1RtpChannel?: number;\n track1RtcpChannel?: number;\n rtpVideoSeq?: number;\n rtpVideoTimestamp?: number;\n rtpVideoBaseMicroseconds?: number;\n rtpVideoBaseTimestamp?: number;\n rtpVideoLastTimestamp?: number;\n rtpVideoSsrc?: number;\n rtpAudioSeq?: number;\n rtpAudioTimestamp?: number;\n rtpAudioSsrc?: number;\n rtpSentVideoConfig?: boolean;\n }\n >();\n\n private isRtspDebugEnabled(): boolean {\n const dbg = this.api.client.getDebugConfig();\n return dbg.debugRtsp || envBool(process.env.BAICHUAN_DEBUG_RTSP, false);\n }\n\n private rtspDebugLog(message: string): void {\n if (!this.isRtspDebugEnabled()) return;\n this.logger.debug(`[BaichuanRtspServer] ${message}`);\n }\n // Track when first frame arrives from camera\n private firstFramePromise: Promise<void> | null = null;\n private firstFrameResolve: (() => void) | null = null;\n private firstFrameReceived = false;\n private firstAudioPromise: Promise<void> | null = null;\n private firstAudioResolve: (() => void) | null = null;\n private firstAudioDetected = false;\n // Audio support (TCP only): AAC with ADTS framing, packetized to RTP (mpeg4-generic).\n private hasAudio = false;\n private audioInfo: {\n codec: \"aac-adts\";\n sampleRate: number;\n channels: number;\n configHex: string;\n } | null = null;\n private audioPrimingFrame: Buffer | null = null;\n // Temporary stream for extracting parameter sets during DESCRIBE\n private tempStreamGenerator: AsyncGenerator<\n {\n audio: boolean;\n data: Buffer;\n codec: string | null;\n sampleRate: number | null;\n microseconds: number | null;\n videoType?: \"H264\" | \"H265\";\n },\n void,\n unknown\n > | null = null;\n\n // Shared native stream fan-out (single camera stream, multiple RTSP clients)\n private nativeFanout: NativeStreamFanout<{\n audio: boolean;\n data: Buffer;\n codec: string | null;\n sampleRate: number | null;\n microseconds: number | null;\n videoType?: \"H264\" | \"H265\";\n }> | null = null;\n private noClientAutoStopTimer: NodeJS.Timeout | undefined;\n\n private static isAdtsAacFrame(b: Buffer): boolean {\n // ADTS syncword: 0xFFF (12 bits)\n return b.length >= 2 && b[0] === 0xff && (b[1]! & 0xf0) === 0xf0;\n }\n\n private static parseAdtsSamplingInfo(\n b: Buffer,\n ): { sampleRate: number; channels: number; configHex: string } | null {\n // Minimal ADTS header parsing to extract sample rate index + channel config.\n // Reference layout:\n // - sampling_frequency_index: bits 2..5 of byte2 (b[2])\n // - channel_configuration: 1 bit in b[2] (LSB) + 2 bits in b[3] (MSBs)\n if (b.length < 7) return null;\n if (!BaichuanRtspServer.isAdtsAacFrame(b)) return null;\n\n const samplingIndex = (b[2]! >> 2) & 0x0f;\n const sampleRates = [\n 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000,\n 11025, 8000, 7350,\n ];\n const sampleRate = sampleRates[samplingIndex] ?? null;\n if (!sampleRate) return null;\n\n const channelConfig = ((b[2]! & 0x01) << 2) | ((b[3]! >> 6) & 0x03);\n const channels = channelConfig === 0 ? 1 : channelConfig;\n\n // ADTS profile (2 bits): 0=Main, 1=LC, 2=SSR. AudioSpecificConfig uses audioObjectType = profile + 1.\n const profile = (b[2]! >> 6) & 0x03;\n const audioObjectType = profile + 1;\n // AudioSpecificConfig (AAC): 5 bits AOT, 4 bits sampling idx, 4 bits channel config.\n const asc =\n (audioObjectType << 11) | (samplingIndex << 7) | (channelConfig << 3);\n const configHex = Buffer.from([(asc >> 8) & 0xff, asc & 0xff]).toString(\n \"hex\",\n );\n return { sampleRate, channels, configHex };\n }\n\n private static parseInterleavedChannels(\n transportHeader: string,\n ): { rtp: number; rtcp: number } | null {\n const m = transportHeader.match(/interleaved\\s*=\\s*(\\d+)\\s*-\\s*(\\d+)/i);\n if (!m) return null;\n const rtp = Number.parseInt(m[1]!, 10);\n const rtcp = Number.parseInt(m[2]!, 10);\n if (!Number.isFinite(rtp) || !Number.isFinite(rtcp)) return null;\n return { rtp, rtcp };\n }\n\n private static splitAnnexBNals(data: Buffer): Buffer[] {\n // Returns NAL units WITHOUT start codes.\n const nals: Buffer[] = [];\n const len = data.length;\n const isStartCodeAt = (i: number): number => {\n // returns start code length (3 or 4) or 0\n if (i + 3 <= len && data[i] === 0x00 && data[i + 1] === 0x00) {\n if (data[i + 2] === 0x01) return 3;\n if (i + 4 <= len && data[i + 2] === 0x00 && data[i + 3] === 0x01)\n return 4;\n }\n return 0;\n };\n\n let i = 0;\n // find first start code\n while (i < len) {\n const sc = isStartCodeAt(i);\n if (sc) break;\n i++;\n }\n while (i < len) {\n const sc = isStartCodeAt(i);\n if (!sc) {\n i++;\n continue;\n }\n const nalStart = i + sc;\n let j = nalStart;\n while (j < len) {\n const sc2 = isStartCodeAt(j);\n if (sc2) break;\n j++;\n }\n if (nalStart < j) {\n const nal = data.subarray(nalStart, j);\n // skip empty/zero-length nals\n if (nal.length > 0) nals.push(nal);\n }\n i = j;\n }\n return nals;\n }\n\n private static stripAdtsHeader(adtsFrame: Buffer): Buffer | null {\n if (!BaichuanRtspServer.isAdtsAacFrame(adtsFrame)) return null;\n if (adtsFrame.length < 7) return null;\n const protectionAbsent = (adtsFrame[1]! & 0x01) === 0x01;\n const headerLen = protectionAbsent ? 7 : 9;\n if (adtsFrame.length <= headerLen) return null;\n return adtsFrame.subarray(headerLen);\n }\n\n constructor(options: BaichuanRtspServerOptions) {\n super();\n this.api = options.api;\n this.channel = options.channel;\n this.profile = options.profile;\n this.variant = options.variant ?? \"default\";\n this.listenHost = options.listenHost ?? \"127.0.0.1\";\n this.listenPort = options.listenPort ?? 8554;\n this.path = options.path ?? `/stream/${this.profile}`;\n this.logger = options.logger ?? console;\n this.tcpRtpFraming = options.tcpRtpFraming ?? \"rfc4571\";\n\n // Authentication settings\n this.authCredentials = options.credentials ?? [];\n this.requireAuth = options.requireAuth ?? this.authCredentials.length > 0;\n\n // Default flow is conservative (tcp+h264); it will be refined from metadata or first frames.\n const transport = this.api.client.getTransport();\n this.flow = createRtspFlow(transport, \"H264\");\n }\n\n // --- Authentication helpers ---\n\n /**\n * Generate a new nonce for Digest authentication\n */\n private generateNonce(): string {\n return crypto.randomBytes(16).toString(\"hex\");\n }\n\n /**\n * Get or create a nonce for a client\n */\n private getNonceForClient(clientId: string): string {\n const existing = this.authNonces.get(clientId);\n const now = Date.now();\n\n // Clean up old nonces\n for (const [id, data] of this.authNonces) {\n if (now - data.timestamp > this.NONCE_TIMEOUT_MS) {\n this.authNonces.delete(id);\n }\n }\n\n if (existing && now - existing.timestamp < this.NONCE_TIMEOUT_MS) {\n return existing.nonce;\n }\n\n const nonce = this.generateNonce();\n this.authNonces.set(clientId, { nonce, timestamp: now });\n return nonce;\n }\n\n /**\n * Parse Digest Authorization header\n */\n private parseDigestAuth(authHeader: string): Record<string, string> | null {\n if (!authHeader.toLowerCase().startsWith(\"digest \")) return null;\n\n const params: Record<string, string> = {};\n const regex = /(\\w+)=(?:\"([^\"]+)\"|([^\\s,]+))/g;\n let match: RegExpExecArray | null;\n\n while ((match = regex.exec(authHeader)) !== null) {\n const key = match[1]!.toLowerCase();\n const value = match[2] ?? match[3]!;\n params[key] = value;\n }\n\n return params;\n }\n\n /**\n * Calculate MD5 hash\n */\n private md5(data: string): string {\n return crypto.createHash(\"md5\").update(data).digest(\"hex\");\n }\n\n /**\n * Validate Digest authentication against any of the configured credentials\n */\n private validateDigestAuth(\n authHeader: string,\n method: string,\n uri: string,\n clientId: string,\n ): boolean {\n if (this.authCredentials.length === 0) return false;\n\n const params = this.parseDigestAuth(authHeader);\n if (!params) return false;\n\n const { username, realm, nonce, uri: authUri, response } = params;\n\n // Validate required fields\n if (!username || !realm || !nonce || !response) return false;\n\n // Validate nonce (must match what we issued to this client)\n const clientNonceData = this.authNonces.get(clientId);\n if (!clientNonceData || clientNonceData.nonce !== nonce) {\n this.rtspDebugLog(`Auth failed: nonce mismatch for client ${clientId}`);\n return false;\n }\n\n // Try to match against any configured credential\n for (const cred of this.authCredentials) {\n if (username !== cred.username) continue;\n\n // Calculate expected response for this credential\n // HA1 = MD5(username:realm:password)\n const ha1 = this.md5(`${cred.username}:${realm}:${cred.password}`);\n // HA2 = MD5(method:uri)\n const ha2 = this.md5(`${method}:${authUri || uri}`);\n // Response = MD5(HA1:nonce:HA2)\n const expectedResponse = this.md5(`${ha1}:${nonce}:${ha2}`);\n\n if (response === expectedResponse) {\n this.rtspDebugLog(\n `Auth successful for client ${clientId} with user ${username}`,\n );\n return true;\n }\n }\n\n this.rtspDebugLog(\n `Auth failed: no matching credentials for user ${username}`,\n );\n return false;\n }\n\n /**\n * Generate WWW-Authenticate header for 401 response\n */\n private generateWwwAuthenticateHeader(clientId: string): string {\n const nonce = this.getNonceForClient(clientId);\n return `Digest realm=\"${this.AUTH_REALM}\", nonce=\"${nonce}\"`;\n }\n\n // --- End Authentication helpers ---\n\n private clearNoClientAutoStopTimer(): void {\n if (this.noClientAutoStopTimer) {\n clearTimeout(this.noClientAutoStopTimer);\n this.noClientAutoStopTimer = undefined;\n }\n }\n\n private setFlowVideoType(videoType: RtspVideoType, reason: string): void {\n if (this.flow.videoType === videoType) return;\n const transport = this.api.client.getTransport();\n this.flow.stopKeepAlive();\n this.flow = createRtspFlow(transport, videoType);\n this.rtspDebugLog(`Using RTSP flow ${this.flow.key} (${reason})`);\n }\n\n /**\n * Start the RTSP server.\n */\n async start(): Promise<void> {\n if (this.active) {\n throw new Error(\"RTSP server is already active\");\n }\n\n // Get stream metadata\n try {\n const metadata = await this.api.getStreamMetadata(this.channel);\n const stream = metadata.streams.find((s) => s.profile === this.profile);\n if (stream) {\n this.streamMetadata = {\n frameRate: stream.frameRate || 25,\n width: stream.width,\n height: stream.height,\n };\n // Detect video type from metadata (refines flow early, before first frame).\n const enc = String(stream.videoEncType ?? \"\")\n .trim()\n .toLowerCase();\n const metaVideoType: RtspVideoType =\n enc.includes(\"265\") || enc.includes(\"hevc\") ? \"H265\" : \"H264\";\n this.setFlowVideoType(metaVideoType, \"metadata\");\n }\n } catch (error) {\n this.logger.warn(\n `[BaichuanRtspServer] Could not get stream metadata: ${error}`,\n );\n this.streamMetadata = { frameRate: 25, width: 1920, height: 1080 };\n this.setFlowVideoType(\"H264\", \"metadata unavailable\");\n }\n\n // Start TCP server to handle RTSP connections\n this.clientConnectionServer = net.createServer((socket) => {\n this.handleRtspConnection(socket);\n });\n\n // Start listening\n await new Promise<void>((resolve, reject) => {\n this.clientConnectionServer!.listen(\n this.listenPort,\n this.listenHost,\n () => {\n // Update listenPort with the actual assigned port (in case listenPort was 0)\n const address = this.clientConnectionServer!.address();\n if (address && typeof address === \"object\" && \"port\" in address) {\n this.listenPort = address.port;\n }\n resolve();\n },\n );\n this.clientConnectionServer!.on(\"error\", (error) => {\n reject(error);\n });\n });\n\n this.active = true;\n this.logger.info(\n `[BaichuanRtspServer] RTSP server started on ${this.listenHost}:${this.listenPort}, path: ${this.path}`,\n );\n }\n\n /**\n * Handle RTSP connection from a client.\n */\n private handleRtspConnection(socket: net.Socket): void {\n const clientId = `${socket.remoteAddress}:${socket.remotePort}`;\n this.logger.info(`[BaichuanRtspServer] RTSP client connected: ${clientId}`);\n\n let sessionId = \"\";\n let buffer = Buffer.alloc(0);\n let clientFfmpeg: ReturnType<typeof spawn> | undefined;\n let useTcpInterleaved = false;\n let clientUdpSocket: dgram.Socket | null = null;\n let clientUdpSocketAudio: dgram.Socket | null = null;\n\n const cleanup = () => {\n this.removeClient(clientId);\n\n // Clean up authentication nonce for this client\n this.authNonces.delete(clientId);\n\n // Remove from tracking\n const resources = this.clientResources.get(clientId);\n if (resources) {\n // Kill ffmpeg process\n if (resources.ffmpeg) {\n try {\n resources.ffmpeg.stdin?.end();\n resources.ffmpeg.kill(\"SIGTERM\");\n setTimeout(() => {\n try {\n resources.ffmpeg?.kill(\"SIGKILL\");\n } catch {}\n }, 1000);\n } catch {}\n }\n\n // Close UDP sockets\n if (resources.udpSocket) {\n try {\n resources.udpSocket.close();\n } catch {}\n }\n if ((resources as any).udpSocketAudio) {\n try {\n ((resources as any).udpSocketAudio as dgram.Socket).close();\n } catch {}\n }\n\n // Close RTSP socket if still open\n if (resources.rtspSocket && !resources.rtspSocket.destroyed) {\n try {\n resources.rtspSocket.destroy();\n } catch {}\n }\n\n this.clientResources.delete(clientId);\n }\n\n // Also cleanup local variables\n if (clientFfmpeg) {\n try {\n clientFfmpeg.stdin?.end();\n clientFfmpeg.kill(\"SIGTERM\");\n setTimeout(() => {\n try {\n clientFfmpeg?.kill(\"SIGKILL\");\n } catch {}\n }, 1000);\n } catch {}\n clientFfmpeg = undefined;\n }\n\n if (clientUdpSocket) {\n try {\n clientUdpSocket.close();\n } catch {}\n clientUdpSocket = null;\n }\n\n if (clientUdpSocketAudio) {\n try {\n clientUdpSocketAudio.close();\n } catch {}\n clientUdpSocketAudio = null;\n }\n };\n\n socket.on(\"close\", cleanup);\n socket.on(\"error\", (error) => {\n if (\n error &&\n typeof error === \"object\" &&\n \"code\" in error &&\n error.code !== \"EPIPE\"\n ) {\n this.logger.error(`[BaichuanRtspServer] RTSP client error:`, error);\n }\n cleanup();\n });\n\n socket.on(\"data\", async (data: Buffer) => {\n buffer = Buffer.concat([buffer, data]);\n\n while (buffer.includes(\"\\r\\n\\r\\n\")) {\n const endIndex = buffer.indexOf(\"\\r\\n\\r\\n\");\n const requestText = buffer.subarray(0, endIndex).toString();\n buffer = buffer.subarray(endIndex + 4);\n\n if (!requestText.trim()) continue;\n\n const lines = requestText.split(\"\\r\\n\");\n const requestLine = lines[0]?.split(\" \");\n if (!requestLine || requestLine.length < 3) continue;\n\n const method = requestLine[0];\n const url = requestLine[1];\n const version = requestLine[2];\n\n const cseqMatch = requestText.match(/CSeq:\\s*(\\d+)/i);\n const cseq = cseqMatch ? parseInt(cseqMatch[1] ?? \"0\", 10) : 0;\n\n const sendResponse = (\n statusCode: number,\n statusText: string,\n headers: Record<string, string> = {},\n body?: string,\n ) => {\n let response = `${version} ${statusCode} ${statusText}\\r\\n`;\n response += `CSeq: ${cseq}\\r\\n`;\n for (const [key, value] of Object.entries(headers)) {\n response += `${key}: ${value}\\r\\n`;\n }\n if (body) {\n response += `Content-Length: ${Buffer.byteLength(body, \"utf8\")}\\r\\n`;\n }\n response += \"\\r\\n\";\n if (body) {\n response += body;\n }\n socket.write(response);\n };\n\n this.rtspDebugLog(`RTSP ${method} ${url}`);\n\n // --- Authentication check ---\n if (this.requireAuth) {\n const authMatch = requestText.match(/Authorization:\\s*([^\\r\\n]+)/i);\n const authHeader = authMatch?.[1] ?? \"\";\n\n // Allow OPTIONS without authentication (RFC 2617 recommends this)\n if (method !== \"OPTIONS\") {\n if (!authHeader) {\n // No Authorization header - send 401 challenge\n this.rtspDebugLog(\n `Auth required, sending 401 challenge to ${clientId}`,\n );\n sendResponse(401, \"Unauthorized\", {\n \"WWW-Authenticate\":\n this.generateWwwAuthenticateHeader(clientId),\n });\n continue;\n }\n\n // Validate the Authorization header\n if (\n !this.validateDigestAuth(\n authHeader,\n method ?? \"\",\n url ?? \"\",\n clientId,\n )\n ) {\n // Invalid credentials - send 401 with new nonce\n this.rtspDebugLog(`Auth failed for ${clientId}, sending 401`);\n // Generate a new nonce for retry\n this.authNonces.delete(clientId);\n sendResponse(401, \"Unauthorized\", {\n \"WWW-Authenticate\":\n this.generateWwwAuthenticateHeader(clientId),\n });\n continue;\n }\n }\n }\n // --- End Authentication check ---\n\n if (method === \"OPTIONS\") {\n sendResponse(200, \"OK\", {\n Public: \"DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE, OPTIONS\",\n });\n } else if (method === \"DESCRIBE\") {\n // Best-effort priming: try to include parameter sets in SDP.\n // For H.264, ffmpeg often needs SPS/PPS (or it can't determine width/height/extradata).\n if (!this.firstFrameReceived && this.connectedClients.size === 0) {\n try {\n if (!this.nativeStreamActive) {\n await this.startNativeStream();\n }\n } catch (error) {\n this.logger.warn(\n `[BaichuanRtspServer] Failed to start native stream for SDP priming: ${error}`,\n );\n }\n\n const { hasParamSets } = this.flow.getFmtp();\n if (!hasParamSets) {\n // Wait a bit (seconds, not tens of seconds) to avoid ffmpeg failing on missing params,\n // but still keep DESCRIBE responsive.\n const primingMs =\n this.api.client.getTransport() === \"udp\" ? 4000 : 1500;\n try {\n await Promise.race([\n this.firstFramePromise || Promise.resolve(),\n new Promise((resolve) => setTimeout(resolve, primingMs)),\n ]);\n } catch {\n // ignore\n }\n }\n }\n\n // Generate SDP (parameter sets will be included if available)\n {\n const { fmtp, hasParamSets } = this.flow.getFmtp();\n const fmtpPreview =\n fmtp.length > 160 ? `${fmtp.slice(0, 160)}...` : fmtp;\n this.logger.info(\n `[BaichuanRtspServer] DESCRIBE SDP for ${clientId} path=${this.path} codec=${this.flow.sdpCodec} hasParamSets=${hasParamSets} fmtp=${fmtpPreview}`,\n );\n if (!hasParamSets) {\n this.rtspDebugLog(\n `DESCRIBE responding without parameter sets yet (client=${clientId}, path=${this.path}, flow=${this.flow.key})`,\n );\n }\n }\n const sdp = this.generateSdp();\n sendResponse(\n 200,\n \"OK\",\n {\n \"Content-Type\": \"application/sdp\",\n \"Content-Base\": `rtsp://${this.listenHost}:${this.listenPort}${this.path}/`,\n },\n sdp,\n );\n } else if (method === \"SETUP\") {\n const isTrack0 = url?.includes(\"track0\");\n const isTrack1 = url?.includes(\"track1\");\n if (!isTrack0 && !isTrack1) {\n sendResponse(404, \"Not Found\", {\n Session:\n sessionId ||\n `session_${Date.now()}_${Math.random().toString(36).substring(7)}`,\n });\n continue;\n }\n\n // Only accept track1 if we advertised audio in SDP.\n if (isTrack1 && !this.hasAudio) {\n sendResponse(404, \"Not Found\", {\n Session:\n sessionId ||\n `session_${Date.now()}_${Math.random().toString(36).substring(7)}`,\n });\n continue;\n }\n\n // Add client first\n this.connectedClients.add(clientId);\n this.emit(\"client\", clientId);\n this.clearNoClientAutoStopTimer();\n\n // Start native stream if first client\n if (this.connectedClients.size === 1 && !this.nativeStreamActive) {\n await this.startNativeStream();\n }\n\n // Parse transport\n const transportMatch = requestText.match(/Transport:\\s*([^\\r\\n]+)/i);\n const transport = (transportMatch?.[1] ?? \"\").trim();\n useTcpInterleaved = transport\n ? transport.includes(\"TCP\") || transport.includes(\"tcp\")\n : true; // Default to TCP\n\n // Generate session ID (must stay stable across SETUP for multiple tracks)\n if (!sessionId) {\n sessionId = `session_${Date.now()}_${Math.random().toString(36).substring(7)}`;\n }\n\n // Track client resources\n const existing = this.clientResources.get(clientId);\n if (!existing) {\n this.clientResources.set(clientId, {\n ffmpeg: undefined,\n udpSocket: null,\n udpSocketAudio: null,\n rtspSocket: socket,\n pipelineStarted: false,\n seenFirstVideoKeyframe: false,\n setupTrack0: false,\n setupTrack1: false,\n isPlaying: false,\n });\n } else {\n // Keep existing state across multiple SETUP requests (track0 + track1).\n existing.rtspSocket = socket;\n }\n\n // Record requested interleaved channels (ffmpeg can choose them).\n if (useTcpInterleaved) {\n const resources = this.clientResources.get(clientId) as any;\n const requested =\n BaichuanRtspServer.parseInterleavedChannels(transport);\n if (resources) {\n if (isTrack1) {\n const ch = requested ?? { rtp: 2, rtcp: 3 };\n resources.track1RtpChannel = ch.rtp;\n resources.track1RtcpChannel = ch.rtcp;\n } else {\n const ch = requested ?? { rtp: 0, rtcp: 1 };\n resources.track0RtpChannel = ch.rtp;\n resources.track0RtcpChannel = ch.rtcp;\n }\n }\n }\n\n // Start the media pipeline for this client once (on first SETUP, track0 or track1).\n // Note: in direct-RTP mode there is no ffmpeg process, so we must not rely on `clientFfmpeg`.\n {\n const resources = this.clientResources.get(clientId) as any;\n if (resources && !resources.pipelineStarted) {\n resources.pipelineStarted = true;\n await this.startClientFfmpeg(\n clientId,\n socket,\n useTcpInterleaved,\n (proc, udpSock, udpSockAudio) => {\n clientFfmpeg = proc;\n clientUdpSocket = udpSock;\n clientUdpSocketAudio = udpSockAudio;\n const r = this.clientResources.get(clientId) as any;\n if (r) {\n r.ffmpeg = proc;\n r.udpSocket = udpSock;\n r.udpSocketAudio = udpSockAudio;\n }\n },\n );\n }\n }\n\n // Mark track SETUP state (used to gate interleaved RTP forwarding).\n {\n const resources = this.clientResources.get(clientId) as any;\n if (resources) {\n if (isTrack1) resources.setupTrack1 = true;\n else resources.setupTrack0 = true;\n this.rtspDebugLog(\n `SETUP done for ${clientId}: track0=${!!resources.setupTrack0} track1=${!!resources.setupTrack1} playing=${!!resources.isPlaying}`,\n );\n }\n }\n\n if (useTcpInterleaved) {\n const resources = this.clientResources.get(clientId) as any;\n const fallback = isTrack1\n ? { rtp: 2, rtcp: 3 }\n : { rtp: 0, rtcp: 1 };\n const rtp = isTrack1\n ? (resources?.track1RtpChannel ?? fallback.rtp)\n : (resources?.track0RtpChannel ?? fallback.rtp);\n const rtcp = isTrack1\n ? (resources?.track1RtcpChannel ?? fallback.rtcp)\n : (resources?.track0RtcpChannel ?? fallback.rtcp);\n const interleaved = `${rtp}-${rtcp}`;\n sendResponse(200, \"OK\", {\n Transport: `RTP/AVP/TCP;unicast;interleaved=${interleaved}`,\n Session: sessionId,\n });\n } else {\n // UDP transport to RTSP client is not the main focus here; keep existing behavior.\n sendResponse(200, \"OK\", {\n Transport: `RTP/AVP/UDP;unicast;client_port=5004-5005;server_port=5004-5005`,\n Session: sessionId,\n });\n }\n } else if (method === \"PLAY\") {\n {\n const resources = this.clientResources.get(clientId) as any;\n if (resources) {\n resources.isPlaying = true;\n this.rtspDebugLog(\n `PLAY for ${clientId}: track0=${!!resources.setupTrack0} track1=${!!resources.setupTrack1} playing=${!!resources.isPlaying}`,\n );\n }\n }\n sendResponse(200, \"OK\", {\n Session: sessionId,\n Range: \"npt=0.000-\",\n });\n } else if (method === \"TEARDOWN\") {\n cleanup();\n sendResponse(200, \"OK\", {\n Session: sessionId,\n });\n socket.end();\n } else {\n sendResponse(501, \"Not Implemented\");\n }\n }\n });\n }\n\n /**\n * Generate SDP (Session Description Protocol) for RTSP DESCRIBE.\n */\n private generateSdp(): string {\n const codec = this.flow.sdpCodec;\n const videoPayloadType = 96;\n const audioPayloadType = 97;\n\n let sdp = \"v=0\\r\\n\";\n sdp += `o=- ${Date.now()} ${Date.now()} IN IP4 ${this.listenHost}\\r\\n`;\n sdp += \"s=Baichuan Stream\\r\\n\";\n sdp += `c=IN IP4 ${this.listenHost}\\r\\n`;\n sdp += \"t=0 0\\r\\n\";\n\n // Video track\n sdp += `m=video 0 RTP/AVP ${videoPayloadType}\\r\\n`;\n sdp += `a=rtpmap:${videoPayloadType} ${codec}/90000\\r\\n`;\n if (this.streamMetadata?.frameRate) {\n sdp += `a=framerate:${this.streamMetadata.frameRate}\\r\\n`;\n }\n if (this.streamMetadata?.width && this.streamMetadata?.height) {\n sdp += `a=framesize:${videoPayloadType} ${this.streamMetadata.width}-${this.streamMetadata.height}\\r\\n`;\n }\n sdp += `a=control:track0\\r\\n`;\n\n const { fmtp, hasParamSets } = this.flow.getFmtp();\n if (!hasParamSets) {\n this.logger.warn(\n `[BaichuanRtspServer] SDP missing parameter sets for flow ${this.flow.key}`,\n );\n }\n\n if (fmtp) {\n sdp += `a=fmtp:${videoPayloadType} ${fmtp}\\r\\n`;\n }\n\n // Audio track (TCP only).\n // We packetize AAC (ADTS) as RTP mpeg4-generic, with config derived from ADTS.\n if (this.hasAudio) {\n sdp += `m=audio 0 RTP/AVP ${audioPayloadType}\\r\\n`;\n const a = this.audioInfo;\n const rate = a?.sampleRate ?? 8000;\n const ch = a?.channels ?? 1;\n const cfg = a?.configHex ?? \"\";\n sdp += `a=rtpmap:${audioPayloadType} mpeg4-generic/${rate}/${ch}\\r\\n`;\n if (cfg) {\n sdp += `a=fmtp:${audioPayloadType} streamtype=5; profile-level-id=15; mode=AAC-hbr; config=${cfg}; SizeLength=13; IndexLength=3; IndexDeltaLength=3;\\r\\n`;\n }\n sdp += `a=control:track1\\r\\n`;\n }\n\n sdp += `a=setup:passive\\r\\n`;\n sdp += `a=connection:new\\r\\n`;\n\n return sdp;\n }\n\n /**\n * Start ffmpeg for a specific client.\n */\n private async startClientFfmpeg(\n clientId: string,\n rtspSocket: net.Socket,\n useTcpInterleaved: boolean,\n onProcess: (\n proc: ReturnType<typeof spawn> | undefined,\n udpSock: dgram.Socket | null,\n udpSockAudio: dgram.Socket | null,\n ) => void,\n ): Promise<void> {\n // Re-fetch stream metadata to ensure we have the correct frame rate for this profile\n let streamMetadata = this.streamMetadata;\n if (!streamMetadata || !streamMetadata.frameRate) {\n try {\n const metadata = await this.api.getStreamMetadata(this.channel);\n const stream = metadata.streams.find((s) => s.profile === this.profile);\n if (stream) {\n streamMetadata = {\n frameRate: stream.frameRate || 25,\n width: stream.width,\n height: stream.height,\n };\n this.rtspDebugLog(\n `Fetched metadata for profile ${this.profile}: ${streamMetadata.frameRate} fps`,\n );\n }\n } catch (error) {\n this.logger.warn(\n `[BaichuanRtspServer] Could not fetch stream metadata: ${error}`,\n );\n streamMetadata = { frameRate: 25, width: 1920, height: 1080 };\n }\n }\n\n const ffmpegFormat = this.flow.ffmpegFormat;\n\n // For TCP interleaved we can either:\n // - packetize locally (direct RTP), or\n // - use ffmpeg as a packetizer and forward RTP.\n // The ffmpeg path proved flaky; prefer direct RTP for TCP interleaved.\n let localUdpPort = 0;\n let localUdpPortAudio = 0;\n let udpSocket: dgram.Socket | null = null;\n let udpSocketAudio: dgram.Socket | null = null;\n\n const useDirectRtp = useTcpInterleaved;\n\n const frameRtpOverTcp = (channel: number, rtpPacket: Buffer): Buffer => {\n // If the RTSP client negotiated TCP interleaved transport, we MUST use RTSP interleaved framing.\n // RFC4571 is only valid on a raw TCP transport carrying RTP, not on RTSP interleaved.\n const framing = useTcpInterleaved\n ? \"rtsp-interleaved\"\n : this.tcpRtpFraming;\n\n if (framing === \"rfc4571\") {\n const h = Buffer.alloc(2);\n h.writeUInt16BE(rtpPacket.length & 0xffff, 0);\n return Buffer.concat([h, rtpPacket]);\n }\n const h = Buffer.alloc(4);\n h[0] = 0x24; // '$'\n h[1] = channel & 0xff;\n h[2] = (rtpPacket.length >> 8) & 0xff;\n h[3] = rtpPacket.length & 0xff;\n return Buffer.concat([h, rtpPacket]);\n };\n\n if (useTcpInterleaved && !useDirectRtp) {\n localUdpPort = 50000 + Math.floor(Math.random() * 10000);\n udpSocket = dgram.createSocket(\"udp4\");\n\n await new Promise<void>((resolve, reject) => {\n udpSocket!.once(\"listening\", () => resolve());\n udpSocket!.once(\"error\", reject);\n udpSocket!.bind(localUdpPort, \"127.0.0.1\");\n });\n\n const sendInterleaved = (channel: number, msg: Buffer): boolean => {\n if (!rtspSocket || rtspSocket.destroyed || !rtspSocket.writable)\n return false;\n if (msg.length < 12) return false;\n\n const version = (msg[0]! >> 6) & 0x3;\n if (version !== 2) return false;\n\n // Gate forwarding until the RTSP client has completed SETUP and PLAY.\n const resources = this.clientResources.get(clientId) as any;\n if (!resources?.isPlaying) return false;\n const videoRtpChannel = resources?.track0RtpChannel ?? 0;\n const audioRtpChannel = resources?.track1RtpChannel ?? 2;\n if (channel === videoRtpChannel && !resources?.setupTrack0)\n return false;\n if (channel === audioRtpChannel && !resources?.setupTrack1)\n return false;\n\n try {\n return rtspSocket.write(frameRtpOverTcp(channel, msg));\n } catch (error) {\n if (\n error &&\n typeof error === \"object\" &&\n \"code\" in error &&\n (error as any).code === \"EPIPE\"\n )\n return false;\n }\n\n return false;\n };\n\n if (udpSocket) {\n const resources = this.clientResources.get(clientId) as any;\n const videoRtpChannel = resources?.track0RtpChannel ?? 0;\n let rtpPacketCount = 0;\n let firstSeen = false;\n let firstForwarded = false;\n udpSocket.on(\"message\", (msg: Buffer) => {\n if (!firstSeen) {\n firstSeen = true;\n this.rtspDebugLog(\n `First video RTP packet received from ffmpeg for client ${clientId} (len=${msg.length})`,\n );\n }\n\n const forwarded = sendInterleaved(videoRtpChannel, msg);\n if (forwarded && !firstForwarded) {\n firstForwarded = true;\n this.rtspDebugLog(\n `First video RTP packet forwarded via TCP interleaved for client ${clientId}`,\n );\n }\n rtpPacketCount++;\n if (rtpPacketCount % 1000 === 0) {\n this.rtspDebugLog(\n `Forwarded ${rtpPacketCount} RTP packets to client ${clientId} via TCP interleaved`,\n );\n }\n });\n }\n\n if (this.hasAudio) {\n localUdpPortAudio =\n localUdpPort + 2 + Math.floor(Math.random() * 1000) * 2;\n udpSocketAudio = dgram.createSocket(\"udp4\");\n await new Promise<void>((resolve, reject) => {\n udpSocketAudio!.once(\"listening\", () => resolve());\n udpSocketAudio!.once(\"error\", reject);\n udpSocketAudio!.bind(localUdpPortAudio, \"127.0.0.1\");\n });\n let audioPacketCount = 0;\n let firstSeenAudio = false;\n let firstForwardedAudio = false;\n udpSocketAudio.on(\"message\", (msg: Buffer) => {\n const resources = this.clientResources.get(clientId) as any;\n const audioRtpChannel = resources?.track1RtpChannel ?? 2;\n audioPacketCount++;\n if (!firstSeenAudio) {\n firstSeenAudio = true;\n this.rtspDebugLog(\n `First audio RTP packet received from ffmpeg for client ${clientId} (len=${msg.length})`,\n );\n }\n const forwarded = sendInterleaved(audioRtpChannel, msg);\n if (forwarded && !firstForwardedAudio) {\n firstForwardedAudio = true;\n this.rtspDebugLog(\n `First audio RTP packet forwarded via TCP interleaved for client ${clientId}`,\n );\n }\n if (audioPacketCount % 1000 === 0) {\n this.rtspDebugLog(\n `Forwarded ${audioPacketCount} audio RTP packets to client ${clientId} via TCP interleaved`,\n );\n }\n });\n }\n }\n\n const resources = this.clientResources.get(clientId) as any;\n const rtspDebug = this.isRtspDebugEnabled();\n const rtspDebugLog = (message: string) => this.rtspDebugLog(message);\n\n const sendInterleaved = (channel: number, msg: Buffer): boolean => {\n if (!rtspSocket || rtspSocket.destroyed || !rtspSocket.writable)\n return false;\n try {\n return rtspSocket.write(frameRtpOverTcp(channel, msg));\n } catch (error) {\n if (\n error &&\n typeof error === \"object\" &&\n \"code\" in error &&\n (error as any).code === \"EPIPE\"\n )\n return false;\n }\n return false;\n };\n\n const getVideoChannel = (): number => resources?.track0RtpChannel ?? 0;\n const getAudioChannel = (): number => resources?.track1RtpChannel ?? 2;\n\n const buildRtpHeader = (\n payloadType: number,\n marker: boolean,\n seq: number,\n timestamp: number,\n ssrc: number,\n ): Buffer => {\n const h = Buffer.alloc(12);\n h[0] = 0x80; // V=2\n h[1] = (marker ? 0x80 : 0x00) | (payloadType & 0x7f);\n h.writeUInt16BE(seq & 0xffff, 2);\n h.writeUInt32BE(timestamp >>> 0, 4);\n h.writeUInt32BE(ssrc >>> 0, 8);\n return h;\n };\n\n const sendRtpPacket = (\n isAudio: boolean,\n payload: Buffer,\n marker: boolean,\n ) => {\n const pt = isAudio ? 97 : 96;\n if (!resources?.isPlaying) return;\n if (isAudio && !resources?.setupTrack1) return;\n if (!isAudio && !resources?.setupTrack0) return;\n\n if (!isAudio) {\n if (resources.rtpVideoSeq === undefined)\n resources.rtpVideoSeq = Math.floor(Math.random() * 0x10000);\n // Start at 0 to reduce A/V offset when RTCP SR is not present.\n if (resources.rtpVideoTimestamp === undefined)\n resources.rtpVideoTimestamp = 0;\n if (resources.rtpVideoSsrc === undefined)\n resources.rtpVideoSsrc = (Math.random() * 0xffffffff) >>> 0;\n const h = buildRtpHeader(\n pt,\n marker,\n resources.rtpVideoSeq,\n resources.rtpVideoTimestamp,\n resources.rtpVideoSsrc,\n );\n resources.rtpVideoSeq = (resources.rtpVideoSeq + 1) & 0xffff;\n sendInterleaved(getVideoChannel(), Buffer.concat([h, payload]));\n } else {\n if (resources.rtpAudioSeq === undefined)\n resources.rtpAudioSeq = Math.floor(Math.random() * 0x10000);\n // Start at 0 to reduce A/V offset when RTCP SR is not present.\n if (resources.rtpAudioTimestamp === undefined)\n resources.rtpAudioTimestamp = 0;\n if (resources.rtpAudioSsrc === undefined)\n resources.rtpAudioSsrc = (Math.random() * 0xffffffff) >>> 0;\n const h = buildRtpHeader(\n pt,\n marker,\n resources.rtpAudioSeq,\n resources.rtpAudioTimestamp,\n resources.rtpAudioSsrc,\n );\n resources.rtpAudioSeq = (resources.rtpAudioSeq + 1) & 0xffff;\n sendInterleaved(getAudioChannel(), Buffer.concat([h, payload]));\n }\n };\n\n const maxRtpPayload = 1200;\n\n const isH264IdrAccessUnit = (annexB: Buffer): boolean => {\n const nals = BaichuanRtspServer.splitAnnexBNals(annexB);\n for (const nal of nals) {\n if (nal.length < 1) continue;\n const t = (nal[0] ?? 0) & 0x1f;\n if (t === 5) return true; // IDR\n }\n return false;\n };\n\n const packetizeAndSendH264 = (nal: Buffer, markerOnLast: boolean) => {\n if (nal.length <= maxRtpPayload) {\n sendRtpPacket(false, nal, markerOnLast);\n return;\n }\n const nalHeader = nal[0]!;\n const nalType = nalHeader & 0x1f;\n // FU indicator: keep forbidden_zero_bit + NRI, set type to 28 (FU-A)\n const fuIndicator = (nalHeader & 0xe0) | 28;\n const data = nal.subarray(1);\n let offset = 0;\n while (offset < data.length) {\n const remaining = data.length - offset;\n const chunkLen = Math.min(remaining, maxRtpPayload - 2);\n const start = offset === 0;\n const end = offset + chunkLen >= data.length;\n const fuHeader = (start ? 0x80 : 0x00) | (end ? 0x40 : 0x00) | nalType;\n const chunk = data.subarray(offset, offset + chunkLen);\n const payload = Buffer.concat([\n Buffer.from([fuIndicator, fuHeader]),\n chunk,\n ]);\n sendRtpPacket(false, payload, markerOnLast && end);\n offset += chunkLen;\n }\n };\n\n const packetizeAndSendH265 = (nal: Buffer, markerOnLast: boolean) => {\n if (nal.length <= maxRtpPayload) {\n sendRtpPacket(false, nal, markerOnLast);\n return;\n }\n if (nal.length < 3) return;\n const nalHeader0 = nal[0]!;\n const nalHeader1 = nal[1]!;\n const nalType = (nalHeader0 >> 1) & 0x3f;\n const fuIndicator0 = (nalHeader0 & 0x81) | (49 << 1);\n const fuIndicator1 = nalHeader1;\n const data = nal.subarray(2);\n let offset = 0;\n while (offset < data.length) {\n const remaining = data.length - offset;\n const chunkLen = Math.min(remaining, maxRtpPayload - 3);\n const start = offset === 0;\n const end = offset + chunkLen >= data.length;\n const fuHeader =\n (start ? 0x80 : 0x00) | (end ? 0x40 : 0x00) | (nalType & 0x3f);\n const chunk = data.subarray(offset, offset + chunkLen);\n const payload = Buffer.concat([\n Buffer.from([fuIndicator0, fuIndicator1, fuHeader]),\n chunk,\n ]);\n sendRtpPacket(false, payload, markerOnLast && end);\n offset += chunkLen;\n }\n };\n\n const videoClockRate = 90000;\n const videoFps =\n streamMetadata?.frameRate && streamMetadata.frameRate > 0\n ? streamMetadata.frameRate\n : 25;\n const videoTimestampIncrement = Math.max(\n 1,\n Math.round(videoClockRate / videoFps),\n );\n\n const setVideoTimestampFromMicroseconds = (\n frameMicroseconds: number | null | undefined,\n ) => {\n if (!resources) return;\n if (frameMicroseconds === null || frameMicroseconds === undefined) return;\n if (!Number.isFinite(frameMicroseconds)) return;\n\n if (resources.rtpVideoTimestamp === undefined)\n resources.rtpVideoTimestamp = 0;\n if (resources.rtpVideoBaseTimestamp === undefined)\n resources.rtpVideoBaseTimestamp = resources.rtpVideoTimestamp;\n\n if (resources.rtpVideoBaseMicroseconds === undefined) {\n resources.rtpVideoBaseMicroseconds = frameMicroseconds >>> 0;\n resources.rtpVideoLastTimestamp = resources.rtpVideoTimestamp;\n return;\n }\n\n const baseUs = resources.rtpVideoBaseMicroseconds >>> 0;\n const curUs = frameMicroseconds >>> 0;\n const deltaUs = (curUs - baseUs) >>> 0;\n const baseTs = (resources.rtpVideoBaseTimestamp ?? 0) >>> 0;\n let ts =\n (baseTs + Math.round((deltaUs * videoClockRate) / 1_000_000)) >>> 0;\n\n const last = resources.rtpVideoLastTimestamp;\n if (last !== undefined && ts <= last >>> 0) {\n ts = ((last >>> 0) + 1) >>> 0;\n }\n\n resources.rtpVideoTimestamp = ts;\n resources.rtpVideoLastTimestamp = ts;\n };\n\n const sendVideoAccessUnit = (\n videoType: \"H264\" | \"H265\",\n accessUnitAnnexB: Buffer,\n advanceTimestamp = true,\n ) => {\n const nals = BaichuanRtspServer.splitAnnexBNals(accessUnitAnnexB);\n if (nals.length === 0) return;\n for (let idx = 0; idx < nals.length; idx++) {\n const nal = nals[idx]!;\n const isLastNal = idx === nals.length - 1;\n if (videoType === \"H265\") packetizeAndSendH265(nal, isLastNal);\n else packetizeAndSendH264(nal, isLastNal);\n }\n\n // If we don't have bcmedia microseconds available, fall back to fixed FPS increment.\n if (\n advanceTimestamp &&\n resources?.rtpVideoTimestamp !== undefined &&\n resources?.rtpVideoBaseMicroseconds === undefined\n ) {\n resources.rtpVideoTimestamp =\n (resources.rtpVideoTimestamp + videoTimestampIncrement) >>> 0;\n }\n };\n\n const sendAudioAdtsFrame = (adts: Buffer) => {\n const raw = BaichuanRtspServer.stripAdtsHeader(adts);\n if (!raw) return;\n // RFC 3640: AU-headers-length (16 bits) + AU-header (16 bits)\n const auHeadersLength = Buffer.from([0x00, 0x10]);\n const auSize = raw.length & 0x1fff;\n const auHeader = Buffer.alloc(2);\n // AU-size (13 bits) + AU-Index (3 bits, 0)\n auHeader[0] = (auSize >> 5) & 0xff;\n auHeader[1] = (auSize & 0x1f) << 3;\n const payload = Buffer.concat([auHeadersLength, auHeader, raw]);\n sendRtpPacket(true, payload, true);\n\n // advance audio timestamp by 1024 samples per AAC-LC frame\n if (resources?.rtpAudioTimestamp !== undefined) {\n resources.rtpAudioTimestamp =\n (resources.rtpAudioTimestamp + 1024) >>> 0;\n }\n };\n\n const isH265IrapAccessUnit = (annexB: Buffer): boolean => {\n const nals = splitAnnexBToNalPayloads(annexB);\n for (const nal of nals) {\n if (nal.length < 2) continue;\n const b0 = nal[0];\n if (b0 === undefined) continue;\n if ((b0 & 0x80) !== 0) continue;\n const nalType = (b0 >> 1) & 0x3f;\n if (isH265Irap(nalType)) return true;\n }\n return false;\n };\n\n if (useDirectRtp) {\n onProcess(undefined, null, null);\n }\n\n let ffmpeg: ReturnType<typeof spawn> | undefined;\n let audioPipe: NodeJS.WritableStream | undefined;\n if (!useDirectRtp) {\n const ffmpegArgs = [\n \"-hide_banner\",\n \"-loglevel\",\n \"error\",\n \"-fflags\",\n \"+genpts+igndts\", // Generate PTS, ignore DTS from input\n ];\n\n // Set input frame rate if available (before -f and -i)\n // This tells ffmpeg how to interpret the timing of frames from stdin\n const frameRate = streamMetadata?.frameRate || 25;\n if (frameRate > 0) {\n ffmpegArgs.push(\"-r\", frameRate.toString());\n this.rtspDebugLog(\n `Using frame rate ${frameRate} fps for client ${clientId}`,\n );\n }\n\n ffmpegArgs.push(\"-f\", ffmpegFormat, \"-i\", \"pipe:0\");\n\n // Optional audio input (AAC ADTS -> RTP L16).\n // We keep it TCP-only and conservative: if audio isn't detected/advertised, skip entirely.\n if (this.hasAudio) {\n ffmpegArgs.push(\"-f\", \"aac\", \"-i\", \"pipe:3\");\n }\n\n // Note: For RTP output, we don't need to specify output frame rate\n // The timestamps will be generated based on the input frame rate and -vsync cfr\n\n // Note: Frames from BaichuanVideoStream are already in Annex-B format with SPS/PPS prepended\n // So we don't need h264_mp4toannexb or hevc_mp4toannexb bitstream filters\n if (useTcpInterleaved) {\n // Video output\n ffmpegArgs.push(\n \"-map\",\n \"0:v:0\",\n \"-c:v\",\n \"copy\",\n \"-vsync\",\n \"cfr\", // Constant frame rate - generate timestamps based on input frame rate\n \"-avoid_negative_ts\",\n \"make_zero\", // Ensure timestamps are non-negative\n \"-f\",\n \"rtp\",\n \"-payload_type\",\n \"96\",\n `rtp://127.0.0.1:${localUdpPort}?pkt_size=1300`,\n );\n // Audio output\n if (this.hasAudio && udpSocketAudio && localUdpPortAudio) {\n const a = this.audioInfo;\n const rate = a?.sampleRate ?? 8000;\n const ch = a?.channels ?? 1;\n ffmpegArgs.push(\n \"-map\",\n \"1:a:0\",\n // ffmpeg's RTP muxer requires AAC extradata (global headers); encoding ensures it's present.\n \"-c:a\",\n \"aac\",\n \"-profile:a\",\n \"aac_low\",\n \"-ar\",\n String(rate),\n \"-ac\",\n String(ch),\n \"-flags:a\",\n \"+global_header\",\n \"-f\",\n \"rtp\",\n \"-payload_type\",\n \"97\",\n `rtp://127.0.0.1:${localUdpPortAudio}?pkt_size=1300`,\n );\n }\n } else {\n ffmpegArgs.push(\n \"-map\",\n \"0:v:0\",\n \"-c:v\",\n \"copy\",\n \"-vsync\",\n \"cfr\",\n \"-avoid_negative_ts\",\n \"make_zero\",\n \"-f\",\n \"rtp\",\n \"-payload_type\",\n \"96\",\n `rtp://127.0.0.1:5004?pkt_size=1300`,\n );\n }\n\n const stdio: any[] = [\"pipe\", \"ignore\", \"pipe\"];\n if (this.hasAudio) stdio.push(\"pipe\");\n this.rtspDebugLog(\n `Spawning ffmpeg for client ${clientId}: ffmpeg ${ffmpegArgs.join(\" \")}`,\n );\n ffmpeg = spawn(\"ffmpeg\", ffmpegArgs, {\n stdio,\n });\n\n // Seed ffmpeg with video parameter sets so it can start parsing/packetizing immediately.\n // This helps when live access units don't carry SPS/PPS early enough.\n try {\n const paramSets = this.flow.getParameterSetsAnnexB();\n if (paramSets && paramSets.length > 0 && ffmpeg?.stdin) {\n ffmpeg.stdin.write(paramSets);\n this.rtspDebugLog(\n `Wrote video parameter sets to ffmpeg stdin for client ${clientId} (len=${paramSets.length})`,\n );\n }\n } catch (e) {\n this.logger.warn(\n `[BaichuanRtspServer] Failed to write video parameter sets to ffmpeg for client ${clientId}: ${e}`,\n );\n }\n ffmpeg.on(\"error\", (error) => {\n this.logger.error(\n `[BaichuanRtspServer] Failed to spawn ffmpeg for client ${clientId}:`,\n error,\n );\n });\n\n ffmpeg.on(\"close\", (code, signal) => {\n this.rtspDebugLog(\n `ffmpeg exited for client ${clientId} (code=${code}, signal=${signal})`,\n );\n });\n\n onProcess(ffmpeg, udpSocket, udpSocketAudio);\n\n // Prevent unhandled errors on the writable side when the client disconnects or the server stops.\n // We treat these as normal shutdown signals.\n ffmpeg.stdin?.on(\"error\", (error: NodeJS.ErrnoException) => {\n const code = (error as any)?.code;\n if (code === \"EPIPE\" || code === \"ERR_STREAM_WRITE_AFTER_END\") {\n this.rtspDebugLog(\n `FFmpeg stdin error (${code}) for client ${clientId}`,\n );\n return;\n }\n this.logger.error(\n `[BaichuanRtspServer] FFmpeg stdin error for client ${clientId}:`,\n error,\n );\n });\n\n audioPipe = this.hasAudio\n ? (ffmpeg.stdio?.[3] as NodeJS.WritableStream | undefined)\n : undefined;\n (audioPipe as any)?.on?.(\"error\", (error: NodeJS.ErrnoException) => {\n const code = (error as any)?.code;\n if (code === \"EPIPE\" || code === \"ERR_STREAM_WRITE_AFTER_END\") {\n this.rtspDebugLog(\n `FFmpeg audio pipe error (${code}) for client ${clientId}`,\n );\n return;\n }\n this.logger.error(\n `[BaichuanRtspServer] FFmpeg audio pipe error for client ${clientId}:`,\n error,\n );\n });\n\n // If we already observed an ADTS frame during SDP priming, push one immediately.\n // This prevents ffmpeg from blocking on probing `pipe:3` before it can start producing RTP.\n if (audioPipe && this.audioPrimingFrame) {\n try {\n audioPipe.write(this.audioPrimingFrame);\n } catch {}\n }\n }\n\n // Each RTSP client gets its own iterator, but they all share the same underlying\n // camera-native stream (critical for BCUDP/battery reliability).\n this.rtspDebugLog(`Creating native stream iterator for client ${clientId}`);\n const clientGenerator = this.nativeFanout\n ? this.nativeFanout.subscribe(clientId)\n : createNativeStream(this.api, this.channel, this.profile, {\n variant: this.variant,\n });\n // Legacy: disable old priming generator reuse path.\n this.tempStreamGenerator = null;\n\n // Feed frames to ffmpeg from native stream with proper timing\n let frameCount = 0;\n let lastFrameTime = Date.now();\n const targetFrameInterval =\n streamMetadata && streamMetadata.frameRate > 0\n ? 1000 / streamMetadata.frameRate\n : 40; // Default to 25fps if not available\n\n const feedFrames = async () => {\n try {\n this.rtspDebugLog(\n `Starting to feed frames to client ${clientId} (target FPS: ${streamMetadata?.frameRate || 25}, interval: ${targetFrameInterval}ms)`,\n );\n let audioFrameCount = 0;\n let firstVideoWriteLogged = false;\n let firstAudioWriteLogged = false;\n let firstVideoFrameSeenLogged = false;\n let h265WaitParamSetsLogged = false;\n let h265WaitIrapLogged = false;\n for await (const frame of clientGenerator) {\n // Check if client is still connected before processing frame\n if (!this.connectedClients.has(clientId)) {\n this.rtspDebugLog(\n `Client ${clientId} disconnected, stopping frame feed`,\n );\n break;\n }\n\n const stdin = ffmpeg?.stdin;\n if (!useDirectRtp) {\n if (\n !stdin ||\n stdin.destroyed ||\n stdin.writableEnded ||\n stdin.writableFinished\n ) {\n this.rtspDebugLog(`FFmpeg stdin closed for client ${clientId}`);\n break;\n }\n }\n\n if (frame.data.length === 0) continue;\n\n if (!frame.audio && !firstVideoFrameSeenLogged) {\n firstVideoFrameSeenLogged = true;\n if (rtspDebug) {\n const headHex = frame.data.subarray(0, 16).toString(\"hex\");\n rtspDebugLog(\n `First video frame received from generator for client ${clientId} (len=${frame.data.length}, videoType=${String(\n (frame as any).videoType ?? this.flow.videoType,\n )}, head=${headHex})`,\n );\n }\n }\n\n // Handle audio frames (TCP only): write ADTS AAC frames to ffmpeg audio pipe.\n if (frame.audio) {\n audioFrameCount++;\n if (audioFrameCount === 1) {\n this.rtspDebugLog(\n `Audio frames detected (codec: ${frame.codec || \"unknown\"}, sampleRate: ${frame.sampleRate || \"unknown\"})`,\n );\n }\n if (audioFrameCount % 100 === 0) {\n this.rtspDebugLog(\n `Received ${audioFrameCount} audio frames (not sent to RTSP yet)`,\n );\n }\n\n if (useDirectRtp) {\n // Avoid starting with audio-only while the decoder is still waiting for the first keyframe.\n if (!resources?.seenFirstVideoKeyframe) {\n continue;\n }\n if (\n this.hasAudio &&\n BaichuanRtspServer.isAdtsAacFrame(frame.data)\n ) {\n if (rtspDebug && !firstAudioWriteLogged) {\n firstAudioWriteLogged = true;\n const headHex = frame.data.subarray(0, 16).toString(\"hex\");\n rtspDebugLog(\n `First audio ADTS frame packetized to RTP for client ${clientId} (len=${frame.data.length}, head=${headHex})`,\n );\n }\n sendAudioAdtsFrame(frame.data);\n }\n continue;\n } else {\n const audioPipeOk =\n this.hasAudio &&\n audioPipe &&\n !(audioPipe as any).writableEnded &&\n !(audioPipe as any).writableFinished;\n if (audioPipeOk) {\n const ap = audioPipe;\n if (!ap) continue;\n // Only accept AAC ADTS frames for now.\n if (BaichuanRtspServer.isAdtsAacFrame(frame.data)) {\n try {\n if (!firstAudioWriteLogged) {\n firstAudioWriteLogged = true;\n const headHex = frame.data\n .subarray(0, 16)\n .toString(\"hex\");\n this.rtspDebugLog(\n `First audio frame written to ffmpeg pipe for client ${clientId} (len=${frame.data.length}, head=${headHex})`,\n );\n }\n const written = ap.write(frame.data);\n if (!written) {\n await new Promise<void>((resolve) =>\n ap.once(\"drain\", () => resolve()),\n );\n }\n } catch {}\n }\n }\n continue;\n }\n continue;\n }\n\n // Extract parameter sets until available.\n // Some cameras don't include VPS/SPS/PPS in the very first access unit.\n if (frame.videoType === \"H264\" || frame.videoType === \"H265\") {\n const normalizedVideoData =\n frame.videoType === \"H264\"\n ? convertH264ToAnnexB(frame.data)\n : convertH265ToAnnexB(frame.data);\n if (frameCount === 0) {\n this.setFlowVideoType(frame.videoType, \"first video frame\");\n }\n\n const before = this.flow.getFmtp();\n if (!before.hasParamSets) {\n this.flow.extractParameterSets(normalizedVideoData);\n const after = this.flow.getFmtp();\n if (after.hasParamSets) {\n this.markFirstFrameReceived();\n }\n } else if (!this.firstFrameReceived) {\n this.markFirstFrameReceived();\n }\n }\n\n frameCount++;\n if (frameCount % 100 === 0) {\n this.rtspDebugLog(\n `Sent ${frameCount} frames to client ${clientId} (frame size: ${frame.data.length} bytes)`,\n );\n }\n\n // Throttle frame sending to match frame rate\n // Use a more precise timing mechanism to ensure frames are sent at the correct rate\n const now = Date.now();\n const timeSinceLastFrame = now - lastFrameTime;\n const waitTime = targetFrameInterval - timeSinceLastFrame;\n if (waitTime > 0) {\n // Wait for the exact interval before sending the next frame\n await new Promise((resolve) =>\n setTimeout(resolve, Math.min(waitTime, targetFrameInterval * 2)),\n );\n }\n lastFrameTime = Date.now();\n\n if (useDirectRtp) {\n const videoType = (frame.videoType ?? this.flow.videoType) as\n | \"H264\"\n | \"H265\";\n const normalizedVideoData =\n videoType === \"H264\"\n ? convertH264ToAnnexB(frame.data)\n : convertH265ToAnnexB(frame.data);\n\n // Many cameras start streaming with P-frames; decoding stays black until the first IDR/IRAP.\n // For H.264 we gate strictly on IDR.\n // For H.265 we gate on: (1) having VPS/SPS/PPS extracted (so we can send config), then\n // (2) seeing an IRAP access unit (with a short timeout to avoid deadlocks).\n if (!resources?.seenFirstVideoKeyframe) {\n if (videoType === \"H265\") {\n const { hasParamSets } = this.flow.getFmtp();\n if (!hasParamSets) {\n if (rtspDebug && !h265WaitParamSetsLogged) {\n h265WaitParamSetsLogged = true;\n rtspDebugLog(\n `H265 gating: waiting for VPS/SPS/PPS before sending RTP to client ${clientId}`,\n );\n }\n continue;\n }\n\n // Send parameter sets as soon as we have them, even before the first IRAP.\n if (!resources?.rtpSentVideoConfig) {\n const paramSets = this.flow.getParameterSetsAnnexB();\n if (paramSets && paramSets.length > 0) {\n sendVideoAccessUnit(videoType, paramSets, false);\n resources.rtpSentVideoConfig = true;\n }\n }\n\n if (!resources.h265WaitStartMs)\n resources.h265WaitStartMs = Date.now();\n const isIrap = isH265IrapAccessUnit(normalizedVideoData);\n const waitedMs =\n Date.now() - (resources.h265WaitStartMs as number);\n if (!isIrap && waitedMs < 2000) {\n if (rtspDebug && !h265WaitIrapLogged) {\n h265WaitIrapLogged = true;\n rtspDebugLog(\n `H265 gating: waiting for IRAP (or timeout) for client ${clientId}`,\n );\n }\n continue;\n }\n\n resources.seenFirstVideoKeyframe = true;\n } else {\n // H.264 gating:\n // - wait until SPS/PPS are extracted (so we can provide config)\n // - then wait for an IDR (type 5)\n const { hasParamSets } = this.flow.getFmtp();\n if (!hasParamSets) {\n if (rtspDebug && !h265WaitParamSetsLogged) {\n // reuse the flag name to avoid adding more state; message makes it clear.\n h265WaitParamSetsLogged = true;\n rtspDebugLog(\n `H264 gating: waiting for SPS/PPS before sending RTP to client ${clientId}`,\n );\n }\n continue;\n }\n\n // Send parameter sets as soon as we have them, even before the first IDR.\n if (!resources?.rtpSentVideoConfig) {\n const paramSets = this.flow.getParameterSetsAnnexB();\n if (paramSets && paramSets.length > 0) {\n sendVideoAccessUnit(videoType, paramSets, false);\n resources.rtpSentVideoConfig = true;\n }\n }\n\n const isIdr = isH264IdrAccessUnit(normalizedVideoData);\n if (!isIdr) {\n continue;\n }\n\n resources.seenFirstVideoKeyframe = true;\n }\n }\n\n // Derive RTP timestamps from the bcmedia microseconds clock (when available).\n // This makes frame pacing/timing match the camera source more closely than using a fixed FPS increment.\n const frameMicroseconds = (frame as any).microseconds as\n | number\n | null\n | undefined;\n setVideoTimestampFromMicroseconds(frameMicroseconds);\n\n if (!resources?.rtpSentVideoConfig) {\n const paramSets = this.flow.getParameterSetsAnnexB();\n if (paramSets && paramSets.length > 0) {\n // Parameter sets are not a video frame; keep the same RTP timestamp.\n sendVideoAccessUnit(videoType, paramSets, false);\n resources.rtpSentVideoConfig = true;\n }\n }\n if (!firstVideoWriteLogged) {\n firstVideoWriteLogged = true;\n if (rtspDebug) {\n const headHex = frame.data.subarray(0, 16).toString(\"hex\");\n rtspDebugLog(\n `First video access unit packetized to RTP for client ${clientId} (len=${frame.data.length}, head=${headHex})`,\n );\n }\n }\n\n sendVideoAccessUnit(videoType, normalizedVideoData, true);\n } else {\n try {\n if (\n stdin &&\n !stdin.destroyed &&\n !stdin.writableEnded &&\n !stdin.writableFinished\n ) {\n if (!firstVideoWriteLogged) {\n firstVideoWriteLogged = true;\n const headHex = frame.data.subarray(0, 16).toString(\"hex\");\n this.rtspDebugLog(\n `First video frame written to ffmpeg stdin for client ${clientId} (len=${frame.data.length}, head=${headHex})`,\n );\n }\n const written = stdin.write(\n frame.videoType === \"H264\"\n ? convertH264ToAnnexB(frame.data)\n : frame.data,\n );\n if (!written) {\n await new Promise<void>((resolve) => {\n if (stdin) {\n stdin.once(\"drain\", () => resolve());\n } else {\n resolve();\n }\n });\n }\n }\n } catch (error) {\n const code = (error as any)?.code;\n if (code === \"EPIPE\" || code === \"ERR_STREAM_WRITE_AFTER_END\") {\n this.rtspDebugLog(\n `EPIPE writing to ffmpeg for client ${clientId}`,\n );\n break;\n }\n this.logger.error(\n `[BaichuanRtspServer] Error writing frame to ffmpeg for client ${clientId}:`,\n error,\n );\n }\n }\n }\n this.rtspDebugLog(\n `Finished feeding frames to client ${clientId} (total: ${frameCount} frames)`,\n );\n } catch (error) {\n this.logger.error(\n `[BaichuanRtspServer] Error in feedFrames for client ${clientId}:`,\n error,\n );\n }\n };\n\n feedFrames().catch((error) => {\n this.logger.error(\n `[BaichuanRtspServer] Error feeding frames to client ${clientId}:`,\n error,\n );\n });\n\n // Log ffmpeg errors (ffmpeg path only)\n ffmpeg?.stderr?.on(\"data\", (data: Buffer) => {\n const output = data.toString();\n if (output.includes(\"error\") || output.includes(\"Error\")) {\n this.logger.error(\n `[BaichuanRtspServer] FFmpeg error for client ${clientId}: ${output}`,\n );\n }\n });\n }\n\n /**\n * Start native stream (mark as active).\n * Each client will create its own generator, so we just track that the stream is active.\n */\n private async startNativeStream(): Promise<void> {\n if (this.nativeStreamActive) {\n return;\n }\n\n this.nativeStreamActive = true;\n this.firstFrameReceived = false;\n this.firstAudioDetected = false;\n this.hasAudio = false;\n this.audioInfo = null;\n this.audioPrimingFrame = null;\n\n // Create promise that resolves when first frame arrives\n this.firstFramePromise = new Promise<void>((resolve) => {\n this.firstFrameResolve = resolve;\n });\n\n this.firstAudioPromise = new Promise<void>((resolve) => {\n this.firstAudioResolve = resolve;\n });\n\n this.rtspDebugLog(\n `Starting native stream for profile ${this.profile} (waiting for camera to start transmitting...)`,\n );\n\n // Keep-alive behavior is part of the selected protocol flow.\n await this.flow.startKeepAlive(this.api);\n\n // Use a single shared native stream and fan out frames to clients.\n // This avoids starting/stopping multiple camera streams (especially fragile on BCUDP/battery).\n this.nativeFanout = new NativeStreamFanout({\n maxQueueItems: 200,\n createSource: () =>\n createNativeStream(this.api, this.channel, this.profile, {\n variant: this.variant,\n }),\n onFrame: (frame) => {\n if (frame.audio) {\n // TCP-only audio detection: only advertise audio if we see ADTS AAC.\n if (\n !this.hasAudio &&\n this.api.client.getTransport() === \"tcp\" &&\n BaichuanRtspServer.isAdtsAacFrame(frame.data)\n ) {\n const info = BaichuanRtspServer.parseAdtsSamplingInfo(frame.data);\n if (info) {\n this.hasAudio = true;\n this.audioInfo = {\n codec: \"aac-adts\",\n sampleRate: info.sampleRate,\n channels: info.channels,\n configHex: info.configHex,\n };\n this.audioPrimingFrame = Buffer.from(frame.data);\n this.markFirstAudioDetected();\n this.rtspDebugLog(\n `Audio detected (AAC/ADTS ${info.sampleRate}Hz ch=${info.channels}); advertising RTSP track1 as mpeg4-generic`,\n );\n }\n }\n return;\n }\n\n if (frame.data.length === 0) return;\n if (frame.videoType === \"H264\" || frame.videoType === \"H265\") {\n this.setFlowVideoType(frame.videoType, \"native stream\");\n }\n\n // Extract parameter sets for SDP.\n this.flow.extractParameterSets(frame.data);\n const { hasParamSets } = this.flow.getFmtp();\n if (hasParamSets) {\n this.markFirstFrameReceived();\n }\n },\n onError: (error) => {\n this.logger.warn(\n `[BaichuanRtspServer] Shared native stream error: ${error}`,\n );\n },\n });\n this.nativeFanout.start();\n\n // If DESCRIBE primes the stream but no RTSP client actually SETUP/PLAYs,\n // auto-stop after a short window so battery cams can go back to sleep.\n this.clearNoClientAutoStopTimer();\n this.noClientAutoStopTimer = setTimeout(() => {\n if (this.connectedClients.size === 0) {\n this.rtspDebugLog(\n `Auto-stopping primed native stream (no clients connected)`,\n );\n void this.stopNativeStream();\n }\n }, 15_000);\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n (this.noClientAutoStopTimer as any)?.unref?.();\n }\n\n private markFirstFrameReceived(): void {\n if (!this.firstFrameReceived && this.firstFrameResolve) {\n this.firstFrameReceived = true;\n this.rtspDebugLog(\n `First frame received from camera for profile ${this.profile}`,\n );\n this.firstFrameResolve();\n this.firstFrameResolve = null;\n }\n }\n\n private markFirstAudioDetected(): void {\n if (!this.firstAudioDetected && this.firstAudioResolve) {\n this.firstAudioDetected = true;\n this.firstAudioResolve();\n this.firstAudioResolve = null;\n }\n }\n\n /**\n * Stop native stream (mark as inactive).\n */\n private async stopNativeStream(): Promise<void> {\n if (!this.nativeStreamActive) {\n return;\n }\n\n this.rtspDebugLog(`Stopping native stream`);\n\n this.flow.stopKeepAlive();\n\n this.clearNoClientAutoStopTimer();\n\n this.nativeStreamActive = false;\n this.firstFrameReceived = false;\n this.firstFramePromise = null;\n if (this.firstFrameResolve) {\n // Reject the promise if it's still pending\n this.firstFrameResolve = null;\n }\n this.firstAudioDetected = false;\n this.firstAudioPromise = null;\n if (this.firstAudioResolve) {\n this.firstAudioResolve = null;\n }\n // Stop shared native stream fan-out.\n if (this.nativeFanout) {\n const fanout = this.nativeFanout;\n this.nativeFanout = null;\n await fanout.stop();\n }\n\n // Legacy: ensure no priming generator remains open.\n if (this.tempStreamGenerator) {\n try {\n await this.tempStreamGenerator.return(undefined as any);\n } catch {}\n this.tempStreamGenerator = null;\n }\n\n // Note: Individual client generators are cleaned up when clients disconnect\n }\n\n /**\n * Remove a client and stop native stream if no clients remain.\n */\n private removeClient(clientId: string): void {\n if (this.connectedClients.has(clientId)) {\n this.connectedClients.delete(clientId);\n this.emit(\"clientDisconnected\", clientId);\n this.logger.info(\n `[BaichuanRtspServer] RTSP client disconnected: ${clientId}`,\n );\n\n // Stop native stream if no clients remain\n if (this.connectedClients.size === 0) {\n void this.stopNativeStream();\n }\n }\n }\n\n /**\n * Wait until RTSP server is ready to accept connections AND camera starts transmitting frames.\n * This ensures that the server is fully ready, including waiting for the camera to wake up\n * and start sending video frames (important for battery-powered cameras).\n */\n async waitUntilReady(timeoutMs: number = 30000): Promise<void> {\n return new Promise((resolve, reject) => {\n if (!this.clientConnectionServer) {\n reject(new Error(\"RTSP server not started\"));\n return;\n }\n\n const timeout = setTimeout(() => {\n reject(\n new Error(\n `Timeout waiting for RTSP server to be ready (${timeoutMs}ms). Camera may be sleeping or not responding.`,\n ),\n );\n }, timeoutMs);\n\n // First, check if port is listening\n const checkPort = () => {\n const socket = new net.Socket();\n socket.setTimeout(1000);\n socket.on(\"connect\", () => {\n socket.destroy();\n // Port is listening, now wait for first frame if native stream is active\n if (this.nativeStreamActive && this.firstFramePromise) {\n this.rtspDebugLog(\n `Port is listening, waiting for camera to start transmitting frames...`,\n );\n this.firstFramePromise\n .then(() => {\n clearTimeout(timeout);\n resolve();\n })\n .catch((error) => {\n clearTimeout(timeout);\n reject(error);\n });\n } else {\n // No native stream active yet, just wait for port\n clearTimeout(timeout);\n resolve();\n }\n });\n socket.on(\"timeout\", () => {\n socket.destroy();\n setTimeout(checkPort, 500);\n });\n socket.on(\"error\", () => {\n socket.destroy();\n setTimeout(checkPort, 500);\n });\n socket.connect(this.listenPort, this.listenHost);\n };\n\n // Start checking immediately\n checkPort();\n });\n }\n\n /**\n * Stop the RTSP server.\n * This will close all client connections and release all resources.\n */\n async stop(): Promise<void> {\n if (!this.active) {\n return;\n }\n\n this.logger.info(\n `[BaichuanRtspServer] Stopping RTSP server on ${this.listenHost}:${this.listenPort}...`,\n );\n\n // Stop native stream\n await this.stopNativeStream();\n\n // Close all client connections and cleanup resources\n const clientIds = Array.from(this.connectedClients);\n for (const clientId of clientIds) {\n const resources = this.clientResources.get(clientId);\n if (resources) {\n // Kill ffmpeg process\n if (resources.ffmpeg) {\n try {\n resources.ffmpeg.stdin?.end();\n resources.ffmpeg.kill(\"SIGTERM\");\n setTimeout(() => {\n try {\n resources.ffmpeg?.kill(\"SIGKILL\");\n } catch {}\n }, 1000);\n } catch {}\n }\n\n // Close UDP socket\n if (resources.udpSocket) {\n try {\n resources.udpSocket.close();\n } catch {}\n }\n\n // Close RTSP socket\n if (resources.rtspSocket && !resources.rtspSocket.destroyed) {\n try {\n resources.rtspSocket.destroy();\n } catch {}\n }\n }\n }\n this.clientResources.clear();\n\n // Close client connection server\n if (this.clientConnectionServer) {\n await new Promise<void>((resolve) => {\n this.clientConnectionServer?.close(() => {\n resolve();\n });\n });\n this.clientConnectionServer = undefined;\n }\n\n this.active = false;\n this.connectedClients.clear();\n this.emit(\"close\");\n this.logger.info(`[BaichuanRtspServer] RTSP server stopped`);\n }\n\n /**\n * Get RTSP URL for this server.\n */\n getRtspUrl(): string {\n return `rtsp://${this.listenHost}:${this.listenPort}${this.path}`;\n }\n\n /**\n * Check if server is active.\n */\n isActive(): boolean {\n return this.active;\n }\n\n /**\n * Get number of connected clients.\n */\n getClientCount(): number {\n return this.connectedClients.size;\n }\n}\n","import type { ReolinkBaichuanApi } from \"../../reolink/baichuan/ReolinkBaichuanApi\";\nimport { splitAnnexBToNalPayloads } from \"./H264Converter\";\nimport { extractPpsFromAnnexB, extractSpsFromAnnexB, extractVpsFromAnnexB } from \"./H265Converter\";\n\nexport type RtspTransport = \"tcp\" | \"udp\";\nexport type RtspVideoType = \"H264\" | \"H265\";\n\nexport type RtspFlowKey = `${RtspTransport}-${RtspVideoType}`;\n\nexport interface RtspFlow {\n readonly key: RtspFlowKey;\n readonly transport: RtspTransport;\n readonly videoType: RtspVideoType;\n\n /** ffmpeg demuxer for stdin. */\n readonly ffmpegFormat: \"h264\" | \"hevc\";\n\n /** SDP rtpmap codec token. */\n readonly sdpCodec: \"H264\" | \"H265\";\n\n /** Called on first frames to extract parameter sets used in SDP. */\n extractParameterSets(accessUnitAnnexB: Buffer): void;\n\n /** Returns fmtp value (without the leading \"a=fmtp:96 \"). */\n getFmtp(): { fmtp: string; hasParamSets: boolean };\n getParameterSetsAnnexB(): Buffer | null;\n\n /** Called while native stream is active; may keep device awake (UDP/battery). */\n startKeepAlive(api: ReolinkBaichuanApi): Promise<void>;\n stopKeepAlive(): void;\n}\n\nfunction toKey(transport: RtspTransport, videoType: RtspVideoType): RtspFlowKey {\n return `${transport}-${videoType === \"H264\" ? \"h264\" : \"h265\"}` as RtspFlowKey;\n}\n\nabstract class BaseFlow implements RtspFlow {\n public readonly key: RtspFlowKey;\n public readonly transport: RtspTransport;\n public readonly videoType: RtspVideoType;\n public abstract readonly ffmpegFormat: \"h264\" | \"hevc\";\n public abstract readonly sdpCodec: \"H264\" | \"H265\";\n\n private keepAliveTimer: NodeJS.Timeout | undefined;\n\n protected constructor(transport: RtspTransport, videoType: RtspVideoType) {\n this.transport = transport;\n this.videoType = videoType;\n this.key = toKey(transport, videoType);\n }\n\n async startKeepAlive(api: ReolinkBaichuanApi): Promise<void> {\n // Keepalive for battery/UDP cameras:\n // - BCUDP ACK + resend\n // - BCUDP heartbeat (C2D_HB)\n // - Respond to camera-initiated Baichuan UDP keepalive frames (cmd_id=234)\n // with response_code=200 (handled centrally in `BaichuanClient.handleFrame()`).\n //\n // Avoid sending extra Baichuan pings (cmd_id=93) from here: on some models\n // it appears to correlate with early stream termination.\n this.stopKeepAlive();\n\n if (api.client.getTransport() !== \"udp\") return;\n\n // Nothing else to do here for UDP.\n return;\n }\n\n stopKeepAlive(): void {\n if (this.keepAliveTimer) {\n clearInterval(this.keepAliveTimer);\n this.keepAliveTimer = undefined;\n }\n }\n\n abstract extractParameterSets(accessUnitAnnexB: Buffer): void;\n abstract getFmtp(): { fmtp: string; hasParamSets: boolean };\n abstract getParameterSetsAnnexB(): Buffer | null;\n}\n\nconst ANNEXB_START_CODE = Buffer.from([0x00, 0x00, 0x00, 0x01]);\n\nfunction joinAnnexBNals(...nals: Array<Buffer | null>): Buffer | null {\n const present = nals.filter((n): n is Buffer => !!n && n.length > 0);\n if (present.length === 0) return null;\n const parts: Buffer[] = [];\n for (const n of present) {\n parts.push(ANNEXB_START_CODE, n);\n }\n return Buffer.concat(parts);\n}\n\nclass H264Flow extends BaseFlow {\n public readonly ffmpegFormat = \"h264\" as const;\n public readonly sdpCodec = \"H264\" as const;\n\n private sps: Buffer | null = null;\n private pps: Buffer | null = null;\n\n public constructor(transport: RtspTransport) {\n super(transport, \"H264\");\n }\n\n extractParameterSets(accessUnitAnnexB: Buffer): void {\n const nals = splitAnnexBToNalPayloads(accessUnitAnnexB);\n for (const nal of nals) {\n if (nal.length < 1) continue;\n const nalType = (nal[0] ?? 0) & 0x1f;\n if (nalType === 7 && !this.sps) this.sps = nal;\n if (nalType === 8 && !this.pps) this.pps = nal;\n if (this.sps && this.pps) break;\n }\n }\n\n getFmtp(): { fmtp: string; hasParamSets: boolean } {\n let fmtp = \"packetization-mode=1\";\n\n // Add profile-level-id when we have an SPS.\n // SPS NAL payload format (without AnnexB start code):\n // - byte 0: nal header\n // - bytes 1..3: profile_idc, constraint_set flags, level_idc\n if (this.sps && this.sps.length >= 4) {\n const profileLevelId = Buffer.from([this.sps[1]!, this.sps[2]!, this.sps[3]!]).toString(\"hex\");\n fmtp += `;profile-level-id=${profileLevelId}`;\n }\n\n if (this.sps && this.pps) {\n fmtp += `;sprop-parameter-sets=${this.sps.toString(\"base64\")},${this.pps.toString(\"base64\")}`;\n return { fmtp, hasParamSets: true };\n }\n return { fmtp, hasParamSets: false };\n }\n\n getParameterSetsAnnexB(): Buffer | null {\n return joinAnnexBNals(this.sps, this.pps);\n }\n}\n\nclass H265Flow extends BaseFlow {\n public readonly ffmpegFormat = \"hevc\" as const;\n public readonly sdpCodec = \"H265\" as const;\n\n private vps: Buffer | null = null;\n private sps: Buffer | null = null;\n private pps: Buffer | null = null;\n\n public constructor(transport: RtspTransport) {\n super(transport, \"H265\");\n }\n\n extractParameterSets(accessUnitAnnexB: Buffer): void {\n const vps = extractVpsFromAnnexB(accessUnitAnnexB);\n const sps = extractSpsFromAnnexB(accessUnitAnnexB);\n const pps = extractPpsFromAnnexB(accessUnitAnnexB);\n if (vps && !this.vps) this.vps = vps;\n if (sps && !this.sps) this.sps = sps;\n if (pps && !this.pps) this.pps = pps;\n }\n\n getFmtp(): { fmtp: string; hasParamSets: boolean } {\n // RFC 7798 uses sprop-vps/sprop-sps/sprop-pps.\n let fmtp = \"\";\n if (this.vps && this.sps && this.pps) {\n fmtp = `sprop-vps=${this.vps.toString(\"base64\")};sprop-sps=${this.sps.toString(\"base64\")};sprop-pps=${this.pps.toString(\"base64\")}`;\n return { fmtp, hasParamSets: true };\n }\n return { fmtp, hasParamSets: false };\n }\n\n getParameterSetsAnnexB(): Buffer | null {\n return joinAnnexBNals(this.vps, this.sps, this.pps);\n }\n}\n\nexport function createRtspFlow(transport: RtspTransport, videoType: RtspVideoType): RtspFlow {\n if (videoType === \"H264\") return new H264Flow(transport);\n return new H265Flow(transport);\n}\n","import type {\n AbilityInfo,\n DeviceAbilities,\n DeviceCapabilities,\n SupportInfo,\n SupportItem,\n} from \"./types\";\n\nfunction toNumberOrUndefined(value: string | undefined): number | undefined {\n if (value == null) return undefined;\n const n = Number(value);\n return Number.isFinite(n) ? n : undefined;\n}\n\nfunction isTruthyNumberLike(value: unknown): boolean {\n if (typeof value === \"number\") return value > 0;\n if (typeof value === \"string\") {\n const n = Number(value);\n if (Number.isFinite(n)) return n > 0;\n return value.length > 0 && value !== \"0\";\n }\n return Boolean(value);\n}\n\nexport function flattenAbilitiesForChannel(\n abilities: DeviceAbilities | undefined,\n channel: number,\n): AbilityInfo | undefined {\n if (!abilities || typeof abilities !== \"object\") return undefined;\n\n // NOTE: callers always pass channel as 0-based.\n // Do not attempt 1-based fallbacks here: it is ambiguous and can mix in data from the wrong channel.\n const anyAbilities = abilities as any;\n const channelAbilities = anyAbilities[channel] as AbilityInfo | undefined;\n const host = anyAbilities.Host as AbilityInfo | undefined;\n\n const merged: AbilityInfo = {\n ...(host && typeof host === \"object\" ? host : {}),\n ...(channelAbilities && typeof channelAbilities === \"object\"\n ? channelAbilities\n : {}),\n };\n\n return Object.keys(merged).length ? merged : undefined;\n}\n\nexport function abilitiesHasAny(\n abilities: AbilityInfo | undefined,\n re: RegExp,\n): boolean {\n if (!abilities) return false;\n for (const [key, value] of Object.entries(abilities)) {\n if (!re.test(key)) continue;\n if (isTruthyNumberLike(value)) return true;\n }\n return false;\n}\n\nexport function parseSupportXml(xml: string): SupportInfo | undefined {\n if (!xml) return undefined;\n\n const match = xml.match(/<Support[^>]*>([\\s\\S]*?)<\\/Support>/i);\n if (!match) return undefined;\n const supportXml = match[1] ?? \"\";\n\n const ptzMode = supportXml.match(/<ptzMode>([^<]*)<\\/ptzMode>/i)?.[1];\n\n const topLevelInt = (tag: string): number | undefined => {\n const m = supportXml.match(new RegExp(`<${tag}>([^<]*)<\\\\/${tag}>`, \"i\"));\n return toNumberOrUndefined(m?.[1]);\n };\n\n const topLevelString = (tag: string): string | undefined => {\n const m = supportXml.match(new RegExp(`<${tag}>([^<]*)<\\\\/${tag}>`, \"i\"));\n return m?.[1];\n };\n\n const items: SupportItem[] = [];\n for (const itemMatch of supportXml.matchAll(\n /<item[^>]*>([\\s\\S]*?)<\\/item>/gi,\n )) {\n const itemXml = itemMatch[1] ?? \"\";\n const item: SupportItem = {\n chnID:\n toNumberOrUndefined(itemXml.match(/<chnID>([^<]*)<\\/chnID>/i)?.[1]) ??\n 0,\n };\n\n for (const tagMatch of itemXml.matchAll(\n /<([A-Za-z0-9_]+)>([^<]*)<\\/\\1>/g,\n )) {\n const tag = tagMatch[1];\n const value = tagMatch[2];\n if (!tag) continue;\n if (tag === \"chnID\") continue;\n const n = toNumberOrUndefined(value);\n (item as any)[tag] = n ?? value;\n }\n\n items.push(item);\n }\n\n const support: SupportInfo = { items };\n if (ptzMode !== undefined) support.ptzMode = ptzMode;\n\n const assignInt = (key: keyof SupportInfo, tag: string) => {\n const v = topLevelInt(tag);\n if (v !== undefined) (support as any)[key] = v;\n };\n assignInt(\"IOInputPortNum\", \"IOInputPortNum\");\n assignInt(\"IOOutputPortNum\", \"IOOutputPortNum\");\n assignInt(\"diskNum\", \"diskNum\");\n assignInt(\"channelNum\", \"channelNum\");\n assignInt(\"audioNum\", \"audioNum\");\n assignInt(\"ptzCfg\", \"ptzCfg\");\n assignInt(\"B485\", \"B485\");\n assignInt(\"autoUpdate\", \"autoUpdate\");\n assignInt(\"pushAlarm\", \"pushAlarm\");\n assignInt(\"ftp\", \"ftp\");\n assignInt(\"ftpTest\", \"ftpTest\");\n assignInt(\"email\", \"email\");\n assignInt(\"wifi\", \"wifi\");\n assignInt(\"record\", \"record\");\n assignInt(\"wifiTest\", \"wifiTest\");\n assignInt(\"rtsp\", \"rtsp\");\n assignInt(\"onvif\", \"onvif\");\n assignInt(\"audioTalk\", \"audioTalk\");\n\n const subVersion = topLevelString(\"subVersion\");\n if (subVersion !== undefined) (support as any).subVersion = subVersion;\n\n return support;\n}\n\nfunction getSupportItemForChannel(\n support: SupportInfo | undefined,\n channel: number,\n): SupportItem | undefined {\n if (!support?.items?.length) return undefined;\n\n // NOTE: callers always pass channel as 0-based and we only match that exact chnID.\n // Some firmwares return multiple <item> entries with the same chnID:\n // - smartHome items (googleHome/amazonAlexa) with only {name, ver}\n // - a \"real\" capability item with many numeric fields (ptzType, ledCtrl, lightType, etc.)\n // We must pick the best candidate, otherwise we miss lightType/ledCtrl and mis-detect capabilities.\n\n const scoreSupportItem = (item: SupportItem): number => {\n const anyItem = item as any;\n let score = 0;\n\n // Prefer items without a \"name\" field (often smartHome entries).\n if (anyItem.name == null) score += 2;\n\n // Prefer items with known capability-ish fields.\n const capabilityKeys = [\n \"ptzType\",\n \"ptzControl\",\n \"ptzPreset\",\n \"ledCtrl\",\n \"lightType\",\n \"battery\",\n \"audioVersion\",\n \"motion\",\n \"encCtrl\",\n \"newIspCfg\",\n \"remoteAbility\",\n ];\n for (const k of capabilityKeys) {\n if (anyItem[k] !== undefined) score += 3;\n }\n\n // Fallback: reward having many fields at all.\n score += Math.min(10, Math.max(0, Object.keys(anyItem).length - 1));\n return score;\n };\n\n const pickBest = (chnId: number): SupportItem | undefined => {\n const candidates = support.items.filter((i) => i.chnID === chnId);\n if (!candidates.length) return undefined;\n return candidates\n .slice()\n .sort((a, b) => scoreSupportItem(b) - scoreSupportItem(a))[0];\n };\n\n return pickBest(channel);\n}\n\nexport function computeDeviceCapabilities(params: {\n channel: number;\n /** Device model name/type (best-effort). Used for heuristic capability detection. */\n model?: string;\n abilities?: DeviceAbilities;\n support?: SupportInfo;\n}): DeviceCapabilities {\n const { channel } = params;\n const flat = flattenAbilitiesForChannel(params.abilities, channel);\n const supportItem = getSupportItemForChannel(params.support, channel);\n\n const ptzModeRaw = params.support?.ptzMode;\n const ptzMode =\n typeof ptzModeRaw === \"string\" ? ptzModeRaw.toLowerCase() : undefined;\n\n // Per-channel PTZ signals from SupportInfo item.\n // On some hub/NVR firmwares, top-level <ptzMode> reflects the host and may be \"none\"\n // even when specific channels are PTZ.\n const ptzTypeRaw = supportItem ? (supportItem as any).ptzType : undefined;\n const ptzControlRaw = supportItem\n ? (supportItem as any).ptzControl\n : undefined;\n const supportExplicitlyDefinesPtz =\n ptzTypeRaw !== undefined || ptzControlRaw !== undefined;\n const ptzExplicitlyDisabledBySupportItem =\n supportExplicitlyDefinesPtz &&\n !isTruthyNumberLike(ptzTypeRaw) &&\n !isTruthyNumberLike(ptzControlRaw);\n const hasPtzFromSupportItem =\n isTruthyNumberLike(ptzTypeRaw) ||\n isTruthyNumberLike(ptzControlRaw) ||\n isTruthyNumberLike((supportItem as any)?.ptzPreset);\n\n // Some battery cameras expose legacy/host PTZ abilities (e.g. preset_rw/ptzInfo_ro) even when\n // the actual channel PTZ is explicitly disabled. When support.ptzMode says \"none\", treat it\n // as authoritative ONLY if the channel support item does not indicate PTZ.\n const ptzDisabledBySupport =\n (ptzMode === \"none\" || ptzMode === \"0\") && !hasPtzFromSupportItem;\n\n const hasBatteryFromSupport = supportItem\n ? isTruthyNumberLike((supportItem as any).battery)\n : false;\n // NOTE: ledCtrl is typically the indicator/status LED control, NOT the floodlight.\n // Do not map it to floodlight capability.\n const ptzPresetRaw = supportItem ? (supportItem as any).ptzPreset : undefined;\n // If SupportInfo explicitly provides ptzPreset, treat it as authoritative.\n // This avoids false positives from AbilityInfo (some firmwares leak host/legacy preset keys).\n const supportExplicitlyDefinesPresets = ptzPresetRaw !== undefined;\n const hasPresetsFromSupport = supportExplicitlyDefinesPresets\n ? isTruthyNumberLike(ptzPresetRaw)\n : false;\n const presetsExplicitlyDisabledBySupport =\n supportExplicitlyDefinesPresets && !isTruthyNumberLike(ptzPresetRaw);\n const doorbellVersionRaw = supportItem\n ? (supportItem as any).doorbellVersion\n : undefined;\n const isDoorbellFromSupport = Number.isFinite(Number(doorbellVersionRaw))\n ? Number(doorbellVersionRaw) > 0\n : isTruthyNumberLike(doorbellVersionRaw);\n const isDoorbellFromModel =\n typeof params.model === \"string\" && /doorbell/i.test(params.model);\n\n // Some firmwares expose an explicit lightType in SupportInfo items.\n // Observed values:\n // - 0: no white LED / no floodlight\n // - 1: IR LED only (not controllable as floodlight)\n // - 2+: white LED / spotlight / floodlight (controllable)\n // Only treat lightType >= 2 as having a controllable floodlight.\n const lightTypeRaw = supportItem ? (supportItem as any).lightType : undefined;\n const lightType =\n typeof lightTypeRaw === \"number\"\n ? lightTypeRaw\n : typeof lightTypeRaw === \"string\"\n ? Number(lightTypeRaw)\n : undefined;\n\n const hasPtzFromSupport =\n hasPtzFromSupportItem ||\n (ptzMode ? ptzMode !== \"none\" && ptzMode !== \"0\" : false);\n const hasPanTiltFromSupport = ptzMode\n ? ptzMode.includes(\"pt\") || ptzMode === \"pt\" || ptzMode === \"ptz\"\n : false;\n const hasZoomFromSupport = ptzMode ? ptzMode.includes(\"z\") : false;\n\n const hasBatteryFromAbilities = abilitiesHasAny(flat, /battery/i);\n const hasFloodlightFromAbilities = abilitiesHasAny(\n flat,\n /white\\s*led|whiteLed|flood\\s*light|floodlight/i,\n );\n const hasSirenFromAbilities = abilitiesHasAny(\n flat,\n /audio\\s*alarm|audioAlarm|siren|pushAlarn|audioPlay/i,\n );\n\n // Two-way audio (intercom/talk) capability.\n // Observed signals:\n // - `support.audioTalk` (some firmwares)\n // - per-channel SupportInfo item `ipcAudioTalk` (common on NVR doorbells)\n const hasIntercomFromSupport =\n isTruthyNumberLike((params.support as any)?.audioTalk) ||\n (supportItem\n ? isTruthyNumberLike((supportItem as any).ipcAudioTalk)\n : false);\n\n // AbilityInfo can contain host/legacy PTZ keys even for non-PTZ channels.\n // If SupportInfo explicitly defines ptzType/ptzControl and both are falsey, treat that as authoritative.\n const hasPanTiltFromAbilities = ptzExplicitlyDisabledBySupportItem\n ? false\n : abilitiesHasAny(flat, /ptz/i);\n const hasZoomFromAbilities = ptzExplicitlyDisabledBySupportItem\n ? false\n : abilitiesHasAny(flat, /zoom|zoomFocus|StartZoomFocus/i);\n const hasPresetsFromAbilities = abilitiesHasAny(flat, /preset/i);\n // PIR detection is inconsistently advertised across firmwares.\n // Observed signals:\n // - explicit pir_* abilities\n // - rfAlarm_* host ability (common on battery cams; \"rf\" ~ PIR/radio sensor config)\n // - mdWithPir version flag (seen in other SDKs)\n const hasPirFromAbilities =\n abilitiesHasAny(flat, /(^|_)pir/i) ||\n abilitiesHasAny(flat, /rfAlarm/i) ||\n abilitiesHasAny(flat, /mdWithPir/i);\n\n // Some firmwares expose rfCfg in SupportInfo items; treat it as a hint for PIR support.\n // Observed variants:\n // - rfCfg (older)\n // - newRfCfg (common)\n // - rfVersion (present even when *Cfg is 0)\n // Additionally, many battery cams are PIR-based; treat battery>0 as a weak hint.\n const hasPirFromSupport = supportItem\n ? isTruthyNumberLike((supportItem as any).rfCfg) ||\n isTruthyNumberLike((supportItem as any).newRfCfg) ||\n isTruthyNumberLike((supportItem as any).rfVersion) ||\n isTruthyNumberLike((supportItem as any).battery)\n : false;\n\n // Autotracking (smartTrack) support.\n // Observed signals:\n // - autoPt in SupportInfo item (indicates the device supports auto pan-tilt tracking)\n // - smartAI in SupportInfo item (indicates AI-based tracking capability)\n // NOTE: The heuristic (ptzControl > 0 && aitype > 0) was too aggressive and caused\n // false positives on cameras that have PTZ and AI detection but NOT autotracking.\n // NOTE: aiCfg in abilities is NOT a reliable indicator of autotracking - many cameras\n // have AI configuration (for detection) without autotracking capability.\n // NOTE: The actual smartTrack enable state is in AiCfg (cmdId=299), but the capability\n // can be detected from the SupportInfo item fields.\n const hasAutotrackingFromSupport = supportItem\n ? isTruthyNumberLike((supportItem as any).autoPt) ||\n isTruthyNumberLike((supportItem as any).smartAI)\n : false;\n\n // Fallback: check abilities for explicit smartTrack only (NOT aiCfg - too broad)\n const hasAutotrackingFromAbilities = abilitiesHasAny(flat, /smartTrack/i);\n\n const hasPan = hasPanTiltFromSupport || hasPanTiltFromAbilities;\n const hasTilt = hasPanTiltFromSupport || hasPanTiltFromAbilities;\n const hasZoom = hasZoomFromSupport || hasZoomFromAbilities;\n const hasPresets = presetsExplicitlyDisabledBySupport\n ? false\n : hasPresetsFromSupport || hasPresetsFromAbilities;\n\n const finalHasPan = ptzDisabledBySupport ? false : hasPan;\n const finalHasTilt = ptzDisabledBySupport ? false : hasTilt;\n const finalHasZoom = ptzDisabledBySupport ? false : hasZoom;\n const finalHasPresets = ptzDisabledBySupport ? false : hasPresets;\n\n const result: DeviceCapabilities = {\n channel,\n hasPan: finalHasPan,\n hasTilt: finalHasTilt,\n hasZoom: finalHasZoom,\n hasPresets: finalHasPresets,\n hasPtz: ptzDisabledBySupport\n ? false\n : hasPtzFromSupport ||\n finalHasPan ||\n finalHasTilt ||\n finalHasZoom ||\n finalHasPresets,\n hasBattery: hasBatteryFromSupport || hasBatteryFromAbilities,\n hasIntercom: hasIntercomFromSupport,\n hasSiren: hasSirenFromAbilities,\n // lightType >= 2 indicates controllable white LED / floodlight (1 = IR only)\n hasFloodlight: Number.isFinite(lightType as number)\n ? (lightType as number) >= 2\n : hasFloodlightFromAbilities,\n hasPir: hasPirFromAbilities || hasPirFromSupport,\n isDoorbell: isDoorbellFromSupport || isDoorbellFromModel,\n hasAutotracking: hasAutotrackingFromSupport || hasAutotrackingFromAbilities,\n };\n\n if (ptzMode !== undefined) result.ptzMode = ptzMode;\n return result;\n}\n","import { spawn } from \"node:child_process\";\nimport { mkdir } from \"node:fs/promises\";\nimport { dirname } from \"node:path\";\nimport { PassThrough, type Readable } from \"node:stream\";\nimport {\n BaichuanRtspServer,\n type BaichuanRtspServerOptions,\n} from \"../../baichuan/stream/BaichuanRtspServer\";\nimport { BaichuanVideoStream } from \"../../baichuan/stream/BaichuanVideoStream\";\nimport {\n BcMediaAnnexBDecoder,\n type BcMediaAudioType,\n type BcMediaVideoType,\n} from \"../../baichuan/stream/BcMediaAnnexBDecoder\";\nimport { MpegTsMuxer } from \"../../baichuan/stream/MpegTsMuxer\";\nimport {\n BaichuanClient,\n type BaichuanClientOptions,\n} from \"../../client/BaichuanClient\";\nimport {\n eventTraceLog,\n normalizeDebugOptions,\n recordingsTraceLog,\n type Logger,\n} from \"../../debug/DebugConfig\";\nimport {\n collectNvrDiagnostics,\n runAllDiagnosticsConsecutively,\n RunAllDiagnosticsConsecutivelyParams,\n runMultifocalDiagnosticsConsecutively,\n RunMultifocalDiagnosticsConsecutivelyParams,\n} from \"../../debug/DiagnosticsTools\";\nimport { createDebugGateLogger } from \"../../logging/logger\";\nimport {\n BC_CLASS_FILE_DOWNLOAD,\n BC_CLASS_MODERN_24,\n BC_CMD_ID_ABILITY_INFO,\n BC_CMD_ID_AUDIO_ALARM_PLAY,\n BC_CMD_ID_CHANNEL_INFO_ALL,\n BC_CMD_ID_CMD_123,\n BC_CMD_ID_CMD_209,\n BC_CMD_ID_CMD_265,\n BC_CMD_ID_CMD_440,\n BC_CMD_ID_FILE_INFO_LIST_DL_VIDEO,\n BC_CMD_ID_FILE_INFO_LIST_DOWNLOAD,\n BC_CMD_ID_FILE_INFO_LIST_GET,\n BC_CMD_ID_FILE_INFO_LIST_OPEN,\n BC_CMD_ID_FILE_INFO_LIST_REPLAY,\n BC_CMD_ID_FILE_INFO_LIST_STOP,\n BC_CMD_ID_FLOODLIGHT_STATUS_LIST,\n BC_CMD_ID_GET_ABILITY_SUPPORT,\n BC_CMD_ID_GET_ACCESS_USER_LIST,\n BC_CMD_ID_GET_AI_ALARM,\n BC_CMD_ID_GET_AI_CFG,\n BC_CMD_ID_SET_AI_CFG,\n BC_CMD_ID_GET_AI_DENOISE,\n BC_CMD_ID_GET_AUDIO_ALARM,\n BC_CMD_ID_GET_AUDIO_CFG,\n BC_CMD_ID_GET_AUDIO_TASK,\n BC_CMD_ID_GET_BATTERY_INFO,\n BC_CMD_ID_GET_BATTERY_INFO_LIST,\n BC_CMD_ID_GET_DAY_NIGHT_THRESHOLD,\n BC_CMD_ID_GET_DAY_RECORDS,\n BC_CMD_ID_GET_EMAIL_TASK,\n BC_CMD_ID_GET_FTP_TASK,\n BC_CMD_ID_GET_HDD_INFO_LIST,\n BC_CMD_ID_GET_KIT_AP_CFG,\n BC_CMD_ID_GET_LED_STATE,\n BC_CMD_ID_GET_MOTION_ALARM,\n BC_CMD_ID_GET_ONLINE_USER_LIST,\n BC_CMD_ID_GET_OSD_DATETIME,\n BC_CMD_ID_GET_PIR_INFO,\n BC_CMD_ID_GET_PTZ_POSITION,\n BC_CMD_ID_GET_PTZ_PRESET,\n BC_CMD_ID_GET_REC_ENC_CFG,\n BC_CMD_ID_GET_RECORD,\n BC_CMD_ID_GET_RECORD_CFG,\n BC_CMD_ID_GET_SIREN_STATUS,\n BC_CMD_ID_GET_SLEEP_STATE,\n BC_CMD_ID_GET_STREAM_INFO_LIST,\n BC_CMD_ID_GET_SUPPORT,\n BC_CMD_ID_GET_SYSTEM_GENERAL,\n BC_CMD_ID_GET_TIMELAPSE_CFG,\n BC_CMD_ID_GET_VIDEO_INPUT,\n BC_CMD_ID_GET_WHITE_LED,\n BC_CMD_ID_GET_WIFI,\n BC_CMD_ID_GET_WIFI_SIGNAL,\n BC_CMD_ID_GET_ZOOM_FOCUS,\n BC_CMD_ID_PING,\n BC_CMD_ID_PTZ_CONTROL,\n BC_CMD_ID_PTZ_CONTROL_PRESET,\n BC_CMD_ID_PUSH_COORDINATE_POINT_LIST,\n BC_CMD_ID_PUSH_DINGDONG_LIST,\n BC_CMD_ID_PUSH_NET_INFO,\n BC_CMD_ID_PUSH_SERIAL,\n BC_CMD_ID_PUSH_SLEEP_STATUS,\n BC_CMD_ID_PUSH_VIDEO_INPUT,\n BC_CMD_ID_SET_AI_ALARM,\n BC_CMD_ID_SET_AUDIO_TASK,\n BC_CMD_ID_SET_MOTION_ALARM,\n BC_CMD_ID_SET_PIR_INFO,\n BC_CMD_ID_SET_WHITE_LED_STATE,\n BC_CMD_ID_SET_WHITE_LED_TASK,\n BC_CMD_ID_SET_ZOOM_FOCUS,\n BC_CMD_ID_SUPPORT,\n BC_CMD_ID_TALK_ABILITY,\n BC_CMD_ID_TALK_CONFIG,\n BC_CMD_ID_TALK_RESET,\n BC_CMD_ID_UDP_KEEP_ALIVE,\n BC_CMD_ID_VIDEO,\n BC_CMD_ID_VIDEO_STOP,\n} from \"../../protocol/constants\";\nimport {\n buildAbilityInfoExtensionXml,\n buildBinaryExtensionXml,\n buildChannelExtensionXml,\n buildPreviewStopXml,\n buildPreviewStopXmlV11,\n buildPreviewXml,\n buildPreviewXmlV11,\n buildPtzControlXml,\n buildPtzPresetXml,\n buildPtzPresetXmlV2,\n buildSirenManualXml,\n buildSirenTimesXml,\n buildStartZoomFocusXml,\n getXmlText,\n xmlEscape,\n} from \"../../protocol/xml\";\nimport type {\n AIState,\n AiTypesCacheEntry,\n BaichuanCachedPush,\n BaichuanCoordinatePointListPush,\n BaichuanDingdongListPush,\n BaichuanGetOsdDatetimeResult,\n BaichuanLedState,\n BaichuanNetInfoPush,\n BaichuanOsdChannelName,\n BaichuanOsdDatetime,\n BaichuanRecordCfg,\n BaichuanRecordSchedule,\n BaichuanSerialPush,\n BaichuanSettingsPushCacheEntry,\n BaichuanSleepState,\n BaichuanSleepStatusPush,\n BaichuanStreamInfoList,\n BaichuanVideoInputPush,\n BaichuanWifi,\n BaichuanWifiSignal,\n BatteryInfo,\n ChannelPushCacheEntry,\n ChannelPushDataEntry,\n ChannelStreamMetadata,\n DeviceAbilities,\n DeviceCapabilitiesCacheEntry,\n DeviceCapabilitiesDebugInfo,\n DeviceCapabilitiesResult,\n DeviceSupportFlags,\n DownloadRecordingParams,\n DualLensChannelAnalysis,\n DualLensChannelInfo,\n Events,\n GetRecordingVideoResult,\n GetVideoclipsParams,\n LastSleepProbe,\n NativeVideoStreamVariant,\n NvrChannelsSummaryCacheEntry,\n OsdConfig,\n PirState,\n PlaybackSnapshotStreamInfo,\n PtzCommand,\n PtzPosition,\n PtzPreset,\n RecordingFile,\n RecordingPlaybackUrls,\n RecordingsCacheEntry,\n RecordingsQueueItem,\n RecordingStreamType,\n ReolinkBaichuanChannelIdentity,\n ReolinkBaichuanChannelInfo,\n ReolinkBaichuanNetworkInfo,\n ReolinkBaichuanPorts,\n ReolinkEvent,\n ReolinkNvrDeviceGroupsResult,\n ReolinkNvrDeviceGroupSummary,\n ReolinkSimpleEvent,\n ReolinkSupportedStream,\n ReolinkVideoStreamOptionsResult,\n RtspCreateOptions,\n RunAllDiagnosticsConsecutivelyResult,\n RunMultifocalDiagnosticsConsecutivelyResult,\n SirenState,\n SleepStatus,\n StreamMetadata,\n StreamProfile,\n SupportInfo,\n TwoWayAudioConfig,\n VideoclipThumbnailResult,\n WakeUpOptions,\n WhiteLedState,\n ZoomFocusStatus,\n ZoomFocusTriplet,\n AudioTaskConfig,\n FloodlightTaskConfig,\n WhiteLedConfig,\n PirConfig,\n AiConfig,\n AudioCfgConfig,\n DayNightThresholdConfig,\n AiDenoiseConfig,\n RecEncConfig,\n MotionAlarmConfig,\n AiAlarmConfig,\n VideoInputConfig,\n SystemGeneralConfig,\n SupportConfig,\n SirenStatusConfig,\n FtpTaskConfig,\n EmailTaskConfig,\n HddInfoListConfig,\n TimelapseCfgConfig,\n AccessUserListConfig,\n OnlineUserListConfig,\n} from \"./types\";\nimport { parseXmlFragmentToJson, type XmlJsonValue } from \"./utils/xml\";\n\nimport { Jimp, JimpMime } from \"jimp\";\nimport type { CompositeStreamPipOptions } from \"../../multifocal/compositeStream\";\nimport {\n ReolinkCgiApi,\n VodFile,\n type CgiGetVideoclipsParams,\n type GetVodUrlParams,\n} from \"../cgi/ReolinkCgiApi\";\nimport { ReolinkHttpClient } from \"../http/ReolinkHttpClient\";\nimport type { ReolinkDeviceInfo, ReolinkDeviceInfoTag } from \"../types\";\nimport { computeDeviceCapabilities, parseSupportXml } from \"./capabilities\";\nimport { parseAbilityInfoXml } from \"./utils/abilityInfo\";\nimport { getAiStateViaGetAiAlarm } from \"./utils/aiState\";\nimport { parseChannelInfoPushBlocks } from \"./utils/channelInfoPush\";\nimport {\n buildChannelPushDataLogSnapshot,\n computeChannelPushUpdateFromEntry,\n} from \"./utils/channelInfoStore\";\nimport { mapToSimpleEvent } from \"./utils/events\";\nimport { formatClientIoForLog, formatErrorForLog } from \"./utils/logging\";\nimport { parseBoolean01, parseNumber } from \"./utils/parsing\";\nimport { calculatePipOverlayPosition, resolvePipMarginPx } from \"./utils/pip\";\nimport {\n buildDeletePtzPresetAttempts,\n extractFrameErrorDetails,\n isPresetEffectivelyDeleted,\n resolvePtzDirection,\n resolvePtzSpeed,\n runDeletePtzPresetAttempts,\n} from \"./utils/ptz\";\nimport {\n parseCoordinatePointListPushXml,\n parseDingdongListPushXml,\n parseNetInfoPushXml,\n parseSerialPushXml,\n parseSleepStatusPushXml,\n parseVideoInputPushXml,\n} from \"./utils/pushSettings\";\nimport { buildFileInfoListDownloadXml } from \"./utils/recordingDownload\";\nimport {\n buildFileInfoListReplayByIdXml,\n buildFileInfoListReplayByNameXml,\n buildFileInfoListStopXml,\n buildReplayStopNameFromFileName,\n type RecordingReplayStreamType,\n} from \"./utils/recordingReplay\";\nimport { sleepMs } from \"./utils/recordings\";\nimport {\n dedupeRecordingFiles,\n downloadRecordingViaFileInfoListPaged,\n listRecordingsViaFileInfoList,\n} from \"./utils/recordingsFileInfoList\";\nimport { parseChannelStreamMetadataFromGetEncXml } from \"./utils/streamMetadata\";\nimport {\n buildTalkSessionInfoFromAbility,\n sendTalkConfigWithReset,\n} from \"./utils/talkConfig\";\nimport { createBufferedTalkSession } from \"./utils/talkSession\";\nimport { discoverPerChannelUidViaCgiChannelstatus } from \"./utils/uidDiscovery\";\nimport { getXmlBlocks, getXmlTexts, parseTalkAbilityXml } from \"./xmlUtils\";\n\nimport { parseRecordingFileName } from \"./recordingFileName\";\nimport { parseEventsFromGetEventsXml } from \"./utils/eventsGetEvents\";\nimport { parsePirInfoFromXml } from \"./utils/pir\";\nimport { discoverDeviceUidForRecordings as discoverDeviceUidForRecordingsUtil } from \"./utils/uidRecordings\";\nimport type { FloodlightTaskState } from \"./utils/whiteLed\";\nimport {\n applyFloodlightOnMotionToXml,\n applyFloodlightSettingsToXml,\n applyWhiteLedBrightnessToXml,\n applyWhiteLedOnOffToXml,\n buildWhiteLedManualPayloadXml,\n parseFloodlightTaskFromXml,\n parseWhiteLedStateFromXml,\n} from \"./utils/whiteLed\";\n\ntype TalkAbility = import(\"./types\").TalkAbility;\ntype TalkSession = import(\"./types\").TalkSession;\ntype SupportItem = import(\"./types\").SupportItem;\n\nexport type {\n NativeVideoStreamVariant,\n ReolinkBaichuanPorts,\n ReolinkNvrChannelInfo,\n ReolinkSupportedStream,\n WakeUpOptions,\n} from \"./types\";\n\n// Constants to identify dual lens models\nexport const DUAL_LENS_DUAL_MOTION_MODELS = new Set<string>([\n \"Reolink Duo PoE\",\n \"Reolink Duo WiFi\",\n]);\n\nexport const DUAL_LENS_SINGLE_MOTION_MODELS = new Set<string>([\n \"Reolink TrackMix\",\n \"Reolink TrackMix PoE\",\n \"Reolink TrackMix WiFi\",\n \"RLC-81MA\",\n]);\n\nexport const DUAL_LENS_MODELS = new Set<string>([\n ...DUAL_LENS_DUAL_MOTION_MODELS,\n ...DUAL_LENS_SINGLE_MOTION_MODELS,\n]);\n\nexport const isDualLenseModel = (model: string): boolean => {\n return (\n DUAL_LENS_MODELS.has(model) || model.toLowerCase().includes(\"trackmix\")\n );\n};\n\n/**\n * Exact type values that indicate NVR/Hub devices.\n * These are checked with exact case-insensitive match.\n */\nexport const NVR_HUB_EXACT_TYPES: string[] = [\"NVR\", \"WIFI_NVR\", \"HOMEHUB\"];\n\n/**\n * Model patterns that indicate NVR/Hub devices.\n * These devices may report channelNum=1 but actually support multiple channels.\n * Case-insensitive matching is used.\n */\nexport const NVR_HUB_MODEL_PATTERNS: RegExp[] = [\n /home\\s*hub/i, // \"Home Hub\", \"HomeHub\", \"Reolink Home Hub\"\n /reolink\\s*hub/i, // \"Reolink Hub\"\n /wifi[-_\\s]*nvr/i, // \"WIFI-NVR\", \"WiFi NVR\", \"WIFI_NVR\"\n /^nvr/i, // \"NVR8-xxx\", \"NVR16-xxx\"\n /^rlk\\d+-\\d+/i, // \"RLK8-xxx\", \"RLK16-xxx\" (NVR kits)\n /^rlk\\d+w/i, // \"RLK8W-xxx\" (wireless NVR kits)\n];\n\n/**\n * Check if a model/type name indicates an NVR/Hub device.\n * Checks both exact type matches and regex patterns.\n * @param model - The device model/type string\n * @returns true if the model matches NVR/Hub patterns\n */\nexport const isNvrHubModel = (model?: string): boolean => {\n if (!model) return false;\n const normalized = model.trim();\n const upper = normalized.toUpperCase();\n\n // Check exact type matches first\n if (NVR_HUB_EXACT_TYPES.includes(upper)) return true;\n\n // Check regex patterns\n return NVR_HUB_MODEL_PATTERNS.some((pattern) => pattern.test(normalized));\n};\n\nexport class ReolinkBaichuanApi {\n readonly client: BaichuanClient;\n readonly logger: Logger;\n private readonly httpClient: ReolinkHttpClient;\n private readonly cgiApi: ReolinkCgiApi;\n private readonly nativeOnly: boolean;\n private readonly host: string;\n private readonly username: string;\n private readonly password: string;\n /**\n * Cached camera UID. May be initially undefined if not provided in the constructor.\n * Will be lazily populated on demand when needed (e.g. for recordings).\n */\n private uid: string | undefined;\n /**\n * Cached channel count from device capabilities.\n * - 1 = standalone camera\n * - >1 = NVR/Hub with multiple channels\n * Set during login or when capabilities are queried.\n */\n private _channelCount: number | undefined;\n\n private rebootAfterDisconnectionsPerMinute: number | undefined;\n private readonly disconnectStormVoluntaryAtMs: number[] = [];\n private disconnectStormRebootInFlight: Promise<void> | undefined;\n private disconnectStormLastRebootAtMs: number | undefined;\n private readonly simpleEventListeners = new Set<\n (event: ReolinkSimpleEvent) => void | Promise<void>\n >();\n private simpleEventSubscribed = false;\n private simpleEventSubscribeInFlight: Promise<void> | undefined;\n private simpleEventUnsubscribeInFlight: Promise<void> | undefined;\n private simpleEventResubscribeTimer: NodeJS.Timeout | undefined;\n private simpleEventResubscribeInFlight: Promise<void> | undefined;\n private readonly simpleEventResubscribeIntervalMs = 5 * 60_000;\n private statePollingInterval: NodeJS.Timeout | undefined;\n private udpSleepInferenceInterval: NodeJS.Timeout | undefined;\n private readonly udpLastInferredSleepStateByChannel = new Map<\n number,\n SleepStatus[\"state\"]\n >();\n private readonly udpSleepInferenceIntervalMs = 2_000;\n private lastMotionState: boolean | undefined;\n private lastAiState: AIState | undefined;\n private aiStatePollingDisabled = false;\n private aiStatePollingDisabledLogged = false;\n private readonly aiDetectTypesCache = new Map<number, AiTypesCacheEntry>();\n private readonly aiAlarmCandidateTypesCache = new Map<\n number,\n AiTypesCacheEntry\n >();\n private rtspServers = new Set<BaichuanRtspServer>(); // Track all RTSP servers for cleanup\n private readonly activeVideoMsgNums = new Map<string, number>();\n private readonly nvrChannelsSummaryCache = new Map<\n string,\n NvrChannelsSummaryCacheEntry\n >();\n\n /**\n * Cached device capabilities per channel.\n * Cache is invalidated on reconnect or after TTL (5 minutes).\n */\n private readonly deviceCapabilitiesCache = new Map<\n number,\n DeviceCapabilitiesCacheEntry\n >();\n private static readonly CAPABILITIES_CACHE_TTL_MS = 5 * 60 * 1000; // 5 minutes\n\n /**\n * Pool of dedicated BaichuanClient instances for streaming/replay operations.\n * Each replay/stream operation gets its own dedicated socket to avoid interference\n * when switching between clips or concurrent operations.\n *\n * Key: unique session ID (e.g., \"replay:channel:fileName\")\n * Value: client, refCount, createdAt\n */\n private readonly dedicatedClients = new Map<\n string,\n {\n client: BaichuanClient;\n refCount: number;\n createdAt: number;\n }\n >();\n\n /**\n * Get a summary of currently active dedicated sessions.\n * Useful for debugging/logging to see how many sockets are open.\n */\n getDedicatedSessionsSummary(): { count: number; keys: string[] } {\n return {\n count: this.dedicatedClients.size,\n keys: Array.from(this.dedicatedClients.keys()),\n };\n }\n\n /**\n * Cached per-channel data from cmd_id 145 push (NVR sends this automatically on connection).\n *\n * This unifies identity (name/uid/state) + best-effort flags (sleep/online).\n */\n private channelPushData: Map<number, ChannelPushDataEntry> = new Map();\n\n /**\n * Best-effort mapping from logical 0-based channel to the Baichuan header channelId used on NVR/Hub.\n *\n * On many NVR firmwares, cmd_id=145 push carries:\n * - <channelId>: a large internal channelId used in Baichuan headers (e.g. 54, 57, ...)\n * - <index>: a small 1-based slot index that often corresponds to the logical channel + 1\n *\n * For standalone cameras (no cmd145 push), this returns undefined.\n */\n private resolveHeaderChannelIdForLogicalChannel(\n channel: number,\n ): number | undefined {\n const ch = Number(channel);\n if (!Number.isFinite(ch)) return undefined;\n\n // Backward-compatible: if the push cache is keyed by the logical channel already.\n if (this.channelPushData.has(ch)) return ch;\n\n const wantIndex1 = ch + 1;\n const wantIndex0 = ch;\n\n let byIndex1: number | undefined;\n let byIndex0: number | undefined;\n\n for (const [headerChannelId, info] of this.channelPushData.entries()) {\n const idx = info.index;\n if (typeof idx !== \"number\" || !Number.isFinite(idx)) continue;\n if (idx === wantIndex1) byIndex1 = headerChannelId;\n if (idx === wantIndex0) byIndex0 = headerChannelId;\n }\n\n return byIndex1 ?? byIndex0;\n }\n\n private getPushCacheEntryForLogicalChannel(\n channel: number,\n ): ChannelPushDataEntry | undefined {\n const direct = this.channelPushData.get(channel);\n if (direct) return direct;\n\n const headerCh = this.resolveHeaderChannelIdForLogicalChannel(channel);\n if (headerCh == null) return undefined;\n return this.channelPushData.get(headerCh);\n }\n\n /** Public helper: best-effort per-logical-channel push cache view. */\n getChannelInfoFromPushCacheByLogicalChannel(\n channel: number,\n ): ChannelPushCacheEntry | undefined {\n const info = this.getPushCacheEntryForLogicalChannel(channel);\n if (!info) return undefined;\n const stateLower = (info.stateLower ?? info.state).toLowerCase();\n if (stateLower === \"none\") return undefined;\n return {\n name: info.name,\n uid: info.uid,\n state: info.state,\n ...(typeof info.index === \"number\" ? { index: info.index } : {}),\n ...(info.streamSupport?.length\n ? { streamSupport: info.streamSupport }\n : {}),\n ...(info.wifiState ? { wifiState: info.wifiState } : {}),\n ...(info.networkSegment ? { networkSegment: info.networkSegment } : {}),\n ...(typeof info.changed === \"boolean\" ? { changed: info.changed } : {}),\n ...(typeof info.abilityChanged === \"boolean\"\n ? { abilityChanged: info.abilityChanged }\n : {}),\n ...(typeof info.online === \"boolean\" ? { online: info.online } : {}),\n ...(typeof info.sleeping === \"boolean\"\n ? { sleeping: info.sleeping }\n : {}),\n ...(info.loginState ? { loginState: info.loginState } : {}),\n ...(typeof info.updatedAtMs === \"number\"\n ? { updatedAtMs: info.updatedAtMs }\n : {}),\n };\n }\n\n /** Cache populated from device->client push frames (cmd_id 78/79/464/484/623/723). */\n private readonly settingsPushCache = new Map<\n number,\n BaichuanSettingsPushCacheEntry\n >();\n\n private lastSleepProbe: LastSleepProbe | undefined;\n\n /**\n * Local cache for recordings. Key is a composite of channel, start, end, streamType.\n * Value contains the cached enriched recordings and timestamp.\n * Unified cache for both NVR and Device recordings (always enriched).\n */\n private recordingsCache = new Map<string, RecordingsCacheEntry>();\n\n /**\n * Queue for serializing listRecordings calls to prevent socket crashes from concurrent requests.\n */\n private recordingsQueue: RecordingsQueueItem[] = [];\n private recordingsQueueProcessing = false;\n\n /**\n * Map for de-duplicating in-flight replay operations.\n * Key is a composite of operation type + fileName, value is the pending promise.\n */\n private pendingReplayOperations = new Map<string, Promise<unknown>>();\n\n /**\n * Simple async queue for serializing replay operations.\n * Operations are executed one at a time in FIFO order.\n */\n private replayQueue: Array<{\n execute: () => Promise<void>;\n }> = [];\n private replayQueueProcessing = false;\n\n /** Minimum delay between replay operations to give camera time to reset */\n private readonly REPLAY_COOLDOWN_MS = 500;\n private lastReplayEndTime = 0;\n\n /**\n * Queue for serializing getVideoclipThumbnail calls.\n * Only one snapshot request can be in-flight at a time since the camera\n * often rejects concurrent CoverPreview requests.\n */\n private videoclipThumbnailInFlight: Promise<VideoclipThumbnailResult> | null =\n null;\n private videoclipThumbnailQueue: Array<{\n params: Parameters<ReolinkBaichuanApi[\"getVideoclipThumbnail\"]>[0];\n resolve: (result: VideoclipThumbnailResult) => void;\n reject: (error: Error) => void;\n }> = [];\n\n /**\n * Process the replay queue - executes operations one at a time.\n */\n private async processReplayQueue(): Promise<void> {\n if (this.replayQueueProcessing) return;\n this.replayQueueProcessing = true;\n\n while (this.replayQueue.length > 0) {\n const item = this.replayQueue.shift();\n if (item) {\n // Ensure minimum cooldown between replay operations\n const timeSinceLastReplay = Date.now() - this.lastReplayEndTime;\n if (timeSinceLastReplay < this.REPLAY_COOLDOWN_MS) {\n await new Promise((r) =>\n setTimeout(r, this.REPLAY_COOLDOWN_MS - timeSinceLastReplay),\n );\n }\n\n await item.execute();\n\n // Record when this operation ended\n this.lastReplayEndTime = Date.now();\n }\n }\n\n this.replayQueueProcessing = false;\n }\n\n /**\n * Enqueue a replay operation with optional de-duplication.\n * If dedupKey is provided and an operation with that key is in progress, returns the existing promise.\n * Operations are serialized - only one runs at a time.\n */\n private enqueueReplayOperation<T>(\n operation: () => Promise<T>,\n dedupKey?: string,\n ): Promise<T> {\n // Check for de-duplication\n if (dedupKey) {\n const existing = this.pendingReplayOperations.get(dedupKey);\n if (existing) {\n this.logger?.debug?.(\n `[ReplayQueue] Reusing existing promise for: ${dedupKey}`,\n );\n return existing as Promise<T>;\n }\n }\n\n // Create the promise that will be resolved when the operation completes\n let resolvePromise: (value: T) => void;\n let rejectPromise: (error: unknown) => void;\n const promise = new Promise<T>((resolve, reject) => {\n resolvePromise = resolve;\n rejectPromise = reject;\n });\n\n // Track for de-duplication if key provided\n if (dedupKey) {\n this.pendingReplayOperations.set(dedupKey, promise);\n promise.finally(() => {\n this.pendingReplayOperations.delete(dedupKey);\n });\n }\n\n // Add to queue\n this.replayQueue.push({\n execute: async () => {\n try {\n const result = await operation();\n resolvePromise(result);\n } catch (e) {\n rejectPromise(e);\n }\n },\n });\n\n // Start processing (no-op if already processing)\n void this.processReplayQueue();\n\n return promise;\n }\n\n /**\n * Enqueue a streaming replay operation.\n * The queue slot is held until the returned release function is called.\n * This is for operations like createRecordingReplayMp4Stream where the stream\n * continues producing data after the initial setup.\n *\n * @param setup - Function that sets up the stream. Called when it's this operation's turn.\n * @returns Promise that resolves when setup is complete, with the result and a release function.\n */\n private enqueueStreamingReplayOperation<T>(\n setup: () => Promise<T>,\n ): Promise<{ result: T; release: () => void }> {\n let resolvePromise: (value: { result: T; release: () => void }) => void;\n let rejectPromise: (error: unknown) => void;\n const promise = new Promise<{ result: T; release: () => void }>(\n (resolve, reject) => {\n resolvePromise = resolve;\n rejectPromise = reject;\n },\n );\n\n // Add to queue\n this.replayQueue.push({\n execute: () => {\n return new Promise<void>((releaseSlot) => {\n // Run the setup\n setup()\n .then((result) => {\n // Setup succeeded - resolve with result and release function\n resolvePromise({\n result,\n release: () => releaseSlot(),\n });\n })\n .catch((e) => {\n // Setup failed - reject and release slot\n rejectPromise(e);\n releaseSlot();\n });\n });\n },\n });\n\n // Start processing (no-op if already processing)\n void this.processReplayQueue();\n\n return promise;\n }\n\n /**\n * Determine streamType from fileName automatically.\n * Checks if there's an 'S' in the first 10 characters of the basename,\n * which indicates subStream (e.g., RecS03_, RecS_).\n */\n private determineStreamTypeFromFileName(\n fileName: string,\n ): RecordingReplayStreamType {\n // Extract basename (last part of path)\n const basename = fileName.split(\"/\").pop() ?? fileName;\n // Check first 10 characters for 'S' (case-insensitive)\n const prefix = basename.substring(0, 10).toUpperCase();\n return prefix.includes(\"S\") ? \"subStream\" : \"mainStream\";\n }\n\n /**\n * Cache for buildVideoStreamOptions.\n *\n * IMPORTANT: only the first non-empty result is cached per key.\n * Empty results are considered transient (common on NVR/Hub) and won't overwrite a good cache.\n */\n private readonly videoStreamOptionsCache = new Map<\n string,\n ReolinkVideoStreamOptionsResult\n >();\n\n /**\n * Process recordings queue sequentially to prevent socket crashes from concurrent requests.\n */\n private async processRecordingsQueue(): Promise<void> {\n if (this.recordingsQueueProcessing || this.recordingsQueue.length === 0) {\n return;\n }\n\n this.recordingsQueueProcessing = true;\n\n while (this.recordingsQueue.length > 0) {\n const item = this.recordingsQueue.shift();\n if (!item) break;\n await item.run();\n }\n\n this.recordingsQueueProcessing = false;\n }\n\n /**\n * Enqueue a recordings operation to be processed sequentially.\n */\n private async enqueueRecordingsOperation<T>(\n operation: () => Promise<T>,\n ): Promise<T> {\n return new Promise<T>((resolve, reject) => {\n this.recordingsQueue.push({\n run: async () => {\n try {\n resolve(await operation());\n } catch (e) {\n reject(e);\n }\n },\n });\n void this.processRecordingsQueue();\n });\n }\n\n private recordingsCacheTtlMs = 20 * 60 * 1000;\n\n /**\n * Create or reuse a dedicated BaichuanClient for streaming/replay operations.\n * Each streaming session gets its own socket to avoid interference when switching clips.\n *\n * @param sessionKey - Unique key for this session (e.g., `replay:\\$\\{deviceId\\}`)\n * @returns The dedicated client and a release function to call when done\n *\n * IMPORTANT: A socket cannot do concurrent streaming. If a client already exists\n * for this sessionKey (same device switching clips), we close the old socket\n * immediately and create a new one. This ensures clean state for each clip.\n */\n private async acquireDedicatedClient(\n sessionKey: string,\n logger?: Logger,\n ): Promise<{\n client: BaichuanClient;\n release: () => Promise<void>;\n }> {\n // Check if there's an existing client for this session key\n // If so, close it immediately - a socket can't do concurrent streaming\n const existing = this.dedicatedClients.get(sessionKey);\n if (existing) {\n logger?.debug?.(\n `[DedicatedClient] Closing existing client for ${sessionKey} (new stream requested)`,\n );\n this.dedicatedClients.delete(sessionKey);\n // Close asynchronously - don't block the new connection\n existing.client\n .close({ reason: \"new stream for same device\" })\n .catch((e) => {\n logger?.debug?.(`[DedicatedClient] Error closing old socket: ${e}`);\n });\n }\n\n // Create a new dedicated client with the same credentials\n logger?.debug?.(`[DedicatedClient] Creating new client for ${sessionKey}`);\n const dedicatedClient = new BaichuanClient({\n host: this.host,\n username: this.username,\n password: this.password,\n logger: logger ?? this.logger,\n debugOptions: this.client.getDebugConfig?.(),\n });\n\n // Login the dedicated client\n await dedicatedClient.login();\n\n this.dedicatedClients.set(sessionKey, {\n client: dedicatedClient,\n refCount: 1, // Keep for compatibility, but not used for reuse logic\n createdAt: Date.now(),\n });\n\n return {\n client: dedicatedClient,\n release: () => this.releaseDedicatedClient(sessionKey, logger),\n };\n }\n\n /**\n * Release a dedicated client. Always closes the socket immediately.\n * This ensures clean teardown at the end of each clip.\n */\n private async releaseDedicatedClient(\n sessionKey: string,\n logger?: Logger,\n ): Promise<void> {\n const entry = this.dedicatedClients.get(sessionKey);\n if (!entry) return;\n\n // Always remove and close - no refCount logic\n this.dedicatedClients.delete(sessionKey);\n logger?.debug?.(`[DedicatedClient] Releasing and closing ${sessionKey}`);\n\n try {\n await entry.client.close({ reason: \"dedicated session ended\" });\n } catch (e) {\n logger?.debug?.(`[DedicatedClient] Error closing socket: ${e}`);\n }\n }\n\n /**\n * Create a dedicated Baichuan client session for streaming.\n * This is useful for consumers that need isolated socket connections per stream.\n *\n * @param sessionKey - Unique key for this session (e.g., `live:\\$\\{deviceId\\}:\\$\\{channel\\}:\\$\\{profile\\}`)\n * @param logger - Optional logger for debug output\n * @returns Object with `client` (the dedicated BaichuanClient) and `release` function to call when done\n *\n * The dedicated client is automatically cleaned up when:\n * 1. `release()` is called explicitly\n * 2. A new session is created with the same sessionKey (old one is closed first)\n * 3. The API is closed via `close()`\n *\n * @example\n * ```typescript\n * const { client, release } = await api.createDedicatedSession('live:device123:ch0:main');\n * try {\n * // Use client for streaming...\n * } finally {\n * await release();\n * }\n * ```\n */\n async createDedicatedSession(\n sessionKey: string,\n logger?: Logger,\n ): Promise<{\n client: BaichuanClient;\n release: () => Promise<void>;\n }> {\n return await this.acquireDedicatedClient(sessionKey, logger);\n }\n\n /**\n * Cleanup all dedicated clients. Called during API close.\n */\n private async cleanupDedicatedClients(): Promise<void> {\n const entries = Array.from(this.dedicatedClients.entries());\n this.dedicatedClients.clear();\n\n await Promise.allSettled(\n entries.map(async ([key, entry]) => {\n try {\n this.logger?.debug?.(`[DedicatedClient] Cleanup: closing ${key}`);\n await entry.client.close({ reason: \"API cleanup\" });\n } catch {\n // ignore\n }\n }),\n );\n }\n\n private dispatchSimpleEvent(evt: ReolinkSimpleEvent): void {\n const debugCfg = this.client.getDebugConfig?.();\n if (debugCfg) {\n const sid = this.client.getSocketSessionId?.();\n const tag = sid ? `ReolinkSimpleEvent sid=${sid}` : \"ReolinkSimpleEvent\";\n eventTraceLog(\n debugCfg,\n this.logger,\n tag,\n `dispatch type=${evt.type} channel=${evt.channel} timestamp=${evt.timestamp}`,\n );\n }\n\n for (const cb of this.simpleEventListeners) {\n try {\n // Support async handlers (common in Scrypted plugins) without unhandled rejections.\n void Promise.resolve(cb(evt)).catch((e: unknown) => {\n (this.logger.warn ?? this.logger.error).call(\n this.logger,\n \"[ReolinkBaichuanApi] onSimpleEvent handler error\",\n formatErrorForLog(e),\n );\n });\n } catch (e) {\n // Never allow user handlers to break the Baichuan client's event loop.\n (this.logger.warn ?? this.logger.error).call(\n this.logger,\n \"[ReolinkBaichuanApi] onSimpleEvent handler error\",\n formatErrorForLog(e),\n );\n }\n }\n }\n\n constructor(\n opts: BaichuanClientOptions & {\n /**\n * Reboot the device if there are too many *voluntary* disconnects within 60 seconds.\n *\n * The count is based on `BaichuanClient.close({ reason: ... })` / idle disconnects.\n * Remote/firmware-initiated closes are ignored.\n */\n rebootAfterDisconnectionsPerMinute?: number;\n /** If true, avoid using HTTP/CGI fallbacks and discovery paths (native Baichuan only). */\n nativeOnly?: boolean;\n },\n ) {\n const dbg = normalizeDebugOptions(opts.debugOptions);\n // Centralized verbosity control: treat `.debug()` as opt-in.\n this.logger = createDebugGateLogger(\n opts.logger,\n dbg.general ||\n dbg.traceNativeStream ||\n dbg.traceRecordings ||\n dbg.traceTalk ||\n dbg.traceEvents ||\n dbg.debugRtsp,\n );\n this.client = new BaichuanClient(opts);\n this.host = opts.host;\n this.username = opts.username;\n this.password = opts.password;\n this.uid = opts.uid;\n this.nativeOnly = opts.nativeOnly ?? false;\n this.httpClient = new ReolinkHttpClient({\n host: opts.host,\n username: opts.username,\n password: opts.password,\n timeoutMs: 600_000,\n });\n this.cgiApi = new ReolinkCgiApi({\n host: opts.host,\n username: opts.username,\n password: opts.password,\n logger: this.logger,\n debugConfig: this.client.getDebugConfig?.(),\n });\n\n // Dispatch parsed events in a minimal, stable shape.\n this.client.on(\"event\", (event) => {\n const mapped = mapToSimpleEvent(event);\n if (!mapped) return;\n\n this.dispatchSimpleEvent(mapped);\n });\n\n // Handle channel info push from NVR (cmd_id 145)\n this.client.on(\"channelInfo\", (xml: string) => {\n try {\n this.parseAndStoreChannelInfo(xml);\n } catch (e: unknown) {\n this.logger.warn?.(\n \"[ReolinkBaichuanApi] Error parsing channel info from push\",\n formatErrorForLog(e),\n );\n }\n });\n\n // Parse and cache additional push frames observed in settings PCAPs.\n this.client.on(\"push\", (frame) => {\n const cmdId = frame.header.cmdId;\n if (\n cmdId !== BC_CMD_ID_PUSH_VIDEO_INPUT &&\n cmdId !== BC_CMD_ID_PUSH_SERIAL &&\n cmdId !== BC_CMD_ID_PUSH_NET_INFO &&\n cmdId !== BC_CMD_ID_PUSH_DINGDONG_LIST &&\n cmdId !== BC_CMD_ID_PUSH_SLEEP_STATUS &&\n cmdId !== BC_CMD_ID_PUSH_COORDINATE_POINT_LIST\n ) {\n return;\n }\n\n try {\n if (frame.body.length === 0) return;\n const xml = this.client.tryDecryptXml(\n frame.body,\n frame.header.channelId,\n this.client.enc,\n );\n if (!xml || !xml.startsWith(\"<?xml\")) return;\n this.parseAndStoreSettingsPush(cmdId, xml, frame.header.channelId);\n } catch (e: unknown) {\n this.logger.debug?.(\n \"[ReolinkBaichuanApi] Error parsing settings push\",\n formatErrorForLog(e),\n );\n }\n });\n\n const v = opts.rebootAfterDisconnectionsPerMinute;\n if (typeof v === \"number\" && Number.isFinite(v) && v > 0) {\n this.rebootAfterDisconnectionsPerMinute = Math.floor(v);\n this.client.on(\"close\", () => {\n try {\n void this.maybeRebootOnDisconnectStorm();\n } catch {\n // never throw from close handler\n }\n });\n }\n }\n\n /**\n * CGI forward: fetch RTSP URL for a channel via `GetRtspUrl`.\n * Request body:\n * `[{\"cmd\":\"GetRtspUrl\",\"action\":0,\"param\":{\"channel\":<channel>}}]`.\n */\n async getRtspUrl(channel: number): Promise<string> {\n const ch = this.normalizeChannel(channel);\n return await this.cgiApi.getRtspUrl(ch);\n }\n\n // --------------------\n // Recordings Cache Methods\n // --------------------\n\n /**\n * Generate a cache key for recordings lookup.\n */\n private getRecordingsCacheKey(\n channel: number,\n start: Date,\n end: Date,\n streamType: string,\n ): string {\n return `${channel}:${start.getTime()}:${end.getTime()}:${streamType}`;\n }\n\n /**\n * Get cached recordings if available and not expired.\n */\n private getCachedRecordings(\n channel: number,\n start: Date,\n end: Date,\n streamType: string,\n ): RecordingFile[] | undefined {\n const key = this.getRecordingsCacheKey(channel, start, end, streamType);\n const cached = this.recordingsCache.get(key);\n\n if (!cached) return undefined;\n\n const now = Date.now();\n if (now - cached.cachedAt > cached.ttlMs) {\n // Cache expired, remove it\n this.recordingsCache.delete(key);\n return undefined;\n }\n\n return cached.recordings;\n }\n\n /**\n * Cache recordings for future lookups.\n */\n private cacheRecordings(\n channel: number,\n start: Date,\n end: Date,\n streamType: string,\n recordings: RecordingFile[],\n ttlMs?: number,\n ): void {\n const key = this.getRecordingsCacheKey(channel, start, end, streamType);\n this.recordingsCache.set(key, {\n recordings,\n cachedAt: Date.now(),\n ttlMs: ttlMs ?? this.recordingsCacheTtlMs,\n });\n }\n\n /**\n * Clear all cached recordings.\n */\n clearRecordingsCache(): void {\n this.recordingsCache.clear();\n }\n\n /**\n * Clear cached recordings for a specific time range.\n */\n clearRecordingsCacheForRange(\n channel: number,\n start: Date,\n end: Date,\n streamType: string,\n ): void {\n const key = this.getRecordingsCacheKey(channel, start, end, streamType);\n this.recordingsCache.delete(key);\n }\n\n /**\n * Set the default TTL for recordings cache.\n * @param ttlMs - TTL in milliseconds (default: 5 minutes = 300000ms)\n */\n setRecordingsCacheTtl(ttlMs: number): void {\n this.recordingsCacheTtlMs = ttlMs;\n }\n\n /**\n * Convenience helper: run all supported diagnostics sequentially into a single output folder.\n *\n * This collects:\n * - Native (Baichuan) diagnostics\n * - Optional CGI diagnostics (if enabled)\n * - Stream sampling (native/rtsp/rtmp, for `selection`)\n *\n * Output layout:\n * - `outDir/<timestamp>/...` contains the raw run artifacts\n * - `outDir/<timestamp>.zip` contains the zipped bundle\n */\n async runAllDiagnosticsConsecutively(\n params: RunAllDiagnosticsConsecutivelyParams,\n ): Promise<RunAllDiagnosticsConsecutivelyResult> {\n return await runAllDiagnosticsConsecutively({\n ...params,\n api: this,\n logger: this.logger,\n host: this.host,\n username: this.username,\n password: this.password,\n });\n }\n\n /**\n * Multifocal/NVR empirical stream diagnostics:\n * probes RTSP/RTMP candidates + native streams, prints discovered resolutions,\n * and saves one clip per working stream into a timestamped folder under outDir.\n */\n async runMultifocalDiagnosticsConsecutively(\n params: Omit<\n RunMultifocalDiagnosticsConsecutivelyParams,\n \"api\" | \"host\" | \"username\" | \"password\" | \"logger\"\n > & {\n /** Optional logger from the caller (preferred over the API logger). */\n logger?: RunMultifocalDiagnosticsConsecutivelyParams[\"logger\"];\n },\n ): Promise<RunMultifocalDiagnosticsConsecutivelyResult> {\n return await runMultifocalDiagnosticsConsecutively({\n ...params,\n api: this,\n logger: params.logger ?? this.logger,\n host: this.host,\n username: this.username,\n password: this.password,\n });\n }\n\n private async maybeRebootOnDisconnectStorm(): Promise<void> {\n const threshold = this.rebootAfterDisconnectionsPerMinute;\n if (threshold == null) return;\n\n const info = this.client.getLastDisconnectInfo?.();\n if (!info?.voluntary) return;\n\n const now = Date.now();\n const windowMs = 60_000;\n const cutoff = now - windowMs;\n while (\n this.disconnectStormVoluntaryAtMs.length &&\n this.disconnectStormVoluntaryAtMs[0]! < cutoff\n ) {\n this.disconnectStormVoluntaryAtMs.shift();\n }\n this.disconnectStormVoluntaryAtMs.push(now);\n\n if (this.disconnectStormVoluntaryAtMs.length < threshold) return;\n\n if (this.disconnectStormRebootInFlight) return;\n const cooldownMs = 10 * 60_000;\n if (\n this.disconnectStormLastRebootAtMs != null &&\n now - this.disconnectStormLastRebootAtMs < cooldownMs\n )\n return;\n\n this.disconnectStormLastRebootAtMs = now;\n (this.logger.warn ?? this.logger.error).call(\n this.logger,\n \"[ReolinkBaichuanApi] disconnect storm detected; rebooting device\",\n {\n transport: info.transport,\n reason: info.reason,\n voluntaryDisconnectsInWindow: this.disconnectStormVoluntaryAtMs.length,\n windowMs,\n threshold,\n cooldownMs,\n method: \"auto\",\n },\n );\n\n this.disconnectStormRebootInFlight = this.rebootFromDisconnectStorm(\"auto\")\n .catch((e) => {\n (this.logger.warn ?? this.logger.error).call(\n this.logger,\n \"[ReolinkBaichuanApi] disconnect-storm reboot failed\",\n e,\n );\n })\n .finally(() => {\n this.disconnectStormRebootInFlight = undefined;\n });\n }\n\n private async rebootFromDisconnectStorm(\n method: \"auto\" | \"baichuan\" | \"cgi\",\n ): Promise<void> {\n let lastErr: unknown;\n\n if (method === \"auto\" || method === \"baichuan\") {\n try {\n await this.reboot();\n return;\n } catch (e) {\n lastErr = e;\n if (method === \"baichuan\") throw e;\n }\n }\n\n if (method === \"auto\" || method === \"cgi\") {\n try {\n await this.cgiApi.login();\n await this.cgiApi.Reboot();\n return;\n } catch (e) {\n lastErr = e;\n if (method === \"cgi\") throw e;\n }\n }\n\n throw lastErr instanceof Error\n ? lastErr\n : new Error(String(lastErr ?? \"disconnect-storm reboot failed\"));\n }\n\n /**\n * Subscribe to minimal high-level events.\n * The API manages Baichuan subscribe/unsubscribe automatically.\n */\n async onSimpleEvent(\n callback: (event: ReolinkSimpleEvent) => void | Promise<void>,\n ): Promise<void> {\n this.simpleEventListeners.add(callback);\n await this.ensureSimpleEventSubscribed();\n this.startSimpleEventResubscribeTimer();\n }\n\n /**\n * Remove one callback, or all callbacks if omitted.\n * When the last listener is removed, the API unsubscribes from Baichuan events.\n */\n async offSimpleEvent(\n callback?: (event: ReolinkSimpleEvent) => void | Promise<void>,\n ): Promise<void> {\n if (callback) {\n this.simpleEventListeners.delete(callback);\n } else {\n this.simpleEventListeners.clear();\n }\n\n if (this.simpleEventListeners.size === 0) {\n this.stopSimpleEventResubscribeTimer();\n this.stopUdpSleepInference();\n await this.ensureSimpleEventUnsubscribed();\n } else {\n // If there are still listeners, keep polling running (TCP only)\n const isUdp = this.client.getTransport?.() === \"udp\";\n if (isUdp) {\n this.startUdpSleepInference();\n } else if (this.client.isStatePollingEnabled?.()) {\n this.startStatePolling();\n }\n }\n }\n\n private startSimpleEventResubscribeTimer(): void {\n if (this.simpleEventResubscribeTimer) return;\n if (this.simpleEventListeners.size === 0) return;\n\n this.simpleEventResubscribeTimer = setInterval(() => {\n // Best-effort renew: some devices silently drop subscriptions without closing the socket.\n void this.renewSimpleEventSubscription();\n }, this.simpleEventResubscribeIntervalMs);\n }\n\n private stopSimpleEventResubscribeTimer(): void {\n if (!this.simpleEventResubscribeTimer) return;\n clearInterval(this.simpleEventResubscribeTimer);\n this.simpleEventResubscribeTimer = undefined;\n }\n\n private async renewSimpleEventSubscription(): Promise<void> {\n if (this.simpleEventListeners.size === 0) return;\n if (this.simpleEventResubscribeInFlight)\n return await this.simpleEventResubscribeInFlight;\n\n this.simpleEventResubscribeInFlight = (async () => {\n try {\n await this.subscribeEvents();\n this.simpleEventSubscribed = true;\n (this.logger.debug ?? this.logger.log).call(\n this.logger,\n \"[ReolinkBaichuanApi] renewed simple event subscription\",\n {\n intervalMs: this.simpleEventResubscribeIntervalMs,\n },\n );\n } catch (e) {\n (this.logger.debug ?? this.logger.log).call(\n this.logger,\n \"[ReolinkBaichuanApi] failed to renew event subscription\",\n e,\n );\n }\n })().finally(() => {\n this.simpleEventResubscribeInFlight = undefined;\n });\n\n return await this.simpleEventResubscribeInFlight;\n }\n\n private async ensureSimpleEventSubscribed(): Promise<void> {\n if (this.simpleEventListeners.size === 0) return;\n if (this.simpleEventSubscribed) return;\n if (this.simpleEventSubscribeInFlight)\n return await this.simpleEventSubscribeInFlight;\n\n this.simpleEventSubscribeInFlight = (async () => {\n // If the caller already subscribed (e.g. NVR shared connection using subscribeToAllEvents),\n // don't resubscribe.\n if (!this.client.subscribed) {\n await this.subscribeEvents();\n }\n this.simpleEventSubscribed = true;\n\n // Only check current state and start polling for TCP connections (not UDP/battery cameras)\n // UDP/battery cameras should rely on event pushes only, not polling\n const isUdp = this.client.getTransport?.() === \"udp\";\n if (isUdp) {\n // Passive sleep inference for UDP/battery cameras.\n // This does not send any requests and restores sleeping/awake events.\n this.startUdpSleepInference();\n } else if (this.client.isStatePollingEnabled?.()) {\n const channel = this.client.getConfiguredChannel?.() ?? 0;\n // Check current state and dispatch events immediately (TCP only)\n await this.checkAndDispatchCurrentState(channel);\n\n // Start periodic polling if not already running (TCP only)\n this.startStatePolling();\n }\n })().finally(() => {\n this.simpleEventSubscribeInFlight = undefined;\n });\n\n return await this.simpleEventSubscribeInFlight;\n }\n\n private async ensureSimpleEventUnsubscribed(): Promise<void> {\n if (!this.simpleEventSubscribed && !this.client.subscribed) return;\n if (this.simpleEventUnsubscribeInFlight)\n return await this.simpleEventUnsubscribeInFlight;\n\n if (this.simpleEventSubscribeInFlight) {\n try {\n await this.simpleEventSubscribeInFlight;\n } catch {\n // ignore\n }\n }\n\n this.simpleEventUnsubscribeInFlight = (async () => {\n await this.unsubscribeEvents();\n this.simpleEventSubscribed = false;\n\n // Stop renew timer when unsubscribed.\n this.stopSimpleEventResubscribeTimer();\n\n // Stop polling when no more listeners\n this.stopStatePolling();\n\n // Stop UDP sleep inference when unsubscribed.\n this.stopUdpSleepInference();\n })().finally(() => {\n this.simpleEventUnsubscribeInFlight = undefined;\n });\n\n return await this.simpleEventUnsubscribeInFlight;\n }\n\n private normalizeChannel(channel?: number | null): number {\n return channel == null ? 0 : channel;\n }\n\n /**\n * Returns the cached channel count, or fetches it from device capabilities if not cached.\n * - 1 = standalone camera\n * - >1 = NVR/Hub with multiple channels\n */\n async getChannelCount(): Promise<number> {\n if (this._channelCount !== undefined) {\n return this._channelCount;\n }\n\n try {\n const support = await this.getAbilitySupport(0);\n const channelNum =\n (support as Record<string, unknown>)?.AbilitySupport &&\n typeof (support as Record<string, unknown>).AbilitySupport === \"object\"\n ? ((support as Record<string, Record<string, unknown>>).AbilitySupport\n ?.channelNum as number | string | undefined)\n : undefined;\n\n if (channelNum !== undefined) {\n this._channelCount =\n typeof channelNum === \"string\"\n ? Number.parseInt(channelNum, 10)\n : channelNum;\n }\n } catch {\n // Ignore errors - will default to 1\n }\n\n // Default to 1 (standalone camera) if not determinable\n if (\n this._channelCount === undefined ||\n !Number.isFinite(this._channelCount)\n ) {\n this._channelCount = 1;\n }\n\n return this._channelCount;\n }\n\n /**\n * Determines if this device is an NVR/Hub (multiple channels) vs a standalone camera.\n * Checks:\n * 1. Channel count > 1 (typical NVR detection)\n * 2. Device model matches NVR/Hub patterns (for devices like Home Hub that report channelNum=1)\n */\n async isNvrDevice(): Promise<boolean> {\n const channelCount = await this.getChannelCount();\n if (channelCount > 3) return true;\n\n // Fallback: check device type for NVR/Hub patterns\n // Some devices (e.g., Home Hub) report channelNum=1 but are actually NVR/Hub\n try {\n const info = await this.getInfo(undefined, {\n tags: [\"type\"],\n timeoutMs: 5000,\n });\n if (isNvrHubModel(info.type)) {\n this.logger.debug?.(\n `[ReolinkBaichuanApi] isNvrDevice: type=\"${info.type}\" matches NVR/Hub pattern`,\n );\n return true;\n }\n } catch {\n // Ignore errors - model check is best-effort\n }\n\n return false;\n }\n\n async login(\n maxEncryption?: import(\"../../client/BaichuanClient.js\").MaxEncryption,\n ): Promise<void> {\n await this.client.login(maxEncryption);\n }\n\n async close(options?: { reason?: string }): Promise<void> {\n // Stop state polling before closing\n this.stopStatePolling();\n this.stopUdpSleepInference();\n // Stop all RTSP servers before closing the client\n await this.cleanup();\n // Cleanup dedicated clients used for streaming/replay\n await this.cleanupDedicatedClients();\n await this.client.close(\n options?.reason ? { reason: options.reason } : undefined,\n );\n }\n\n /**\n * Cleanup all RTSP servers and release resources.\n * This should be called when the API instance is being destroyed to prevent memory leaks.\n */\n async cleanup(): Promise<void> {\n const servers = Array.from(this.rtspServers);\n this.rtspServers.clear();\n\n // Stop all servers in parallel\n await Promise.allSettled(\n servers.map(async (server) => {\n try {\n await server.stop();\n } catch (error) {\n this.logger.error(\n `[ReolinkBaichuanApi] Error stopping RTSP server during cleanup:`,\n error,\n );\n }\n }),\n );\n\n if (servers.length > 0) {\n this.logger.info(\n `[ReolinkBaichuanApi] Cleaned up ${servers.length} RTSP server(s)`,\n );\n }\n }\n\n /** Generic Baichuan cmd_id call, returns XML (if any). */\n async sendXml(\n params: Parameters<BaichuanClient[\"sendXml\"]>[0],\n retry = 3,\n ): Promise<string> {\n // Only call login() if not already logged in (avoid recursion if called from login itself)\n if (!this.client.loggedIn) {\n await this.client.login();\n }\n\n // Use sendFrame to check responseCode and handle 400 errors with retry\n const frame = await this.client.sendFrame(params);\n if (frame.header.responseCode === 400) {\n return await this.handleSendXml400(params, frame, retry);\n }\n\n // Decrypt and return XML\n if (frame.body.length === 0) return \"\";\n return this.client.tryDecryptXml(\n frame.body,\n frame.header.channelId,\n this.client.enc,\n );\n }\n\n private isSendXmlFailFast400(\n params: Parameters<BaichuanClient[\"sendXml\"]>[0],\n bodyLen: number,\n ): boolean {\n // Special cases for NVR/Hub firmwares: some commands may return 400 with an empty body\n // when unsupported (not just for auth/session issues). Retrying/login loops can stall tests.\n //\n // - FILE_INFO_LIST_GET with 400+empty body during pagination means \"no more pages\".\n // - FILE_INFO_LIST_OPEN with 400+empty body often means \"FileInfoList unsupported\" on NVRs.\n // - Motion alarm commands (46/47) can fail on some cameras; avoid retry loops.\n // In both cases, fail fast and let higher-level code fall back to findAlarmVideo/CGI.\n return (\n bodyLen === 0 &&\n (params.cmdId === BC_CMD_ID_FILE_INFO_LIST_GET ||\n params.cmdId === BC_CMD_ID_FILE_INFO_LIST_OPEN ||\n // Non-PTZ cameras commonly return 400+empty body for PTZ preset APIs.\n // Treat it as \"unsupported\" rather than triggering relogin loops.\n params.cmdId === BC_CMD_ID_GET_PTZ_PRESET ||\n // Motion alarm commands may fail on some cameras/firmwares.\n // Fail fast to avoid re-login loops.\n params.cmdId === BC_CMD_ID_GET_MOTION_ALARM ||\n params.cmdId === BC_CMD_ID_SET_MOTION_ALARM)\n );\n }\n\n private async handleSendXml400(\n params: Parameters<BaichuanClient[\"sendXml\"]>[0],\n frame: Awaited<ReturnType<BaichuanClient[\"sendFrame\"]>>,\n retry: number,\n ): Promise<string> {\n const emptyBody = frame.body.length === 0;\n const emptyBody400Msg =\n \"Baichuan request failed (responseCode 400, empty body). Possible causes: camera sleeping/waking (battery), expired session, invalid username/password, or unsupported command on NVR/Hub.\";\n\n if (this.isSendXmlFailFast400(params, frame.body.length)) {\n throw new Error(emptyBody400Msg);\n }\n\n // Retry logic for 400 errors (without re-login to avoid disconnection loops).\n // NOTE: several firmwares return responseCode=400 with empty body when the camera is sleeping,\n // waking up, or when the session has expired (not only for bad credentials).\n // Previously we tried forcing re-login here, but that caused cascading disconnects.\n // Now we simply backoff and retry without re-login.\n if (retry > 0) {\n await sleepMs(1500);\n return await this.sendXml(params, retry - 1);\n }\n\n // Out of retries.\n if (emptyBody) {\n throw new Error(emptyBody400Msg);\n }\n\n // Some firmwares still send a body even with responseCode=400.\n return this.client.tryDecryptXml(\n frame.body,\n frame.header.channelId,\n this.client.enc,\n );\n }\n\n /**\n * Fetch TalkAbility (cmd_id=10) which describes supported two-way audio formats.\n * Uses MSG_ID_TALKABILITY.\n */\n async getTalkAbility(channel?: number): Promise<TalkAbility> {\n const xml = await this.sendXml({\n cmdId: BC_CMD_ID_TALK_ABILITY,\n ...(channel !== undefined ? { channel } : {}),\n });\n return parseTalkAbilityXml(xml);\n }\n\n /**\n * TalkReset via Baichuan: cmd_id 11 (MSG_ID_TALKRESET).\n * Mostly useful for recovering from responseCode=422 after TalkConfig.\n */\n async talkReset(\n channel = 0,\n options?: { channelIdOverride?: number },\n ): Promise<void> {\n await this.client.login();\n const ch = this.normalizeChannel(channel);\n\n const isUdp = this.client.getTransport?.() === \"udp\";\n const channelIdOverride =\n options?.channelIdOverride ?? (isUdp ? ch : undefined);\n\n const frame = await this.client.sendFrame({\n cmdId: BC_CMD_ID_TALK_RESET,\n channel: ch,\n ...(channelIdOverride != null ? { channelIdOverride } : {}),\n payloadXml: \"\",\n messageClass: BC_CLASS_MODERN_24,\n });\n\n if (frame.header.responseCode !== 200) {\n throw new Error(\n `TalkReset rejected (responseCode ${frame.header.responseCode})`,\n );\n }\n }\n\n /**\n * TalkConfig via Baichuan: cmd_id 201 (MSG_ID_TALKCONFIG).\n * Performs a TalkReset retry when the device responds with 422.\n */\n async talkConfig(\n payloadXml: string,\n channel = 0,\n options?: { channelIdOverride?: number },\n ): Promise<void> {\n await this.client.login();\n const ch = this.normalizeChannel(channel);\n\n const isUdp = this.client.getTransport?.() === \"udp\";\n const channelIdOverride =\n options?.channelIdOverride ?? (isUdp ? ch : undefined);\n\n const trySend = async (): Promise<number> => {\n const frame = await this.client.sendFrame({\n cmdId: BC_CMD_ID_TALK_CONFIG,\n channel: ch,\n ...(channelIdOverride != null ? { channelIdOverride } : {}),\n payloadXml,\n messageClass: BC_CLASS_MODERN_24,\n });\n return frame.header.responseCode;\n };\n\n const code = await trySend();\n if (code === 422) {\n await this.talkReset(\n ch,\n channelIdOverride != null ? { channelIdOverride } : undefined,\n );\n const retryCode = await trySend();\n if (retryCode !== 200) {\n throw new Error(\n `TalkConfig rejected after reset (responseCode ${retryCode})`,\n );\n }\n return;\n }\n\n if (code !== 200) {\n throw new Error(`TalkConfig rejected (responseCode ${code})`);\n }\n }\n\n /**\n * Create a talk (two-way audio) session.\n *\n * Input audio format expected by the camera is ADPCM (DVI4/IMA style) in blocks described\n * by TalkAbility.audioConfigList (typically 16kHz mono, lengthPerEncoder=1024).\n */\n async createTalkSession(\n channel = 0,\n options?: {\n blocksPerPayload?: number;\n /** Close the underlying socket when stop() completes (recommended for dedicated sessions). */\n closeSocketOnStop?: boolean;\n },\n ): Promise<TalkSession> {\n if (!this.client.loggedIn) await this.client.login();\n\n // BCUDP/battery firmwares often expect 0-based header channelId.\n // Talk is particularly sensitive because the binary Extension must be decrypted.\n const isUdp = this.client.getTransport?.() === \"udp\";\n const channelIdOverride = isUdp ? channel : undefined;\n\n const ability = await this.getTalkAbility(channel);\n const { payloadXml, info } = buildTalkSessionInfoFromAbility({\n channel,\n ability,\n });\n await sendTalkConfigWithReset({\n client: this.client,\n channel,\n payloadXml,\n ...(channelIdOverride != null ? { channelIdOverride } : {}),\n });\n\n return createBufferedTalkSession({\n client: this.client,\n channel,\n ...(channelIdOverride != null ? { channelIdOverride } : {}),\n info,\n ...(options?.blocksPerPayload != null\n ? { blocksPerPayload: options.blocksPerPayload }\n : {}),\n ...(options?.closeSocketOnStop != null\n ? { closeSocketOnStop: options.closeSocketOnStop }\n : {}),\n });\n }\n\n /**\n * Create a dedicated talk session with its own isolated socket connection.\n * This is the recommended way to use intercom - the library manages the socket lifecycle.\n *\n * The dedicated socket is automatically closed when:\n * 1. `stop()` is called on the returned session\n * 2. The idle timeout expires (no audio sent for `idleTimeoutMs`)\n * 3. The API is closed via `close()`\n *\n * @param channel - Channel number (usually 0)\n * @param options - Configuration options (blocksPerPayload, idleTimeoutMs, deviceId, logger)\n *\n * @example\n * ```typescript\n * const session = await api.createDedicatedTalkSession(0, {\n * blocksPerPayload: 2,\n * idleTimeoutMs: 30000,\n * deviceId: 'camera-123',\n * });\n * try {\n * await session.sendAudio(adpcmBuffer);\n * } finally {\n * await session.stop();\n * }\n * ```\n */\n async createDedicatedTalkSession(\n channel = 0,\n options?: {\n blocksPerPayload?: number;\n /** Auto-teardown if no audio sent for this duration (default 30000ms). Set to 0 to disable. */\n idleTimeoutMs?: number;\n /** Optional device identifier for logging/tracking */\n deviceId?: string;\n /** Optional logger for debug output */\n logger?: Logger;\n },\n ): Promise<TalkSession> {\n const logger = options?.logger ?? this.logger;\n const idleTimeoutMs = options?.idleTimeoutMs ?? 30000;\n const deviceId = options?.deviceId ?? \"unknown\";\n\n // Create a unique session key for this talk session\n const sessionKey = `talk:${deviceId}:ch${channel}:${Date.now()}`;\n\n logger?.info?.(\n `[DedicatedTalk] Creating session: ${sessionKey} (idleTimeout=${idleTimeoutMs}ms)`,\n );\n\n // Create dedicated socket session\n const { client: dedicatedClient, release } =\n await this.acquireDedicatedClient(sessionKey, logger);\n\n // Log sessions summary\n const summary = this.getDedicatedSessionsSummary();\n logger?.info?.(\n `[DedicatedTalk] Session created [sessions: ${summary.count} active${summary.count > 0 ? ` (${summary.keys.join(\", \")})` : \"\"}]`,\n );\n\n try {\n // BCUDP/battery firmwares often expect 0-based header channelId.\n const isUdp = dedicatedClient.getTransport?.() === \"udp\";\n const channelIdOverride = isUdp ? channel : undefined;\n\n // Get talk ability and build session info\n const ability = await this.getTalkAbilityWithClient(\n dedicatedClient,\n channel,\n );\n const { payloadXml, info } = buildTalkSessionInfoFromAbility({\n channel,\n ability,\n });\n\n // Send talk config\n await sendTalkConfigWithReset({\n client: dedicatedClient,\n channel,\n payloadXml,\n ...(channelIdOverride != null ? { channelIdOverride } : {}),\n });\n\n // Create the underlying talk session\n const innerSession = createBufferedTalkSession({\n client: dedicatedClient,\n channel,\n ...(channelIdOverride != null ? { channelIdOverride } : {}),\n info,\n ...(options?.blocksPerPayload != null\n ? { blocksPerPayload: options.blocksPerPayload }\n : {}),\n // Don't close socket on inner stop - we manage it here\n closeSocketOnStop: false,\n });\n\n // Idle timeout tracking\n let idleTimer: NodeJS.Timeout | undefined;\n let stopped = false;\n\n const resetIdleTimer = () => {\n if (idleTimer) clearTimeout(idleTimer);\n if (idleTimeoutMs > 0 && !stopped) {\n idleTimer = setTimeout(async () => {\n if (!stopped) {\n logger?.info?.(\n `[DedicatedTalk] Idle timeout (${idleTimeoutMs}ms), stopping session: ${sessionKey}`,\n );\n await wrappedStop();\n }\n }, idleTimeoutMs);\n }\n };\n\n const wrappedStop = async (): Promise<void> => {\n if (stopped) return;\n stopped = true;\n\n if (idleTimer) {\n clearTimeout(idleTimer);\n idleTimer = undefined;\n }\n\n try {\n await innerSession.stop();\n } catch (e) {\n logger?.debug?.(`[DedicatedTalk] Error stopping inner session: ${e}`);\n }\n\n // Release the dedicated socket\n try {\n await release();\n const summary = this.getDedicatedSessionsSummary();\n logger?.info?.(\n `[DedicatedTalk] Session released: ${sessionKey} [sessions: ${summary.count} active${summary.count > 0 ? ` (${summary.keys.join(\", \")})` : \"\"}]`,\n );\n } catch (e) {\n logger?.debug?.(`[DedicatedTalk] Error releasing session: ${e}`);\n }\n };\n\n // Start idle timer\n resetIdleTimer();\n\n return {\n info: innerSession.info,\n sendAudio: async (adpcm: Buffer) => {\n if (stopped) throw new Error(\"Talk session is closed\");\n resetIdleTimer();\n return await innerSession.sendAudio(adpcm);\n },\n stop: wrappedStop,\n };\n } catch (e) {\n // If setup fails, release the dedicated socket\n try {\n await release();\n } catch {\n // ignore\n }\n throw e;\n }\n }\n\n /**\n * Get talk ability using a specific client (for dedicated sessions).\n * @internal\n */\n private async getTalkAbilityWithClient(\n client: BaichuanClient,\n channel: number,\n ): Promise<TalkAbility> {\n const frame = await client.sendFrame({\n cmdId: BC_CMD_ID_TALK_ABILITY,\n channel,\n payloadXml: \"\",\n messageClass: BC_CLASS_MODERN_24,\n });\n // Decrypt and parse the XML response\n const xml =\n frame.body.length === 0\n ? \"\"\n : client.tryDecryptXml(frame.body, frame.header.channelId, client.enc);\n return parseTalkAbilityXml(xml);\n }\n\n /** Generic Baichuan cmd_id call, returns binary data (for commands like Snap). */\n async sendBinary(\n params: Parameters<BaichuanClient[\"sendBinary\"]>[0],\n ): Promise<Buffer> {\n await this.client.login();\n return await this.client.sendBinary(params);\n }\n\n // --------------------\n // Main operations\n // --------------------\n\n /** GetNetPort via Baichuan: cmd_id 37 */\n async getNetPort(): Promise<ReolinkBaichuanPorts> {\n const xml = await this.sendXml({ cmdId: 37 });\n // Parser minimale: estrae <RtspPort><enable>...</enable><port>...</port>...\n const ports: ReolinkBaichuanPorts = {};\n const protoBlocks = xml.matchAll(\n /<([A-Za-z]+)Port[^>]*>([\\s\\S]*?)<\\/\\1Port>/g,\n );\n for (const m of protoBlocks) {\n const proto = (m[1] ?? \"\").toLowerCase();\n const inner = m[2] ?? \"\";\n const kv: Record<string, number> = {};\n for (const kvp of inner.matchAll(/<([A-Za-z]+)>(-?\\d+)<\\/\\1>/g)) {\n const k = (kvp[1] ?? \"\").toLowerCase();\n const v = Number(kvp[2]);\n if (Number.isFinite(v)) kv[k] = v;\n }\n if (Object.keys(kv).length) ports[proto] = kv;\n }\n return ports;\n }\n\n /** Back-compat alias for older API name used by tests/consumers. */\n async getPorts(): Promise<ReolinkBaichuanPorts> {\n return this.getNetPort();\n }\n\n /** SetNetPort via Baichuan: cmd_id 36 (enable/disable rtsp/rtmp/onvif/http/https) */\n async setPortEnabled(params: {\n port: \"rtsp\" | \"rtmp\" | \"onvif\" | \"http\" | \"https\";\n enable: boolean;\n }): Promise<void> {\n const tag = `${params.port[0]!.toUpperCase()}${params.port.slice(1)}Port`;\n const xml =\n `<?xml version=\"1.0\" encoding=\"UTF-8\" ?>` +\n `<body>` +\n `<${tag} version=\"1.1\">` +\n `<enable>${params.enable ? 1 : 0}</enable>` +\n `</${tag}>` +\n `</body>`;\n await this.sendXml({ cmdId: 36, payloadXml: xml });\n }\n\n /** GetDevInfo via Baichuan: host cmd_id 80, channel cmd_id 318 */\n async getInfo(\n channel?: number,\n options?: {\n timeoutMs?: number;\n /** Optional message class override for older firmwares (e.g. 0x6614). */\n messageClass?: number;\n /** List of XML tags to extract. Defaults to the canonical minimal set. */\n tags?: ReolinkDeviceInfoTag[];\n },\n ): Promise<Partial<ReolinkDeviceInfo>> {\n const req: {\n cmdId: number;\n channel?: number;\n timeoutMs?: number;\n messageClass?: number;\n } = { cmdId: channel == null ? 80 : 318 };\n if (channel !== undefined) req.channel = channel;\n if (options?.timeoutMs != null) req.timeoutMs = options.timeoutMs;\n if (options?.messageClass != null) req.messageClass = options.messageClass;\n const xml = await this.sendXml(req);\n // Canonical minimal set: type, hardwareVersion, firmwareVersion, itemNo, serialNumber, name\n const tags = options?.tags?.length\n ? options.tags\n : [\n \"type\",\n \"hardwareVersion\",\n \"firmwareVersion\",\n \"itemNo\",\n \"serialNumber\",\n \"name\",\n ];\n return getXmlTexts(xml, tags);\n }\n\n /**\n * Parse and store channel info from cmd_id 145 push XML.\n * This is called automatically when the NVR sends channel info on connection.\n *\n * The XML structure is typically:\n * - <ChannelInfoList> with <ChannelInfo> blocks containing <channelId>, <devName>, <state>, <uid>, etc.\n * - <IOTInfoList> with <IOTInfo> blocks (for IoT devices)\n */\n private parseAndStoreChannelInfo(xml: string): void {\n const channelBlocks = getXmlBlocks(xml, \"ChannelInfo\");\n const iotBlocks = getXmlBlocks(xml, \"IOTInfo\");\n const allBlocks = [...channelBlocks, ...iotBlocks];\n\n this.logger.debug?.(\n `[ReolinkBaichuanApi] cmd_id 145 ChannelInfo push: blocks=${JSON.stringify(allBlocks)}`,\n );\n\n const entries = parseChannelInfoPushBlocks(allBlocks);\n const nowMs = Date.now();\n\n for (const entry of entries) {\n const existing = this.channelPushData.get(entry.channel);\n const { shouldSkip, next, events } = computeChannelPushUpdateFromEntry({\n entry,\n existing,\n nowMs,\n });\n if (shouldSkip || !next) continue;\n\n for (const evt of events) {\n this.dispatchSimpleEvent(evt);\n }\n this.channelPushData.set(entry.channel, next);\n }\n\n if (entries.length > 0) {\n const snap = buildChannelPushDataLogSnapshot(this.channelPushData);\n this.logger.debug?.(\n `[ReolinkBaichuanApi] Channel info received by the NVR: ${JSON.stringify(snap)}`,\n );\n }\n }\n\n /**\n * GetChannelInfo via Baichuan: cmd_id 318 (channel-specific DevInfo).\n *\n * This method extracts channel information similar to CGI GetChnTypeInfo,\n * but using the Baichuan protocol. It returns typeInfo (model), firmwareVersion,\n * and boardInfo if available in the XML response.\n */\n async getChannelInfo(\n channel: number,\n options?: {\n timeoutMs?: number;\n },\n ): Promise<ReolinkBaichuanChannelInfo> {\n const req: { cmdId: number; channel: number; timeoutMs?: number } = {\n cmdId: 318,\n channel,\n };\n if (options?.timeoutMs != null) req.timeoutMs = options.timeoutMs;\n const xml = await this.sendXml(req);\n\n // Extract fields similar to CgiChnTypeInfoValue\n // typeInfo can come from <type> tag in Baichuan response\n const typeInfo = getXmlText(xml, \"typeInfo\") ?? getXmlText(xml, \"type\");\n const firmVer =\n getXmlText(xml, \"firmVer\") ?? getXmlText(xml, \"firmwareVersion\");\n const firmwareVersion = firmVer || \"\";\n const boardInfo = getXmlText(xml, \"boardInfo\");\n const pakSuffix = getXmlText(xml, \"pakSuffix\");\n\n return {\n ...(typeInfo ? { typeInfo } : {}),\n ...(firmVer ? { firmVer, firmwareVersion } : {}),\n ...(boardInfo ? { boardInfo } : {}),\n ...(pakSuffix ? { pakSuffix } : {}),\n };\n }\n\n /**\n * GetAllChannelsInfo via Baichuan: cmd_id 145 (all channels info in a single request).\n *\n * Note: The NVR sends a message with cmd_id 145 when connecting, but it seems to not allow\n * requesting that id explicitly. This method will return an empty Map, and the caller should\n * fall back to per-channel requests using getChannelInfo (cmd_id 318).\n *\n * Returns a map of channel number to channel info (typically empty).\n */\n async getAllChannelsInfo(options?: {\n timeoutMs?: number;\n }): Promise<Map<number, ReolinkBaichuanChannelInfo>> {\n // Try with empty body XML first\n const req: { cmdId: number; payloadXml?: string; timeoutMs?: number } = {\n cmdId: BC_CMD_ID_CHANNEL_INFO_ALL,\n payloadXml: `<?xml version=\"1.0\" encoding=\"UTF-8\" ?><body></body>`,\n };\n if (options?.timeoutMs != null) req.timeoutMs = options.timeoutMs;\n\n let xml = await this.sendXml(req);\n\n // If empty response, try without body XML\n if (!xml || xml.trim().length === 0) {\n const reqNoBody: { cmdId: number; timeoutMs?: number } = {\n cmdId: BC_CMD_ID_CHANNEL_INFO_ALL,\n };\n if (options?.timeoutMs != null) reqNoBody.timeoutMs = options.timeoutMs;\n xml = await this.sendXml(reqNoBody);\n }\n\n // If still empty, the command is likely not supported (NVR sends it but doesn't allow requesting it)\n if (!xml || xml.trim().length === 0) {\n return new Map();\n }\n\n const result = new Map<number, ReolinkBaichuanChannelInfo>();\n\n // The response typically contains multiple channel blocks\n // Try common XML block patterns: <DevInfo>, <ChannelInfo>, <Channel>, etc.\n let channelBlocks = getXmlBlocks(xml, \"DevInfo\");\n if (channelBlocks.length === 0) {\n channelBlocks = getXmlBlocks(xml, \"ChannelInfo\");\n }\n if (channelBlocks.length === 0) {\n channelBlocks = getXmlBlocks(xml, \"Channel\");\n }\n if (channelBlocks.length === 0) {\n channelBlocks = getXmlBlocks(xml, \"channel\");\n }\n\n for (const block of channelBlocks) {\n // Try to extract channel number from various possible locations\n const channelText =\n getXmlText(block, \"channel\") ??\n getXmlText(block, \"channelId\") ??\n getXmlText(block, \"id\");\n const channel = channelText\n ? Number.parseInt(channelText, 10)\n : undefined;\n\n if (channel === undefined || !Number.isFinite(channel)) {\n // If no explicit channel number, try to infer from position or skip\n continue;\n }\n\n // Extract fields similar to getChannelInfo\n const typeInfo =\n getXmlText(block, \"typeInfo\") ?? getXmlText(block, \"type\");\n const firmVer =\n getXmlText(block, \"firmVer\") ?? getXmlText(block, \"firmwareVersion\");\n const firmwareVersion = firmVer || \"\";\n const boardInfo = getXmlText(block, \"boardInfo\");\n const pakSuffix = getXmlText(block, \"pakSuffix\");\n const name = getXmlText(block, \"name\");\n\n result.set(channel, {\n ...(typeInfo ? { typeInfo } : {}),\n ...(firmVer ? { firmVer, firmwareVersion } : {}),\n ...(boardInfo ? { boardInfo } : {}),\n ...(pakSuffix ? { pakSuffix } : {}),\n ...(name ? { name } : {}),\n });\n }\n\n // If no blocks found with channel numbers, try parsing the XML as a single response\n // and extract channel info from nested structures\n if (result.size === 0) {\n // Fallback: try to find channel info in a different structure\n // Some devices might return a flat structure with channel info embedded\n const allChannels = getXmlBlocks(xml, \"body\");\n for (const bodyBlock of allChannels) {\n // Look for channel-specific attributes or nested structures\n const channelText = getXmlText(bodyBlock, \"channel\");\n if (channelText) {\n const channel = Number.parseInt(channelText, 10);\n if (Number.isFinite(channel)) {\n const typeInfo =\n getXmlText(bodyBlock, \"typeInfo\") ??\n getXmlText(bodyBlock, \"type\");\n const firmVer =\n getXmlText(bodyBlock, \"firmVer\") ??\n getXmlText(bodyBlock, \"firmwareVersion\");\n const firmwareVersion = firmVer || \"\";\n const boardInfo = getXmlText(bodyBlock, \"boardInfo\");\n const pakSuffix = getXmlText(bodyBlock, \"pakSuffix\");\n const name = getXmlText(bodyBlock, \"name\");\n\n result.set(channel, {\n ...(typeInfo ? { typeInfo } : {}),\n ...(firmVer ? { firmVer, firmwareVersion } : {}),\n ...(boardInfo ? { boardInfo } : {}),\n ...(pakSuffix ? { pakSuffix } : {}),\n ...(name ? { name } : {}),\n });\n }\n }\n }\n }\n\n return result;\n }\n\n /**\n * Convenience helper to get a minimal per-channel identity tuple.\n *\n * Note: the Baichuan DevInfo payload uses <type> as the model string on most firmwares.\n */\n async getChannelIdentity(\n channel: number,\n options?: {\n timeoutMs?: number;\n },\n ): Promise<ReolinkBaichuanChannelIdentity> {\n const info = await this.getInfo(channel, {\n ...(options?.timeoutMs != null ? { timeoutMs: options.timeoutMs } : {}),\n tags: [\"type\", \"name\"],\n });\n return {\n channel,\n model: (info.type ?? \"\").trim(),\n name: (info.name ?? \"\").trim(),\n };\n }\n\n /**\n * Read-only snapshot of the cached channel info received via cmd_id 145 push.\n *\n * This cache is populated automatically when the NVR sends channel info on connection.\n */\n getChannelInfoFromPushCache(): Map<number, ChannelPushCacheEntry> {\n const out = new Map<number, ChannelPushCacheEntry>();\n for (const [channel, info] of this.channelPushData.entries()) {\n const stateLower = (info.stateLower ?? info.state).toLowerCase();\n if (stateLower === \"none\") continue;\n out.set(channel, {\n name: info.name,\n uid: info.uid,\n state: info.state,\n ...(typeof info.index === \"number\" ? { index: info.index } : {}),\n ...(info.streamSupport?.length\n ? { streamSupport: info.streamSupport }\n : {}),\n ...(info.wifiState ? { wifiState: info.wifiState } : {}),\n ...(info.networkSegment ? { networkSegment: info.networkSegment } : {}),\n ...(typeof info.changed === \"boolean\" ? { changed: info.changed } : {}),\n ...(typeof info.abilityChanged === \"boolean\"\n ? { abilityChanged: info.abilityChanged }\n : {}),\n ...(typeof info.online === \"boolean\" ? { online: info.online } : {}),\n ...(typeof info.sleeping === \"boolean\"\n ? { sleeping: info.sleeping }\n : {}),\n ...(info.loginState ? { loginState: info.loginState } : {}),\n ...(typeof info.updatedAtMs === \"number\"\n ? { updatedAtMs: info.updatedAtMs }\n : {}),\n });\n }\n return out;\n }\n\n /**\n * Read-only snapshot of the cached sleep state parsed from cmd_id 145 push.\n *\n * Values are boolean when known.\n */\n getChannelSleepFromPushCache(): Map<number, boolean> {\n const out = new Map<number, boolean>();\n for (const [channel, info] of this.channelPushData.entries()) {\n if (typeof info.sleeping === \"boolean\") out.set(channel, info.sleeping);\n }\n return out;\n }\n\n /**\n * Minimal per-channel inventory for NVR-connected devices.\n *\n * Intended to be fast: avoids AI/abilities and returns only the common identity + battery hints.\n */\n async getNvrChannelsSummary(options?: {\n channels?: number[];\n timeoutMs?: number;\n source?: \"baichuan\" | \"cgi\";\n }): Promise<NvrChannelsSummaryCacheEntry> {\n const source = options?.source ?? \"baichuan\";\n\n const pushInfo = this.getChannelInfoFromPushCache();\n const channels = (\n options?.channels?.length ? options.channels : Array.from(pushInfo.keys())\n )\n .map((c) => Number(c))\n .filter((n) => Number.isFinite(n))\n .sort((a, b) => a - b);\n\n const support = await this.getSupportInfo().catch(() => {\n this.logger.error?.(\n \"[ReolinkBaichuanApi] getNvrChannelsSummary: failed to get support info\",\n );\n });\n\n const truthyNumberLike = (v: unknown): boolean => {\n if (typeof v === \"number\") return v > 0;\n if (typeof v === \"string\") {\n const n = Number(v);\n if (Number.isFinite(n)) return n > 0;\n return v.length > 0 && v !== \"0\";\n }\n return Boolean(v);\n };\n\n const isBatteryByChannel = new Map<number, boolean>();\n const isDoorbellByChannel = new Map<number, boolean>();\n if (support) {\n for (const ch of channels) {\n const caps = computeDeviceCapabilities({ channel: ch, support });\n isBatteryByChannel.set(ch, Boolean(caps.hasBattery));\n const anySupportDoorbellLight = (support.items ?? []).some(\n (i) => i.chnID === ch && truthyNumberLike(i[\"supportDoorbellLight\"]),\n );\n isDoorbellByChannel.set(\n ch,\n Boolean(caps.isDoorbell) || anySupportDoorbellLight,\n );\n }\n }\n\n const cacheKey = `baichuan:${channels.join(\",\")}`;\n const cached = this.nvrChannelsSummaryCache.get(cacheKey);\n if (cached) {\n return {\n channels: [...cached.channels],\n devices: cached.devices.map((d) => ({ ...d })),\n };\n }\n\n const timeoutMs = options?.timeoutMs;\n const infoPerChannel = new Map<number, ReolinkDeviceInfo>();\n const networkInfoPerChannel = new Map<\n number,\n ReolinkBaichuanNetworkInfo | undefined\n >();\n for (const channel of channels) {\n try {\n const info = await this.getInfo(channel, {\n ...(timeoutMs != null ? { timeoutMs } : {}),\n tags: [\"type\", \"name\", \"serialNumber\"],\n });\n infoPerChannel.set(channel, info);\n\n // const net = await this.getNetworkInfo(channel, {\n // ...(timeoutMs != null ? { timeoutMs } : {}),\n // });\n // networkInfoPerChannel.set(channel, net);\n } catch {}\n }\n\n const devices = channels.map((channel) => {\n const cached = pushInfo.get(channel);\n const info = infoPerChannel.get(channel);\n const networkInfo = networkInfoPerChannel.get(channel);\n const isBattery = isBatteryByChannel.get(channel) ?? false;\n const model = info?.type ?? \"\";\n const isDoorbell =\n (isDoorbellByChannel.get(channel) ?? false) || /doorbell/i.test(model);\n\n const normalizedModel = model ? model.trim() : undefined;\n const isMultifocal = normalizedModel\n ? isDualLenseModel(normalizedModel)\n : false;\n\n return {\n channel,\n isBattery,\n isDoorbell,\n isMultifocal,\n model,\n ...(networkInfo?.ip ? { ip: networkInfo.ip } : {}),\n ...(networkInfo?.mac ? { mac: networkInfo.mac } : {}),\n ...(networkInfo?.activeLink\n ? { activeLink: networkInfo.activeLink }\n : {}),\n ...(cached?.name ? { name: cached.name } : {}),\n ...(cached?.uid ? { uid: cached.uid } : {}),\n ...(cached?.state ? { state: cached.state } : {}),\n ...(typeof cached?.index === \"number\" ? { index: cached.index } : {}),\n ...(cached?.streamSupport?.length\n ? { streamSupport: cached.streamSupport }\n : {}),\n ...(cached?.wifiState ? { wifiState: cached.wifiState } : {}),\n ...(cached?.networkSegment\n ? { networkSegment: cached.networkSegment }\n : {}),\n ...(typeof cached?.changed === \"boolean\"\n ? { changed: cached.changed }\n : {}),\n ...(typeof cached?.abilityChanged === \"boolean\"\n ? { abilityChanged: cached.abilityChanged }\n : {}),\n ...(typeof cached?.online === \"boolean\"\n ? { online: cached.online }\n : {}),\n ...(typeof cached?.sleeping === \"boolean\"\n ? { sleeping: cached.sleeping }\n : {}),\n ...(cached?.loginState ? { loginState: cached.loginState } : {}),\n ...(typeof cached?.updatedAtMs === \"number\"\n ? { updatedAtMs: cached.updatedAtMs }\n : {}),\n };\n });\n\n const result = { channels, devices };\n this.nvrChannelsSummaryCache.set(cacheKey, {\n channels: [...channels],\n devices: devices.map((d) => ({ ...d })),\n });\n return result;\n }\n\n /**\n * Group NVR/HUB channels by physical device (best-effort).\n *\n * Heuristics:\n * - Primary key: channel UID from cmd_id 145 push cache.\n * - Secondary key: per-channel serialNumber from getInfo(channel).\n * - Multifocal: group has 2+ channels OR model name matches known dual-lens patterns.\n */\n async getNvrDeviceGroups(options?: {\n channels?: number[];\n timeoutMs?: number;\n }): Promise<ReolinkNvrDeviceGroupsResult> {\n const { channels, devices } = await this.getNvrChannelsSummary(options);\n\n const looksLikeDualLensModel = (model?: string): boolean => {\n const m = (model ?? \"\").trim();\n if (!m) return false;\n if (DUAL_LENS_MODELS.has(m)) return true;\n const lower = m.toLowerCase();\n // Keyword heuristics (covers variations and suffixes)\n if (lower.includes(\"trackmix\")) return true;\n if (lower.includes(\"duo\")) return true;\n // Some firmwares report generic types; keep this conservative.\n return false;\n };\n\n type MutableGroup = Omit<\n ReolinkNvrDeviceGroupSummary,\n \"channels\" | \"isMultifocal\" | \"reason\"\n > & {\n channels: number[];\n modelSet: Set<string>;\n nameSet: Set<string>;\n };\n\n const groupsByKey = new Map<string, MutableGroup>();\n const serialToKey = new Map<string, string>();\n const channelToGroup: Record<number, string> = {};\n\n const getOrCreate = (key: string): MutableGroup => {\n let g = groupsByKey.get(key);\n if (!g) {\n g = { key, channels: [], modelSet: new Set(), nameSet: new Set() };\n groupsByKey.set(key, g);\n }\n return g;\n };\n\n for (const d of devices) {\n const uid = (d.uid ?? \"\").trim() || undefined;\n const serial = (d.serialNumber ?? \"\").trim() || undefined;\n const model = (d.model ?? \"\").trim() || undefined;\n const name = (d.name ?? \"\").trim() || undefined;\n\n // Prefer grouping by UID, but allow serial to merge channels when UID is missing.\n let key = uid\n ? `uid:${uid}`\n : serial\n ? `sn:${serial}`\n : `ch:${d.channel}`;\n if (!uid && serial) {\n const existing = serialToKey.get(serial);\n if (existing) key = existing;\n }\n\n const g = getOrCreate(key);\n if (!g.channels.includes(d.channel)) g.channels.push(d.channel);\n if (!g.uid && uid) g.uid = uid;\n if (!g.serialNumber && serial) g.serialNumber = serial;\n if (model) g.modelSet.add(model);\n if (name) g.nameSet.add(name);\n\n // If we have a serial and this group is a UID-group, remember it for later merges.\n if (serial) serialToKey.set(serial, key);\n channelToGroup[d.channel] = key;\n }\n\n const finalizeModel = (g: MutableGroup): string | undefined => {\n if (g.modelSet.size === 1) return Array.from(g.modelSet)[0];\n // Prefer any model that looks like dual lens.\n for (const m of g.modelSet) {\n if (looksLikeDualLensModel(m)) return m;\n }\n return g.modelSet.size ? Array.from(g.modelSet)[0] : undefined;\n };\n\n const finalizeName = (g: MutableGroup): string | undefined => {\n if (g.nameSet.size === 1) return Array.from(g.nameSet)[0];\n return g.nameSet.size ? Array.from(g.nameSet)[0] : undefined;\n };\n\n const groups: ReolinkNvrDeviceGroupSummary[] = Array.from(\n groupsByKey.values(),\n )\n .map((g) => {\n g.channels.sort((a, b) => a - b);\n const name = finalizeName(g);\n const model = finalizeModel(g);\n const isMultifocal =\n g.channels.length > 1 || looksLikeDualLensModel(model);\n const reason =\n g.channels.length > 1\n ? `shared ${g.uid ? \"uid\" : g.serialNumber ? \"serial\" : \"identity\"} across ${g.channels.length} channels`\n : looksLikeDualLensModel(model)\n ? \"model match (dual-lens keyword)\"\n : \"single-channel device\";\n return {\n key: g.key,\n ...(g.uid ? { uid: g.uid } : {}),\n ...(g.serialNumber ? { serialNumber: g.serialNumber } : {}),\n ...(name ? { name } : {}),\n ...(model ? { model } : {}),\n channels: g.channels,\n isMultifocal,\n reason,\n };\n })\n .sort((a, b) => (a.channels[0] ?? 0) - (b.channels[0] ?? 0));\n\n return { channels, groups, channelToGroup };\n }\n\n /** GetEnc via Baichuan: cmd_id 56 (returns raw XML). */\n async getEncXml(\n channel?: number,\n options?: { timeoutMs?: number },\n ): Promise<string> {\n const ch = this.normalizeChannel(channel);\n return await this.sendXml({\n cmdId: 56,\n channel: ch,\n ...(options?.timeoutMs != null ? { timeoutMs: options.timeoutMs } : {}),\n });\n }\n\n /**\n * GetStreamMetadata via Baichuan: cmd_id 56 (GetEnc).\n * Returns metadata for all available streams (main, sub, ext) including:\n * - Video codec (H.264, H.265, etc.)\n * - Resolution (width x height)\n * - Frame rate (FPS)\n * - Bitrate\n * - Audio enabled\n *\n * Note on extStream:\n * - extStream is only present in the XML response if it's enabled/supported by the camera\n * - If extStream is not present in the XML, it means it's not available for that channel\n * - extStream availability is determined by the camera firmware/model capabilities\n * - There's no explicit \"enable\" field for extStream in the GetEnc response\n * - extStream is typically available on newer Reolink models that support multiple stream profiles\n */\n async getStreamMetadata(channel?: number): Promise<ChannelStreamMetadata> {\n const ch = this.normalizeChannel(channel);\n const xml = await this.getEncXml(ch);\n const dbg = this.client.getDebugConfig?.();\n return parseChannelStreamMetadataFromGetEncXml({\n channel: ch,\n xml,\n logger: this.logger,\n traceNativeStream: dbg?.traceNativeStream === true,\n });\n }\n\n /** SetEnc via Baichuan: cmd_id 57 (sends raw XML). */\n async setEncXml(encXml: string): Promise<void>;\n async setEncXml(channel: number, encXml: string): Promise<void>;\n async setEncXml(\n channelOrEncXml: number | string,\n encXmlMaybe?: string,\n ): Promise<void> {\n const ch =\n typeof channelOrEncXml === \"number\"\n ? this.normalizeChannel(channelOrEncXml)\n : 0;\n const encXml =\n typeof channelOrEncXml === \"number\" ? encXmlMaybe! : channelOrEncXml;\n await this.sendXml({ cmdId: 57, channel: ch, payloadXml: encXml });\n }\n\n /**\n * Update the encoder codec for a given stream profile (main/sub/ext).\n *\n * NOTE: This changes the camera configuration.\n * Many models may require a short delay (or stream restart) before the new codec is used.\n */\n async setStreamVideoCodec(\n profile: StreamProfile,\n codec: \"H.264\" | \"H.265\",\n channel?: number,\n ): Promise<void>;\n async setStreamVideoCodec(\n channel: number,\n profile: StreamProfile,\n codec: \"H.264\" | \"H.265\",\n ): Promise<void>;\n async setStreamVideoCodec(\n channelOrProfile: number | StreamProfile,\n profileOrCodec: StreamProfile | (\"H.264\" | \"H.265\"),\n codecOrChannel?: (\"H.264\" | \"H.265\") | number,\n ): Promise<void> {\n const ch =\n typeof channelOrProfile === \"number\"\n ? this.normalizeChannel(channelOrProfile)\n : this.normalizeChannel(codecOrChannel as number | undefined);\n const profile =\n typeof channelOrProfile === \"number\"\n ? (profileOrCodec as StreamProfile)\n : channelOrProfile;\n const codec =\n typeof channelOrProfile === \"number\"\n ? (codecOrChannel as \"H.264\" | \"H.265\")\n : (profileOrCodec as \"H.264\" | \"H.265\");\n const desired = codec === \"H.265\" ? 1 : 0;\n\n const candidateTags =\n profile === \"main\"\n ? [\"mainStream\"]\n : profile === \"sub\"\n ? [\"subStream\"]\n : [\"extStream\", \"thirdStream\", \"externStream\", \"extraStream\"];\n\n const current = await this.getEncXml(ch);\n\n let updated: string | null = null;\n for (const tag of candidateTags) {\n const sectionRe = new RegExp(\n `(<${tag}[^>]*>[\\\\s\\\\S]*?<videoEncType>)(\\\\d+)(</videoEncType>)`,\n );\n if (!sectionRe.test(current)) continue;\n const next = current.replace(sectionRe, `$1${desired}$3`);\n if (next !== current) {\n updated = next;\n break;\n }\n }\n\n if (!updated) {\n throw new Error(\n `Could not find <videoEncType> for profile=${profile} (tags=${candidateTags.join(\",\")}) in GetEnc XML (channel=${ch}).`,\n );\n }\n\n await this.setEncXml(ch, updated);\n }\n\n /** Bulk SetNetPort helper: accepts NetPort with onvifEnable/rtmpEnable/rtspEnable. */\n async setNetPort(netPort: {\n onvifEnable?: number;\n rtmpEnable?: number;\n rtspEnable?: number;\n }): Promise<void> {\n if (netPort.onvifEnable != null)\n await this.setPortEnabled({\n port: \"onvif\",\n enable: netPort.onvifEnable === 1,\n });\n if (netPort.rtmpEnable != null)\n await this.setPortEnabled({\n port: \"rtmp\",\n enable: netPort.rtmpEnable === 1,\n });\n if (netPort.rtspEnable != null)\n await this.setPortEnabled({\n port: \"rtsp\",\n enable: netPort.rtspEnable === 1,\n });\n }\n\n /** Reboot via Baichuan: cmd_id 23 */\n async reboot(channel?: number): Promise<void> {\n const req: { cmdId: number; channel?: number } = { cmdId: 23 };\n if (channel !== undefined) req.channel = channel;\n await this.sendXml(req);\n }\n\n /** Ping via Baichuan: cmd_id 93 (header-only / no payload) */\n async ping(): Promise<void> {\n await this.sendXml({ cmdId: BC_CMD_ID_PING });\n }\n\n /**\n * Best-effort network info via Baichuan.\n *\n * Behavior aligned with common Reolink firmwares:\n * - cmd_id 76 often returns <ip>/<mac> (especially on NVR/Hub)\n * - when querying host (no channel), some firmwares only populate link details after a cmd_id 93 ping\n * - fallback to cmd_id 104 (GetGeneralXml) when 76 is unsupported/empty\n */\n async getNetworkInfo(\n channel?: number,\n options?: {\n timeoutMs?: number;\n },\n ): Promise<ReolinkBaichuanNetworkInfo | undefined> {\n const timeoutMs = options?.timeoutMs ?? 1200;\n\n const trim = (v: unknown): string | undefined => {\n if (typeof v !== \"string\") return undefined;\n const t = v.trim();\n return t ? t : undefined;\n };\n\n const parse = (xml?: string): ReolinkBaichuanNetworkInfo | undefined => {\n if (!xml) return undefined;\n const ip =\n trim(getXmlText(xml, \"ip\")) ||\n trim(getXmlText(xml, \"IP\")) ||\n trim(getXmlText(xml, \"ipv4\")) ||\n trim(getXmlText(xml, \"IPv4\")) ||\n undefined;\n const mac =\n trim(getXmlText(xml, \"mac\")) ||\n trim(getXmlText(xml, \"MAC\")) ||\n undefined;\n const activeLink =\n trim(getXmlText(xml, \"activeLink\")) ||\n trim(getXmlText(xml, \"type\")) ||\n trim(getXmlText(xml, \"linkType\")) ||\n undefined;\n return ip || mac || activeLink\n ? {\n ...(ip ? { ip } : {}),\n ...(mac ? { mac } : {}),\n ...(activeLink ? { activeLink } : {}),\n }\n : undefined;\n };\n\n const merge = (\n a?: ReolinkBaichuanNetworkInfo,\n b?: ReolinkBaichuanNetworkInfo,\n ): ReolinkBaichuanNetworkInfo | undefined => {\n if (!a && !b) return undefined;\n return {\n ...(a?.ip ? { ip: a.ip } : b?.ip ? { ip: b.ip } : {}),\n ...(a?.mac ? { mac: a.mac } : b?.mac ? { mac: b.mac } : {}),\n ...(a?.activeLink\n ? { activeLink: a.activeLink }\n : b?.activeLink\n ? { activeLink: b.activeLink }\n : {}),\n };\n };\n\n if (channel === undefined) {\n // Host-level: cmd 76 then cmd 93 ping (some firmwares only fill link details after ping).\n let xml76: string | undefined;\n let xml93: string | undefined;\n\n try {\n xml76 = await this.sendXml({ cmdId: 76, timeoutMs });\n } catch {\n // ignore\n }\n\n try {\n xml93 = await this.sendXml({ cmdId: 93, timeoutMs });\n } catch {\n // ignore\n }\n\n const merged = merge(parse(xml76), parse(xml93));\n if (merged) return merged;\n\n try {\n const xml = await this.getGeneralXml();\n return parse(xml);\n } catch {\n return undefined;\n }\n }\n\n const ch = this.normalizeChannel(channel);\n try {\n const xml = await this.sendXml({ cmdId: 76, channel: ch, timeoutMs });\n const parsed = parse(xml);\n if (parsed) return parsed;\n } catch {\n // ignore\n }\n\n try {\n const xml = await this.getGeneralXml(ch);\n return parse(xml);\n } catch {\n return undefined;\n }\n }\n\n /** GetLocalLink via Baichuan: cmd_id 104 (general info) - on many models includes MAC/network info. */\n async getGeneralXml(channel?: number): Promise<string> {\n const req: { cmdId: number; channel?: number } = { cmdId: 104 };\n if (channel !== undefined) req.channel = channel;\n return await this.sendXml(req);\n }\n\n /** SetGeneralXml via Baichuan: cmd_id 105 */\n async setGeneralXml(xml: string): Promise<void>;\n async setGeneralXml(channel: number | undefined, xml: string): Promise<void>;\n async setGeneralXml(\n channelOrXml: number | string | undefined,\n xmlMaybe?: string,\n ): Promise<void> {\n const channel =\n typeof channelOrXml === \"number\" || channelOrXml === undefined\n ? channelOrXml\n : undefined;\n const xml = typeof channelOrXml === \"string\" ? channelOrXml : xmlMaybe!;\n await this.sendXml({\n cmdId: 105,\n ...(channel === undefined ? {} : { channel }),\n payloadXml: xml,\n });\n }\n\n /** Helper to build a channel Extension XML (for payloads that require it). */\n static buildChannelExtensionXml(channel: number): string {\n return (\n `<?xml version=\"1.0\" encoding=\"UTF-8\" ?>` +\n `<Extension version=\"1.1\"><channelId>${xmlEscape(String(channel))}</channelId></Extension>`\n );\n }\n\n // --------------------\n // New API implementations (cmd_id to be identified/tested)\n // --------------------\n\n /**\n * GetMotionState via Baichuan.\n * cmd_id: 46 (GetMdAlarm)\n * Returns true if motion detection is enabled.\n */\n async getMotionState(channel?: number): Promise<boolean> {\n const xml = await this.sendXml({\n cmdId: BC_CMD_ID_GET_MOTION_ALARM,\n ...(channel !== undefined ? { channel } : {}),\n });\n // Parse XML to extract motion state from sensInfoNew\n // Expected format: <sensInfoNew><enable>1</enable>...</sensInfoNew>\n const enable = getXmlText(xml, \"enable\");\n return enable === \"1\" || enable === \"true\";\n }\n\n /**\n * GetMdAlarm via Baichuan.\n * cmd_id: 46 (GetMdAlarm)\n * Returns the response parsed as JSON (no XML exposure).\n */\n async getMotionAlarm(\n channel?: number,\n options?: { timeoutMs?: number },\n ): Promise<MotionAlarmConfig> {\n const xml = await this.sendXml({\n cmdId: BC_CMD_ID_GET_MOTION_ALARM,\n ...(channel !== undefined ? { channel } : {}),\n ...(options?.timeoutMs != null ? { timeoutMs: options.timeoutMs } : {}),\n });\n return parseXmlFragmentToJson<MotionAlarmConfig>(xml);\n }\n\n /**\n * SetMdAlarm via Baichuan.\n * cmd_id: 47 (SetMdAlarm)\n * Alias of `setMotionDetection()`.\n */\n async setMotionAlarm(\n enabled: boolean,\n sensitivity?: number,\n channel?: number,\n ): Promise<void>;\n async setMotionAlarm(\n channel: number,\n enabled: boolean,\n sensitivity?: number,\n ): Promise<void>;\n async setMotionAlarm(\n arg1: number | boolean,\n arg2?: boolean | number,\n arg3?: number,\n ): Promise<void> {\n // Delegate to preserve the existing logic (read-modify-write using GetMdAlarm + SetMdAlarm).\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return await (this.setMotionDetection as any)(\n arg1 as any,\n arg2 as any,\n arg3 as any,\n );\n }\n\n /**\n * GetOsd via Baichuan.\n * cmd_id: 26 (GetImage - includes OSD settings)\n */\n async getOsd(channel?: number): Promise<OsdConfig> {\n const ch = this.normalizeChannel(channel);\n const cmdId = 26; // GetImage (includes OSD)\n const xml = await this.sendXml({ cmdId, channel: ch });\n // Parse OSD XML structure from VideoInput/OsdChannel and OsdTime\n // This is a placeholder - actual parsing depends on XML structure\n return {\n channel: ch,\n osdChannel: {\n enable: Number(getXmlText(xml, \"enable\") ?? \"0\"),\n name: getXmlText(xml, \"name\") ?? \"\",\n pos: getXmlText(xml, \"pos\") ?? \"\",\n },\n osdTime: {\n enable: Number(getXmlText(xml, \"timeEnable\") ?? \"0\"),\n pos: getXmlText(xml, \"timePos\") ?? \"\",\n },\n watermark: Number(getXmlText(xml, \"watermark\") ?? \"0\"),\n };\n }\n\n /**\n * SetOsd via Baichuan.\n * cmd_id: 25 (SetImage - includes OSD settings)\n */\n async setOsd(osd: OsdConfig, channel?: number): Promise<void>;\n async setOsd(channel: number, osd: OsdConfig): Promise<void>;\n async setOsd(\n channelOrOsd: number | OsdConfig,\n osdMaybe?: OsdConfig | number,\n ): Promise<void> {\n const ch =\n typeof channelOrOsd === \"number\"\n ? this.normalizeChannel(channelOrOsd)\n : this.normalizeChannel(osdMaybe as number | undefined);\n const osd =\n typeof channelOrOsd === \"number\" ? (osdMaybe as OsdConfig) : channelOrOsd;\n const cmdId = 25; // SetImage (includes OSD)\n const xml =\n `<?xml version=\"1.0\" encoding=\"UTF-8\" ?>` +\n `<body>` +\n `<Osd version=\"1.1\">` +\n `<channel>${ch}</channel>` +\n `<osdChannel>` +\n `<enable>${osd.osdChannel.enable}</enable>` +\n `<name>${xmlEscape(osd.osdChannel.name)}</name>` +\n `<pos>${xmlEscape(osd.osdChannel.pos)}</pos>` +\n `</osdChannel>` +\n `<osdTime>` +\n `<enable>${osd.osdTime.enable}</enable>` +\n `<pos>${xmlEscape(osd.osdTime.pos)}</pos>` +\n `</osdTime>` +\n `<watermark>${osd.watermark}</watermark>` +\n `</Osd>` +\n `</body>`;\n await this.sendXml({ cmdId, channel: ch, payloadXml: xml });\n }\n\n /**\n * GetAiState via Baichuan.\n * cmd_id: 342 (GetAiAlarm)\n * Note: GetAiAlarm requires ai_type parameter, this is a simplified wrapper\n */\n async getAiState(channel?: number): Promise<AIState> {\n const ch = this.normalizeChannel(channel);\n const candidateTypes = await this.getAiAlarmCandidateTypes(ch);\n return await getAiStateViaGetAiAlarm({\n sendXml: (p, retry) => this.sendXml(p, retry),\n channel: ch,\n ...(candidateTypes && candidateTypes.length > 0\n ? { candidateTypes }\n : {}),\n });\n }\n\n /** Alias for `getAiState()` (cmd_id 342, GetAiAlarm). */\n async getAiAlarm(channel?: number): Promise<AIState> {\n return await this.getAiState(channel);\n }\n\n /**\n * Raw GetAiAlarm (cmd_id 342) returning parsed JSON.\n * Useful to inspect full payload without exposing XML.\n */\n async getAiAlarmRaw(\n channel: number,\n aiType: string,\n options?: { timeoutMs?: number; channelIdOverride?: number },\n ): Promise<AiAlarmConfig> {\n const ch = this.normalizeChannel(channel);\n const payloadXml =\n `<?xml version=\"1.0\" encoding=\"UTF-8\" ?>` +\n `<body>` +\n `<AiDetectCfg version=\"1.1\">` +\n `<chn>${ch}</chn>` +\n `<type>${xmlEscape(aiType)}</type>` +\n `</AiDetectCfg>` +\n `</body>`;\n\n const xml = await this.sendXml(\n {\n cmdId: BC_CMD_ID_GET_AI_ALARM,\n channel: ch,\n payloadXml,\n ...(options?.timeoutMs != null ? { timeoutMs: options.timeoutMs } : {}),\n ...(options?.channelIdOverride != null\n ? { channelIdOverride: options.channelIdOverride }\n : {}),\n },\n 0,\n );\n\n return parseXmlFragmentToJson<AiAlarmConfig>(xml);\n }\n\n private normalizeAiDetectTypeForGetAiAlarm(type: string): string[] {\n const raw = (type ?? \"\").trim();\n if (!raw) return [];\n\n const t = raw.toLowerCase();\n if (!t || t === \"none\") return [];\n\n // cmd 299 (<detectType>) and cmd 342 (<type>) are not always identical across firmwares.\n // Keep a small, safe normalization map and still try the original token.\n const canonicalMap: Record<string, string> = {\n person: \"people\",\n people: \"people\",\n car: \"vehicle\",\n vehicle: \"vehicle\",\n pet: \"dog_cat\",\n animal: \"dog_cat\",\n dog_cat: \"dog_cat\",\n face: \"face\",\n package: \"package\",\n };\n\n const canonical = canonicalMap[t] ?? t;\n if (canonical === t) return [canonical];\n return [canonical, t];\n }\n\n private async getAiDetectTypesCached(\n channel: number,\n ): Promise<string[] | undefined> {\n const now = Date.now();\n const cached = this.aiDetectTypesCache.get(channel);\n // Avoid hammering cmd 299; cache both success and \"unknown\" briefly.\n if (cached && now - cached.updatedAtMs < 5 * 60_000) {\n return cached.types;\n }\n\n const detectTypes = await this.getAiDetectTypes(channel, {\n timeoutMs: 1500,\n });\n this.aiDetectTypesCache.set(\n channel,\n detectTypes != null\n ? { types: detectTypes, updatedAtMs: now }\n : { updatedAtMs: now },\n );\n\n return detectTypes;\n }\n\n private async resolveAiTypeForSetAiDetection(\n channel: number,\n requestedAiType: string,\n ): Promise<string> {\n const req = (requestedAiType ?? \"\").trim();\n if (!req) return \"people\";\n\n const requestedCandidates = this.normalizeAiDetectTypeForGetAiAlarm(req);\n const detectTypes = await this.getAiDetectTypesCached(channel);\n\n if (detectTypes && detectTypes.length > 0) {\n const supported = new Set(\n detectTypes\n .flatMap((t) => this.normalizeAiDetectTypeForGetAiAlarm(t))\n .map((t) => t.toLowerCase()),\n );\n\n for (const c of requestedCandidates) {\n if (supported.has(c.toLowerCase())) return c;\n }\n }\n\n // Fall back to canonical requested candidate (still better than raw).\n return requestedCandidates[0] ?? req;\n }\n\n private async getAiAlarmCandidateTypes(\n channel: number,\n ): Promise<string[] | undefined> {\n const now = Date.now();\n const cached = this.aiAlarmCandidateTypesCache.get(channel);\n // Avoid hammering during polling; cache both success and \"unknown\" briefly.\n if (cached && now - cached.updatedAtMs < 5 * 60_000) {\n return cached.types;\n }\n\n const detectTypes = await this.getAiDetectTypesCached(channel);\n const fromDetectTypes = (detectTypes ?? []).flatMap((t) =>\n this.normalizeAiDetectTypeForGetAiAlarm(t),\n );\n\n // Always keep a conservative fallback list at the end.\n const fallback = [\"people\", \"vehicle\", \"dog_cat\", \"face\", \"package\"];\n const all = [...fromDetectTypes, ...fallback]\n .map((s) => s.trim())\n .filter(Boolean);\n\n const deduped: string[] = [];\n const seen = new Set<string>();\n for (const t of all) {\n const key = t.toLowerCase();\n if (seen.has(key)) continue;\n seen.add(key);\n deduped.push(t);\n }\n\n const types = deduped.length > 0 ? deduped : undefined;\n this.aiAlarmCandidateTypesCache.set(\n channel,\n types != null ? { types, updatedAtMs: now } : { updatedAtMs: now },\n );\n return types;\n }\n\n /**\n * GetSnapshot via Baichuan (binary response).\n * cmd_id: 109 (snapshot)\n * Returns JPEG image as Buffer.\n * Note: Snapshot uses a special message ID system for binary responses\n */\n async getSnapshot(\n channel?: number,\n options?: {\n /** Native variant for dual-lens models (default=wide; autotrack/telephoto=tele lens). */\n variant?: NativeVideoStreamVariant;\n /** True when behind NVR/Hub (tele lens is exposed via logic-channel/variant). */\n onNvr?: boolean;\n /** Composite snapshot options for multifocal cameras (PiP). Used when channel is undefined. */\n compositeOptions?: CompositeStreamPipOptions;\n /** Snapshot stream type (quality). Default: \"main\". */\n streamType?: \"main\" | \"sub\";\n timeoutMs?: number;\n },\n ): Promise<Buffer> {\n const cmdId = 109;\n\n // Composite snapshot for multifocal devices: channel=undefined indicates \"composite\".\n // The plugin passes PiP config via compositeOptions.\n if (channel === undefined) {\n const composite = options?.compositeOptions;\n const widerChannel = composite?.widerChannel ?? 0;\n const teleChannel = composite?.teleChannel ?? 1;\n const pipPosition = composite?.pipPosition ?? \"bottom-right\";\n const pipSizeRaw = Number(composite?.pipSize ?? 0.25);\n const pipSize = Math.min(\n 0.9,\n Math.max(0.05, Number.isFinite(pipSizeRaw) ? pipSizeRaw : 0.25),\n );\n const onNvr = options?.onNvr === true || composite?.onNvr === true;\n const streamType: \"main\" | \"sub\" = options?.streamType ?? \"main\";\n const timeoutMs = options?.timeoutMs ?? 15_000;\n\n // Wide snapshot (always default lens).\n const wide = await this.getSnapshot(widerChannel, {\n onNvr,\n variant: \"default\",\n streamType,\n timeoutMs,\n });\n\n // Tele snapshot:\n // - direct device: tele is often exposed as a separate channel\n // - NVR/Hub TrackMix: tele is NOT a separate channel; it is selected via variant/logicChannel\n const teleChannelEffective = onNvr ? widerChannel : teleChannel;\n const tele = await this.getSnapshot(teleChannelEffective, {\n onNvr,\n variant: onNvr ? \"telephoto\" : \"default\",\n streamType,\n timeoutMs,\n });\n\n let wideImg: Awaited<ReturnType<typeof Jimp.read>>;\n let teleImg: Awaited<ReturnType<typeof Jimp.read>>;\n try {\n wideImg = await Jimp.read(wide);\n } catch {\n // If we can't determine dimensions, return wide snapshot.\n return wide;\n }\n\n try {\n teleImg = await Jimp.read(tele);\n } catch {\n // If tele cannot be decoded, fall back to wide.\n return wide;\n }\n\n const mainW = wideImg.bitmap.width;\n const mainH = wideImg.bitmap.height;\n const teleW = teleImg.bitmap.width;\n const teleH = teleImg.bitmap.height;\n\n const pipMarginPx = resolvePipMarginPx(\n mainW,\n mainH,\n composite?.pipMargin,\n 10,\n );\n\n const teleAspect = teleW > 0 && teleH > 0 ? teleW / teleH : 16 / 9;\n\n // PIP size is defined as a fraction of the OUTPUT (wide) snapshot.\n let pipW = Math.max(1, Math.floor(mainW * pipSize));\n let pipH = Math.max(1, Math.floor(pipW / teleAspect));\n\n // Constrain by height too (keeps consistent visual size).\n const maxPipHeight = Math.max(1, Math.floor(mainH * pipSize));\n if (pipH > maxPipHeight) {\n pipH = maxPipHeight;\n pipW = Math.max(1, Math.floor(pipH * teleAspect));\n }\n\n // Clamp to image bounds.\n pipW = Math.min(pipW, mainW);\n pipH = Math.min(pipH, mainH);\n\n const { left, top } = calculatePipOverlayPosition({\n position: pipPosition,\n mainWidth: mainW,\n mainHeight: mainH,\n pipWidth: pipW,\n pipHeight: pipH,\n margin: pipMarginPx,\n });\n\n // Resize to exact PiP bounds (we already computed aspect-aware sizes).\n teleImg.resize({ w: pipW, h: pipH });\n\n wideImg.composite(teleImg, left, top);\n return await wideImg.getBuffer(JimpMime.jpeg, { quality: 80 });\n }\n\n // Regular snapshot for single channel\n const ch = channel !== undefined ? this.normalizeChannel(channel) : 0;\n const variant: NativeVideoStreamVariant = options?.variant ?? \"default\";\n const onNvr = options?.onNvr === true;\n const streamType: \"main\" | \"sub\" = options?.streamType ?? \"main\";\n const timeoutMs = options?.timeoutMs ?? 15_000;\n\n // On NVR/Hub firmwares, cmd_id=109 is annoyingly inconsistent:\n // - some accept header channelId as the NVR channel (0-based, PCAP-observed)\n // - others only accept a fixed \"master\" channelId (often 0), and use the XML <channelId> to select the camera\n // - logicChannel meaning varies (sometimes lens index, sometimes the NVR channel itself)\n //\n // To keep snapshots working for channel>1, we try a small prioritized set of combinations on NVR.\n const buildSnapXml = (params: {\n channelIdTag: number;\n logicChannel: number;\n }) =>\n `<body><Snap version=\"1.1\"><channelId>${params.channelIdTag}</channelId><logicChannel>${params.logicChannel}</logicChannel><time>0</time><fullFrame>0</fullFrame><streamType>${streamType}</streamType></Snap></body>`;\n\n await this.client.login();\n\n // IMPORTANT: the Snap request Extension must NOT include <binaryData>1</binaryData>.\n // The binary chunks in response will have <binaryData>1</binaryData> in their Extension.\n // Delegate to the client binary handler. cmdId=109 (snapshot) is special and is delivered via push frames.\n //\n // NVR/Hub firmware differences:\n // - some expect channelId tags/header channelId to be 0-based (PCAP-observed)\n // - others expect 1-based. Try both when onNvr.\n if (onNvr) {\n const channelIdTagCandidates = [ch, ch + 1];\n const logicChannelCandidates =\n variant === \"default\"\n ? [ch, 0] // wide: some firmwares want logicChannel == camera channel\n : [1]; // tele/autotrack: generally logicChannel=1\n\n // Try header overrides in priority order. Many NVRs accept only header channelId=0 for snapshots.\n const headerChannelIdOverrideCandidates: Array<number | undefined> = [\n 0,\n ch,\n undefined,\n ];\n\n let lastErr: unknown;\n for (const headerChannelIdOverride of headerChannelIdOverrideCandidates) {\n for (const channelIdTag of channelIdTagCandidates) {\n for (const lc of logicChannelCandidates) {\n try {\n return await this.client.sendBinary({\n cmdId,\n channel: ch,\n ...(headerChannelIdOverride !== undefined\n ? { channelIdOverride: headerChannelIdOverride }\n : {}),\n payloadXml: buildSnapXml({ channelIdTag, logicChannel: lc }),\n extensionXml: buildChannelExtensionXml(channelIdTag),\n timeoutMs,\n });\n } catch (e) {\n lastErr = e;\n }\n }\n }\n }\n\n throw lastErr instanceof Error\n ? lastErr\n : new Error(String(lastErr ?? \"getSnapshot failed\"));\n }\n\n return await this.client.sendBinary({\n cmdId,\n channel: ch,\n payloadXml: buildSnapXml({ channelIdTag: ch, logicChannel: ch }),\n extensionXml: buildChannelExtensionXml(ch),\n timeoutMs,\n });\n }\n\n /**\n * Get videoclips via Baichuan FileInfoList.\n *\n * This unified method works for both NVR and standalone cameras.\n * - For NVR: channel is required, UID is obtained from push cache\n * - For standalone cameras: channel defaults to 0, UID is discovered via getInfo()\n *\n * The method automatically detects whether a channel-specific UID is available\n * (NVR mode) or falls back to device-level UID discovery (standalone mode).\n *\n * Flow:\n * - cmdId=14: open search -> returns <handle>\n * - cmdId=15: get pages -> returns file list and optional <bFinished>\n * - cmdId=16: close handle\n *\n * @example\n * ```ts\n * // NVR usage (channel required)\n * const clips = await api.getVideoclips({\n * channel: 0,\n * start: new Date(Date.now() - 24 * 60 * 60 * 1000),\n * end: new Date(),\n * });\n *\n * // Standalone camera (channel optional, defaults to 0)\n * const clips = await api.getVideoclips({\n * start: new Date(Date.now() - 24 * 60 * 60 * 1000),\n * end: new Date(),\n * });\n * ```\n */\n async getVideoclips(params: GetVideoclipsParams): Promise<RecordingFile[]> {\n return await this.enqueueRecordingsOperation(async () => {\n const dbg = this.client.getDebugConfig?.();\n const logger = this.logger;\n\n const channel = this.normalizeChannel(params.channel ?? 0);\n\n // Discover UID: try explicit -> channel-specific (NVR) -> device-level (standalone)\n const uid = await this.ensureUidForRecordings(channel, params.uid);\n\n // Reolink cameras organize recordings per-day.\n // Ensure start and end are always on the same day by forcing end to 23:59:59 of start's day.\n const start = params.start;\n const endOfStartDay = new Date(\n start.getFullYear(),\n start.getMonth(),\n start.getDate(),\n 23,\n 59,\n 59,\n 999,\n );\n // Use the earlier of params.end or end-of-start-day\n const end =\n params.end.getTime() > endOfStartDay.getTime()\n ? endOfStartDay\n : params.end;\n\n recordingsTraceLog(\n dbg,\n logger,\n \"getVideoclips\",\n `Query: start=${start.toISOString()}, end=${end.toISOString()} (forced same day)`,\n );\n\n recordingsTraceLog(\n dbg,\n logger,\n \"getVideoclips\",\n `Using UID for channel ${channel}: ${uid}`,\n );\n\n const streamType = params.streamType ?? \"subStream\";\n const recordType =\n params.recordType ??\n \"manual, sched, io, md, people, face, vehicle, dog_cat, visitor, other, package\";\n const maxIterations = params.maxIterations ?? 50;\n\n const headerChannelIdOverride =\n this.resolveHeaderChannelIdForLogicalChannel(channel);\n\n const files = await listRecordingsViaFileInfoList({\n sendXml: (p) =>\n this.sendXml({\n ...p,\n ...(headerChannelIdOverride != null && p.channel != null\n ? { channelIdOverride: headerChannelIdOverride }\n : {}),\n }),\n channel,\n uid,\n streamType,\n recordType,\n start,\n end,\n maxIterations,\n ...(params.timeoutMs != null ? { timeoutMs: params.timeoutMs } : {}),\n });\n\n const unique = dedupeRecordingFiles(files);\n recordingsTraceLog(\n dbg,\n logger,\n \"getVideoclips\",\n `FileInfoList complete: ${unique.length} unique files (from ${files.length} total)`,\n );\n return unique;\n });\n }\n\n /**\n * Start a recording replay stream over Baichuan push frames.\n *\n * Socket-based equivalent of a “clip playback”: the device will push BcMedia frames\n * on cmdId=5, and you must stop it with cmdId=7.\n */\n /**\n * Start a recording replay stream for STANDALONE cameras (non-NVR).\n * Uses exact parameters from PCAP analysis:\n * - Uses hostChannelId (header channelId) - do NOT force 0\n * - msgClass = BC_CLASS_MODERN_24 (0x6414)\n * - streamType = 0\n * - NO extensionXml\n */\n private async startRecordingReplayStreamStandalone(params: {\n channel: number;\n fileName: string;\n streamType: RecordingReplayStreamType;\n timeoutMs: number;\n logger?: Logger;\n /** External identifier for the dedicated socket session (e.g., deviceId). */\n deviceId?: string;\n }): Promise<{\n msgNum: number;\n stream: BaichuanVideoStream;\n stop: () => Promise<void>;\n }> {\n const channel = params.channel;\n const streamType = params.streamType;\n const logger = params.logger ?? this.logger;\n\n // Create a dedicated client for this replay session\n // This avoids interference when switching between clips\n // Use external deviceId if provided, otherwise generate a unique key\n const sessionKey = params.deviceId\n ? `replay:${params.deviceId}`\n : `replay:standalone:${channel}:${Date.now()}`;\n const { client: dedicatedClient, release: releaseDedicatedClient } =\n await this.acquireDedicatedClient(sessionKey, logger);\n\n // Get UID for the recording (like download does)\n const uid = await this.ensureUidForRecordings(channel, undefined);\n\n // Build payload XML - standalone uses filename (name attribute)\n // Include UID like the working download method does\n // For standalone cameras, use xmlChannelId=0 explicitly\n const payloadXml = buildFileInfoListReplayByNameXml({\n channel,\n xmlChannelId: 0, // PCAP-verified: xmlChannelId=0 for standalone\n name: params.fileName,\n uid,\n streamType,\n });\n\n // Use msgNum=0 like the working download method\n const msgNum = 0;\n dedicatedClient.subscribeVideoStream(\n BC_CMD_ID_FILE_INFO_LIST_REPLAY,\n msgNum,\n );\n\n const profile: StreamProfile = streamType === \"subStream\" ? \"sub\" : \"main\";\n const stream = new BaichuanVideoStream({\n client: dedicatedClient,\n channel,\n profile,\n variant: \"default\",\n cmdId: BC_CMD_ID_FILE_INFO_LIST_REPLAY,\n msgNum,\n acceptAnyStreamType: true,\n logger,\n });\n\n let started = false;\n try {\n await stream.start();\n\n // PCAP-verified parameters (192.168.1.170, 192.168.50.226):\n // - channelIdOverride should be a session counter (incrementing), not 0 or channel+1\n // - Some H265 cameras reject channelId=0 with responseCode=400\n // - messageClass = BC_CLASS_MODERN_24 (0x6414)\n // - NO extensionXml\n const sessionCounter = dedicatedClient.reserveNextMsgNum();\n const frame = await dedicatedClient.sendFrame({\n cmdId: BC_CMD_ID_FILE_INFO_LIST_REPLAY,\n channel,\n channelIdOverride: sessionCounter,\n payloadXml,\n messageClass: BC_CLASS_MODERN_24,\n msgNumOverride: 0,\n timeoutMs: params.timeoutMs,\n internal: true,\n });\n\n if (frame.header.responseCode !== 200) {\n throw new Error(\n `Standalone replay rejected (response_code=${frame.header.responseCode})`,\n );\n }\n\n started = true;\n } catch (e) {\n try {\n await stream.stop();\n } catch {\n // ignore\n }\n try {\n dedicatedClient.unsubscribeVideoStream(\n BC_CMD_ID_FILE_INFO_LIST_REPLAY,\n msgNum,\n );\n } catch {\n // ignore\n }\n // Release dedicated client on error\n await releaseDedicatedClient();\n throw e;\n }\n\n // Track if teardown has been executed to prevent double-close\n let tornDown = false;\n\n const stop = async (): Promise<void> => {\n if (tornDown) return;\n tornDown = true;\n\n const stopName = buildReplayStopNameFromFileName(params.fileName);\n if (started && stopName) {\n try {\n const stopXml = buildFileInfoListStopXml({\n channel,\n name: stopName,\n streamType,\n });\n\n await dedicatedClient.sendXml({\n cmdId: BC_CMD_ID_FILE_INFO_LIST_STOP,\n channel,\n payloadXml: stopXml,\n messageClass: BC_CLASS_MODERN_24,\n timeoutMs: 10_000,\n internal: true,\n });\n } catch {\n // ignore\n }\n }\n\n try {\n dedicatedClient.unsubscribeVideoStream(\n BC_CMD_ID_FILE_INFO_LIST_REPLAY,\n msgNum,\n );\n } catch {\n // ignore\n }\n\n await stream.stop();\n\n // Release dedicated client when stream stops (closes socket)\n await releaseDedicatedClient();\n };\n\n // Auto-teardown: if stream closes/errors, ensure socket is closed\n // This is like closeApiOnTeardown in RFC4571 server\n const autoTeardown = (reason: string) => {\n if (tornDown) return;\n logger?.debug?.(\n `[DedicatedClient] Auto-teardown for ${sessionKey}: ${reason}`,\n );\n void stop();\n };\n\n stream.once(\"close\", () => autoTeardown(\"stream closed\"));\n stream.once(\"error\", (e) =>\n autoTeardown(`stream error: ${e?.message || e}`),\n );\n\n // Also listen to dedicated client socket errors\n dedicatedClient.once(\"error\", (e) =>\n autoTeardown(`client error: ${e?.message || e}`),\n );\n dedicatedClient.once(\"close\", () => autoTeardown(\"client closed\"));\n\n return { msgNum, stream, stop };\n }\n\n /**\n * Start a recording replay stream for NVR devices.\n * Uses exact parameters from PCAP analysis (192.168.1.161):\n * - channelIdOverride = 0 (header channelId)\n * - msgClass = BC_CLASS_MODERN_24 (0x6414)\n * - streamType = 0\n * - NO extensionXml\n * - Uses id (path) in XML payload, not name\n * - Requires UID for channel mapping\n */\n private async startRecordingReplayStreamNvr(params: {\n channel: number;\n fileName: string;\n streamType: RecordingReplayStreamType;\n timeoutMs: number;\n logger?: Logger;\n /** External identifier for the dedicated socket session (e.g., deviceId). */\n deviceId?: string;\n }): Promise<{\n msgNum: number;\n stream: BaichuanVideoStream;\n stop: () => Promise<void>;\n }> {\n const channel = params.channel;\n const streamType = params.streamType;\n const logger = params.logger ?? this.logger;\n\n // Create a dedicated client for this replay session\n // This avoids interference when switching between clips\n // Use external deviceId if provided, otherwise generate a unique key\n const sessionKey = params.deviceId\n ? `replay:${params.deviceId}`\n : `replay:nvr:${channel}:${Date.now()}`;\n const { client: dedicatedClient, release: releaseDedicatedClient } =\n await this.acquireDedicatedClient(sessionKey, logger);\n\n // NVR needs UID for channel mapping\n let uid: string | undefined;\n try {\n uid = await this.ensureUidForRecordings(channel, undefined);\n } catch {\n // Continue without UID\n }\n\n // Resolve header channel ID for NVR (like download does)\n const headerChannelIdOverride =\n this.resolveHeaderChannelIdForLogicalChannel(channel);\n\n // Build payload XML - NVR uses id (path) attribute\n // PCAP (fileInfoListReplayBinaryDownload): xmlChannelId=0 works for NVR\n const payloadXml = buildFileInfoListReplayByIdXml({\n channel,\n xmlChannelId: 0, // PCAP-verified: xmlChannelId=0 works for NVR\n id: params.fileName,\n ...(uid ? { uid } : {}),\n streamType,\n });\n\n // PCAP-verified: NVR replay uses msgNum=0 (like download)\n const msgNum = 0;\n dedicatedClient.subscribeVideoStream(\n BC_CMD_ID_FILE_INFO_LIST_REPLAY,\n msgNum,\n );\n\n const profile: StreamProfile = streamType === \"subStream\" ? \"sub\" : \"main\";\n const stream = new BaichuanVideoStream({\n client: dedicatedClient,\n channel,\n profile,\n variant: \"default\",\n cmdId: BC_CMD_ID_FILE_INFO_LIST_REPLAY,\n msgNum,\n acceptAnyStreamType: true,\n logger,\n });\n\n let started = false;\n try {\n await stream.start();\n\n // For NVR, use the resolved headerChannelId or 82.\n // For standalone cameras, use a session counter (like CoverPreview does).\n // PCAP analysis shows some cameras (e.g. H265) reject channelId=0 or channel+1.\n const isNvr = headerChannelIdOverride != null;\n const channelIdOverride = isNvr\n ? (headerChannelIdOverride ?? 82)\n : dedicatedClient.reserveNextMsgNum();\n\n const frame = await dedicatedClient.sendFrame({\n cmdId: BC_CMD_ID_FILE_INFO_LIST_REPLAY,\n channel,\n channelIdOverride,\n payloadXml,\n // PCAP-verified: NO extension XML for NVR replay (payloadOffset=0)\n extensionXml: \"\",\n messageClass: BC_CLASS_MODERN_24,\n msgNumOverride: msgNum,\n timeoutMs: params.timeoutMs,\n internal: true,\n });\n\n if (frame.header.responseCode !== 200) {\n throw new Error(\n `NVR replay rejected (response_code=${frame.header.responseCode}) channelIdOverride=${channelIdOverride}`,\n );\n }\n\n started = true;\n } catch (e) {\n try {\n await stream.stop();\n } catch {\n // ignore\n }\n try {\n dedicatedClient.unsubscribeVideoStream(\n BC_CMD_ID_FILE_INFO_LIST_REPLAY,\n msgNum,\n );\n } catch {\n // ignore\n }\n // Release dedicated client on error\n await releaseDedicatedClient();\n throw e;\n }\n\n // Track if teardown has been executed to prevent double-close\n let tornDown = false;\n\n const stop = async (): Promise<void> => {\n if (tornDown) return;\n tornDown = true;\n\n const stopName = buildReplayStopNameFromFileName(params.fileName);\n if (started && stopName) {\n try {\n const stopXml = buildFileInfoListStopXml({\n channel,\n name: stopName,\n streamType,\n });\n\n await dedicatedClient.sendXml({\n cmdId: BC_CMD_ID_FILE_INFO_LIST_STOP,\n channel,\n payloadXml: stopXml,\n messageClass: BC_CLASS_MODERN_24,\n timeoutMs: 10_000,\n internal: true,\n });\n } catch {\n // ignore\n }\n }\n\n try {\n dedicatedClient.unsubscribeVideoStream(\n BC_CMD_ID_FILE_INFO_LIST_REPLAY,\n msgNum,\n );\n } catch {\n // ignore\n }\n\n await stream.stop();\n\n // Release dedicated client when stream stops (closes socket)\n await releaseDedicatedClient();\n };\n\n // Auto-teardown: if stream closes/errors, ensure socket is closed\n // This is like closeApiOnTeardown in RFC4571 server\n const autoTeardown = (reason: string) => {\n if (tornDown) return;\n logger?.debug?.(\n `[DedicatedClient] Auto-teardown for ${sessionKey}: ${reason}`,\n );\n void stop();\n };\n\n stream.once(\"close\", () => autoTeardown(\"stream closed\"));\n stream.once(\"error\", (e) =>\n autoTeardown(`stream error: ${e?.message || e}`),\n );\n\n // Also listen to dedicated client socket errors\n dedicatedClient.once(\"error\", (e) =>\n autoTeardown(`client error: ${e?.message || e}`),\n );\n dedicatedClient.once(\"close\", () => autoTeardown(\"client closed\"));\n\n return { msgNum, stream, stop };\n }\n\n /**\n * Start a recording replay stream.\n * Dispatches to the appropriate method based on fileName format:\n * - NVR recordings have \"/\" in the path (e.g., \"/mnt/...\")\n * - Standalone recordings are just filenames\n *\n * NOTE: Only one replay stream can be active at a time on a single socket connection.\n * Use enqueueReplayOperation() to serialize access.\n */\n async startRecordingReplayStream(params: {\n /** Channel number. Optional for standalone cameras (defaults to 0). Required for NVR. */\n channel?: number;\n fileName: string;\n timeoutMs?: number;\n logger?: Logger;\n /**\n * Force NVR mode (uses id-based XML with UID) or standalone mode (name-based XML).\n * If not specified, the library will detect based on device channel count:\n * - channelCount=1 → standalone camera\n * - channelCount>1 → NVR/Hub\n * NOTE: A path containing \"/\" does NOT indicate NVR - standalone cameras also return full paths.\n */\n isNvr?: boolean;\n /**\n * External identifier for the dedicated socket session.\n * When provided, a dedicated BaichuanClient is created/reused for this deviceId.\n * This allows multiple concurrent replay sessions without interference.\n * If not provided, a unique session key is generated automatically.\n */\n deviceId?: string;\n }): Promise<{\n msgNum: number;\n stream: BaichuanVideoStream;\n stop: () => Promise<void>;\n }> {\n await this.client.login();\n\n // For standalone, default to channel 0. For NVR, channel is required.\n const channel = this.normalizeChannel(params.channel ?? 0);\n // Auto-detect streamType from fileName\n const streamType = this.determineStreamTypeFromFileName(params.fileName);\n const timeoutMs = params.timeoutMs ?? 20_000;\n\n // Determine NVR vs standalone mode:\n // - If explicitly specified, use that\n // - Otherwise, detect based on device channel count (channelCount>1 = NVR)\n // NOTE: Do NOT use fileName.includes(\"/\") - standalone cameras also return full paths like /mnt/sda/...\n const isNvr = params.isNvr ?? (await this.isNvrDevice());\n\n const commonParams = {\n channel,\n fileName: params.fileName,\n streamType,\n timeoutMs,\n ...(params.logger != null ? { logger: params.logger } : {}),\n ...(params.deviceId != null ? { deviceId: params.deviceId } : {}),\n };\n\n const result = isNvr\n ? await this.startRecordingReplayStreamNvr(commonParams)\n : await this.startRecordingReplayStreamStandalone(commonParams);\n\n return result;\n }\n\n /** Legacy FileInfoList DL Video (cmdId=8). Returns response parsed as JSON. */\n async fileInfoListDownloadVideo(params?: {\n channel?: number;\n payloadXml?: string;\n timeoutMs?: number;\n }): Promise<XmlJsonValue> {\n const xml = await this.sendXml({\n cmdId: BC_CMD_ID_FILE_INFO_LIST_DL_VIDEO,\n ...(params?.channel != null ? { channel: params.channel } : {}),\n ...(params?.payloadXml != null ? { payloadXml: params.payloadXml } : {}),\n ...(params?.timeoutMs != null ? { timeoutMs: params.timeoutMs } : {}),\n });\n return parseXmlFragmentToJson(xml);\n }\n\n /**\n * Convenience helper to build playback/download URLs for a single recording.\n *\n * Currently returns the RTMP VOD URL (suitable for streaming/export via playback).\n * Use {@link ReolinkBaichuanApi#downloadRecording | downloadRecording} for bit-identical file download.\n */\n async getRecordingPlaybackUrls(params: {\n /** Logical channel to query. If omitted, uses the client's configured channel (or 0). */\n channel?: number;\n fileName: string;\n streamType?: RecordingStreamType;\n /** If true (default), ensure RTMP is enabled before returning the URL. */\n ensureEnabled?: boolean;\n }): Promise<RecordingPlaybackUrls> {\n const effectiveChannel =\n params.channel ?? this.client.getConfiguredChannel?.() ?? 0;\n\n const rtmpVodUrl = await this.getVodRtmpUrl({\n channel: effectiveChannel,\n fileName: params.fileName,\n ...(params.streamType ? { streamType: params.streamType } : {}),\n ...(params.ensureEnabled !== undefined\n ? { ensureEnabled: params.ensureEnabled }\n : {}),\n });\n\n return { rtmpVodUrl };\n }\n\n /**\n * Ensure we have a UID suitable for recording-related operations.\n *\n * If an explicit UID is provided, it is returned as-is.\n * Otherwise, this method returns the cached `this.uid` if already known.\n *\n * No automatic discovery is performed here: callers must ensure that a UID is available\n * either via explicit parameter or via the client configuration.\n */\n private async ensureUidForRecordings(\n channel: number,\n explicitUid?: string,\n ): Promise<string> {\n const dbg = this.client.getDebugConfig?.();\n const logger = this.logger;\n\n recordingsTraceLog(\n dbg,\n logger,\n \"ensureUidForRecordings\",\n `Checking UID: explicitUid=${explicitUid}, this.uid=${this.uid}`,\n );\n\n const trimmedExplicit = explicitUid?.trim();\n if (trimmedExplicit) {\n recordingsTraceLog(\n dbg,\n logger,\n \"ensureUidForRecordings\",\n `Using explicit UID: ${trimmedExplicit}`,\n );\n return trimmedExplicit;\n }\n\n const perChannel = await this.discoverUidForRecordingsForChannel(channel);\n if (perChannel) {\n recordingsTraceLog(\n dbg,\n logger,\n \"ensureUidForRecordings\",\n `Using per-channel UID: ${perChannel}`,\n );\n return perChannel;\n }\n\n const configured = this.uid?.trim();\n if (configured) {\n recordingsTraceLog(\n dbg,\n logger,\n \"ensureUidForRecordings\",\n `Using configured UID: ${configured}`,\n );\n return configured;\n }\n\n if (this.nativeOnly) {\n recordingsTraceLog(\n dbg,\n logger,\n \"ensureUidForRecordings\",\n `Native-only: no UID available (channel=${channel})`,\n );\n throw new Error(\n \"UID is required to access recordings in native-only mode. Provide a UID explicitly, configure the client with a UID, or wait for cmd_id=145 push cache to populate per-channel UIDs.\",\n );\n }\n\n recordingsTraceLog(\n dbg,\n logger,\n \"ensureUidForRecordings\",\n `No UID available, attempting device auto-discovery (ch=${channel})`,\n );\n const discovered = await this.discoverDeviceUidForRecordings(channel);\n if (discovered) return discovered;\n\n recordingsTraceLog(\n dbg,\n logger,\n \"ensureUidForRecordings\",\n `Auto-discovery failed - no UID found`,\n );\n throw new Error(\n \"UID is required to access recordings. Provide a UID explicitly or configure the client with a UID.\",\n );\n }\n\n private cacheChannelUid(channel: number, uid: string): void {\n const existing = this.channelPushData.get(channel);\n const now = Date.now();\n const base = existing ?? { name: \"\", uid: \"\", state: \"\", updatedAtMs: now };\n this.channelPushData.set(channel, { ...base, uid, updatedAtMs: now });\n }\n\n private async discoverUidForRecordingsForChannel(\n channel: number,\n ): Promise<string | undefined> {\n const dbg = this.client.getDebugConfig?.();\n const logger = this.logger;\n\n const fromPush = this.getUidFromPushCacheForChannel(channel);\n if (fromPush) {\n recordingsTraceLog(\n dbg,\n logger,\n \"ensureUidForRecordings\",\n `Using per-channel UID from push cache: ${fromPush}`,\n );\n return fromPush;\n }\n\n if (this.nativeOnly) {\n recordingsTraceLog(\n dbg,\n logger,\n \"ensureUidForRecordings\",\n `Native-only: skipping per-channel UID discovery via HTTP/CGI (channel=${channel})`,\n );\n return undefined;\n }\n\n try {\n recordingsTraceLog(\n dbg,\n logger,\n \"ensureUidForRecordings\",\n `Attempting per-channel UID discovery via HTTP CGI GetChannelstatus (channel=${channel})`,\n );\n const uidCandidate = await discoverPerChannelUidViaCgiChannelstatus({\n channel,\n login: () => this.cgiApi.login(),\n getChannelstatus: () => this.cgiApi.GetChannelstatus(),\n });\n\n recordingsTraceLog(\n dbg,\n logger,\n \"ensureUidForRecordings\",\n `[HTTP CGI] GetChannelstatus channel=${channel} uid=${uidCandidate || \"(missing)\"}`,\n );\n\n if (uidCandidate) {\n this.cacheChannelUid(channel, uidCandidate);\n recordingsTraceLog(\n dbg,\n logger,\n \"ensureUidForRecordings\",\n `Using per-channel UID from GetChannelstatus: ${uidCandidate}`,\n );\n return uidCandidate;\n }\n } catch (e) {\n recordingsTraceLog(\n dbg,\n logger,\n \"ensureUidForRecordings\",\n `[HTTP CGI] GetChannelstatus failed: ${formatErrorForLog(e)}`,\n );\n }\n\n return undefined;\n }\n\n private async discoverDeviceUidForRecordings(\n channel: number,\n ): Promise<string | undefined> {\n if (this.nativeOnly) return undefined;\n const dbg = this.client.getDebugConfig?.();\n const logger = this.logger;\n\n const trace = (message: string): void =>\n recordingsTraceLog(dbg, logger, \"ensureUidForRecordings\", message);\n\n const discoveredUid = await discoverDeviceUidForRecordingsUtil({\n channel,\n // For standalone cameras, use getInfo() without channel (cmdId=80) as cmdId=318 returns empty\n getInfo: () => this.getInfo(),\n cgiLogin: () => this.cgiApi.login(),\n cgiGetP2p: () => this.cgiApi.call(\"GetP2p\", {}),\n cgiGetDevInfo: () => this.cgiApi.GetDevInfo(),\n sendXml: (p) => this.sendXml(p),\n trace,\n });\n\n if (discoveredUid) {\n this.uid = discoveredUid;\n trace(`Auto-discovered and cached device UID: ${discoveredUid}`);\n return discoveredUid;\n }\n\n return undefined;\n }\n\n /**\n * Get a video I-frame (keyframe) from a past recording at a specific timestamp.\n *\n * Uses the Baichuan CoverPreview command (cmd_id=298) to retrieve an I-frame\n * directly from the camera without external dependencies.\n *\n * The returned data is a raw H.264 or H.265 I-frame. To convert it to JPEG,\n * you can use ffmpeg or a video decoder library.\n *\n * NOTE: Requests are queued and processed one at a time. The camera often\n * rejects concurrent CoverPreview requests, so this serialization prevents\n * unnecessary failures and retries.\n *\n * @param params - Parameters for the snapshot\n * @returns Object containing the raw I-frame data and metadata\n */\n async getVideoclipThumbnail(params: {\n /** Channel number (0-based) */\n channel?: number;\n /** Timestamp to capture (start time) */\n time: Date;\n /** Optional end time. If omitted, uses time + 10 seconds. For best results, use the full recording range. */\n endTime?: Date;\n /** Stream type for snapshot quality (\"main\" or \"sub\", default: \"sub\") */\n snapType?: \"main\" | \"sub\";\n /** Optional UID for the camera (required for NVR). If omitted, will be discovered. */\n uid?: string;\n /** Timeout in milliseconds (default: 30000) */\n timeoutMs?: number;\n /** Explicitly specify if this is an NVR device. If omitted, auto-detects. */\n isNvr?: boolean;\n }): Promise<VideoclipThumbnailResult> {\n // If no request in flight, execute immediately\n if (!this.videoclipThumbnailInFlight) {\n this.videoclipThumbnailInFlight = this._getVideoclipThumbnailImpl(params);\n try {\n return await this.videoclipThumbnailInFlight;\n } finally {\n this.videoclipThumbnailInFlight = null;\n // Process next queued request if any\n this._processVideoclipThumbnailQueue();\n }\n }\n\n // Otherwise, queue the request and return a promise\n return new Promise<VideoclipThumbnailResult>((resolve, reject) => {\n this.videoclipThumbnailQueue.push({ params, resolve, reject });\n });\n }\n\n /**\n * Process the next item in the thumbnail queue.\n */\n private _processVideoclipThumbnailQueue(): void {\n const next = this.videoclipThumbnailQueue.shift();\n if (!next) return;\n\n this.videoclipThumbnailInFlight = this._getVideoclipThumbnailImpl(\n next.params,\n );\n this.videoclipThumbnailInFlight\n .then((result) => {\n next.resolve(result);\n })\n .catch((error) => {\n next.reject(error instanceof Error ? error : new Error(String(error)));\n })\n .finally(() => {\n this.videoclipThumbnailInFlight = null;\n this._processVideoclipThumbnailQueue();\n });\n }\n\n /**\n * Internal implementation of getVideoclipThumbnail.\n * This is the actual work - the public method handles queueing.\n */\n private async _getVideoclipThumbnailImpl(params: {\n channel?: number;\n time: Date;\n endTime?: Date;\n snapType?: \"main\" | \"sub\";\n uid?: string;\n timeoutMs?: number;\n isNvr?: boolean;\n }): Promise<VideoclipThumbnailResult> {\n await this.client.login();\n\n const dbg = this.client.getDebugConfig?.();\n const logger = this.logger;\n const trace = (message: string): void =>\n recordingsTraceLog(dbg, logger, \"getVideoclipThumbnail\", message);\n\n const channel = this.normalizeChannel(params.channel);\n const snapType = params.snapType ?? \"sub\";\n const snapStreamType = snapType === \"main\" ? \"mainStream\" : \"subStream\";\n const timeoutMs = params.timeoutMs ?? 30_000;\n const time = params.time;\n\n // Determine if this is an NVR (multiple channels).\n // For NVR, we need to use hostChannelId (250) or push-cache channelId.\n // For standalone cameras, use session counter.\n // Allow explicit override via params.isNvr for cases where auto-detection fails.\n const isNvr = params.isNvr ?? (await this.isNvrDevice());\n const headerChannelIdOverride = isNvr\n ? (this.resolveHeaderChannelIdForLogicalChannel(channel) ?? 250)\n : undefined;\n\n // CoverPreview requires a time range\n // PCAP shows the app uses the full recording range, not just time + 10 seconds\n const endTime = params.endTime ?? new Date(time.getTime() + 10_000);\n\n // For NVR devices, we need to include the camera UID in the CoverPreview XML.\n // PCAP analysis shows: NVR requests always include <uid> element after <channelId>.\n // The UID is the device identifier of the sub-camera connected to the NVR.\n let uidForXml: string | undefined;\n if (isNvr) {\n // First check if UID was provided in params\n uidForXml = params.uid;\n // Otherwise, get it from the push cache\n if (!uidForXml) {\n const pushInfo = this.getChannelInfoFromPushCache();\n const channelInfo = pushInfo.get(channel);\n uidForXml = channelInfo?.uid;\n }\n if (uidForXml) {\n trace(\n `CoverPreview: using UID ${uidForXml} for NVR channel ${channel}`,\n );\n } else {\n trace(\n `CoverPreview: no UID found for NVR channel ${channel}, omitting from XML`,\n );\n }\n }\n\n // Build CoverPreview XML exactly as seen in working PCAP capture:\n // - <channelId> = logical channel (0-based)\n // - <uid> = device identifier (required for NVR, omit for standalone cameras)\n // - NO <desc> tag (PCAP shows it's not present in working requests!)\n // - streamType = \"subStream\" or \"mainStream\"\n // NOTE: uses LOCAL time (not UTC) for timestamps\n const xml = `<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<body>\n<CoverPreview version=\"1.1\">\n<channelId>${channel}</channelId>${\n isNvr && uidForXml\n ? `\n<uid>${uidForXml}</uid>`\n : \"\"\n }\n<streamType>${snapStreamType}</streamType>\n<startTime>\n<year>${time.getFullYear()}</year>\n<month>${time.getMonth() + 1}</month>\n<day>${time.getDate()}</day>\n<hour>${time.getHours()}</hour>\n<minute>${time.getMinutes()}</minute>\n<second>${time.getSeconds()}</second>\n</startTime>\n<endTime>\n<year>${endTime.getFullYear()}</year>\n<month>${endTime.getMonth() + 1}</month>\n<day>${endTime.getDate()}</day>\n<hour>${endTime.getHours()}</hour>\n<minute>${endTime.getMinutes()}</minute>\n<second>${endTime.getSeconds()}</second>\n</endTime>\n<frameList>\n<frameNo>1</frameNo>\n</frameList>\n</CoverPreview>\n</body>`;\n\n trace(\n `CoverPreview: channel=${channel} snapStreamType=${snapStreamType} time=${time.toISOString()} timeoutMs=${timeoutMs} isNvr=${isNvr} headerChId=${headerChannelIdOverride} uid=${uidForXml ?? \"N/A\"}`,\n );\n trace(`CoverPreview XML:\\n${xml}`);\n\n // For NVR: use the resolved headerChannelId from push cache (like FileInfoList).\n // For standalone cameras: use session counter (let client handle it).\n // NOTE: Retry logic is handled inside sendBinaryCoverPreview.\n // PCAP analysis shows the camera often rejects first few requests with 400 before accepting.\n let payload: Buffer;\n try {\n payload = await this.client.sendBinaryCoverPreview({\n cmdId: 298,\n // For NVR: use push-cache channelId. For standalone: let client use session counter.\n ...(isNvr && headerChannelIdOverride != null\n ? { channelIdOverride: headerChannelIdOverride }\n : {}),\n // PCAP shows: msgNum=0 for all CoverPreview requests\n msgNumOverride: 0,\n messageClass: BC_CLASS_MODERN_24,\n streamType: 0,\n payloadXml: xml,\n timeoutMs,\n // Retry parameters - camera often rejects first few requests\n maxRetries: 8,\n retryDelayMs: 1500,\n });\n trace(`CoverPreview succeeded`);\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n trace(`CoverPreview failed: ${msg}`);\n throw e;\n }\n\n // Parse stream header\n if (payload.length < 32) {\n throw new Error(\n `CoverPreview payload too short: ${payload.length} bytes`,\n );\n }\n\n const magic = payload.subarray(0, 4).toString(\"ascii\");\n const supportedMagics = new Set([\"1001\", \"1002\"]);\n\n // Some NVRs return the AVI frame directly without a stream header.\n // Detect this case by checking for \"00dc\" magic at the start.\n if (magic === \"00dc\") {\n trace(\n `CoverPreview: payload starts with '00dc' (AVI frame marker), parsing directly`,\n );\n // The payload is the raw AVI-style frame, no stream header present.\n // Frame format: \"00dc\" (4 bytes) + frame_len (4 bytes little-endian) + frame_data\n const frameLen = payload.readUInt32LE(4);\n const frame = payload.subarray(8, 8 + frameLen);\n\n const detectEncoding = (buf: Buffer): string => {\n const maxScan = Math.min(buf.length - 6, 64 * 1024);\n let start = -1;\n let scLen = 0;\n for (let i = 0; i < maxScan; i++) {\n if (buf[i] !== 0x00 || buf[i + 1] !== 0x00) continue;\n if (buf[i + 2] === 0x01) {\n start = i;\n scLen = 3;\n break;\n }\n if (buf[i + 2] === 0x00 && buf[i + 3] === 0x01) {\n start = i;\n scLen = 4;\n break;\n }\n }\n\n if (start < 0) return \"unknown\";\n const nalHeaderIndex = start + scLen;\n if (nalHeaderIndex >= buf.length) return \"unknown\";\n\n const b0 = buf[nalHeaderIndex];\n if (b0 === undefined) return \"unknown\";\n const h264Type = b0 & 0x1f;\n const h265Type = (b0 >> 1) & 0x3f;\n\n if ([7, 8, 5, 1].includes(h264Type)) return \"H264\";\n if ([32, 33, 34, 19, 20].includes(h265Type)) return \"H265\";\n\n return \"unknown\";\n };\n\n const encoding = detectEncoding(frame);\n\n return {\n frame,\n encoding,\n frameLength: frame.length,\n streamInfo: {},\n };\n }\n\n if (!supportedMagics.has(magic)) {\n throw new Error(\n `CoverPreview payload did not start with a supported stream header magic ('1001'/'1002') but with '${magic}'`,\n );\n }\n\n // Most captures show a u32le header length at offset 4 (often 32). Be defensive.\n let streamHeaderLen = 32;\n try {\n const candidate = payload.readUInt32LE(4);\n if (\n Number.isFinite(candidate) &&\n candidate >= 16 &&\n candidate <= 4096 &&\n candidate <= payload.length\n ) {\n streamHeaderLen = candidate;\n }\n } catch {\n // ignore\n }\n\n const streamHeader = payload.subarray(0, streamHeaderLen);\n\n // Parse stream header fields\n const width = streamHeader.readUInt32LE(8);\n const height = streamHeader.readUInt32LE(12);\n const frameRate = streamHeader.length > 17 ? streamHeader[17] : 0;\n\n // Search for frame magic \"00dc\" after stream header\n const frameSearchArea = payload.subarray(streamHeaderLen);\n const frameMagic = Buffer.from(\"00dc\", \"ascii\");\n let frameMagicIndex = -1;\n\n for (let i = 0; i <= frameSearchArea.length - 4; i++) {\n if (\n frameSearchArea[i] === frameMagic[0] &&\n frameSearchArea[i + 1] === frameMagic[1] &&\n frameSearchArea[i + 2] === frameMagic[2] &&\n frameSearchArea[i + 3] === frameMagic[3]\n ) {\n frameMagicIndex = i;\n break;\n }\n }\n\n if (frameMagicIndex === -1) {\n // Some firmwares appear to return a raw Annex-B payload without the AVI-like \"00dc\" wrapper.\n // Fall back to returning everything after the stream header as the frame payload.\n const frame = payload.subarray(streamHeaderLen);\n if (frame.length === 0) {\n throw new Error(\n `CoverPreview frame marker '00dc' not found and no payload after header. First bytes after header: ${frameSearchArea.subarray(0, 30).toString(\"hex\")}`,\n );\n }\n\n const detectEncoding = (buf: Buffer): string => {\n // Find the first Annex-B startcode (0x000001 or 0x00000001)\n const maxScan = Math.min(buf.length - 6, 64 * 1024);\n let start = -1;\n let scLen = 0;\n for (let i = 0; i < maxScan; i++) {\n if (buf[i] !== 0x00 || buf[i + 1] !== 0x00) continue;\n if (buf[i + 2] === 0x01) {\n start = i;\n scLen = 3;\n break;\n }\n if (buf[i + 2] === 0x00 && buf[i + 3] === 0x01) {\n start = i;\n scLen = 4;\n break;\n }\n }\n\n if (start < 0) return \"unknown\";\n const nalHeaderIndex = start + scLen;\n if (nalHeaderIndex >= buf.length) return \"unknown\";\n\n const b0 = buf[nalHeaderIndex];\n if (b0 === undefined) return \"unknown\";\n const h264Type = b0 & 0x1f;\n const h265Type = (b0 >> 1) & 0x3f;\n\n // H.264 common NAL unit types: 7(SPS),8(PPS),5(IDR),1(non-IDR)\n if ([7, 8, 5, 1].includes(h264Type)) return \"H264\";\n // H.265 common NAL unit types: 32(VPS),33(SPS),34(PPS),19/20(IDR)\n if ([32, 33, 34, 19, 20].includes(h265Type)) return \"H265\";\n\n return \"unknown\";\n };\n\n const encoding = detectEncoding(frame);\n\n const streamInfo: PlaybackSnapshotStreamInfo = {};\n if (width > 0) streamInfo.width = width;\n if (height > 0) streamInfo.height = height;\n const fr = frameRate ?? 0;\n if (fr > 0) streamInfo.frameRate = fr;\n\n return {\n frame,\n encoding,\n frameLength: frame.length,\n streamInfo,\n };\n }\n\n const idx = streamHeaderLen + frameMagicIndex;\n\n // Parse frame header\n // Frame header structure:\n // - 0-4: magic \"00dc\"\n // - 4-8: encoding (e.g., \"H264\", \"H265\")\n // - 8-12: frame length\n // - 12-16: additional header length\n // - 16-20: frame microsecond\n // - 24-28: frame time (Unix timestamp)\n const frameHeaderStart = idx;\n const additionalHeaderLen = payload.readUInt32LE(frameHeaderStart + 12);\n const headerLen = 24 + additionalHeaderLen;\n const frameHeader = payload.subarray(\n frameHeaderStart,\n frameHeaderStart + headerLen,\n );\n\n const encoding = frameHeader\n .subarray(4, 8)\n .toString(\"ascii\")\n .replace(/\\0/g, \"\");\n const frameLen = frameHeader.readUInt32LE(8);\n const frameTime =\n headerLen >= 28 ? frameHeader.readUInt32LE(24) : undefined;\n\n // Extract frame data\n const frameStart = frameHeaderStart + headerLen;\n const frameEnd = frameStart + frameLen;\n\n if (frameEnd > payload.length) {\n throw new Error(\n `Frame data extends beyond payload: frameEnd=${frameEnd}, payloadLength=${payload.length}`,\n );\n }\n\n const frame = payload.subarray(frameStart, frameEnd);\n\n // Build streamInfo conditionally to satisfy exactOptionalPropertyTypes\n const streamInfo: PlaybackSnapshotStreamInfo = {};\n if (width > 0) streamInfo.width = width;\n if (height > 0) streamInfo.height = height;\n const fr = frameRate ?? 0;\n if (fr > 0) streamInfo.frameRate = fr;\n\n // Build result conditionally for frameTime\n const result: VideoclipThumbnailResult = {\n frame,\n encoding,\n frameLength: frameLen,\n streamInfo,\n };\n if (frameTime !== undefined) result.frameTime = frameTime;\n\n return result;\n }\n\n /**\n * Like {@link ReolinkBaichuanApi#getVideoclipThumbnail | getVideoclipThumbnail}, but returns a JPEG.\n *\n * Uses `ffmpeg` to decode the CoverPreview I-frame.\n */\n async getVideoclipThumbnailJpeg(params: {\n channel?: number;\n time: Date;\n endTime?: Date;\n snapType?: \"main\" | \"sub\";\n timeoutMs?: number;\n ffmpegPath?: string;\n /** Explicitly specify if this is an NVR device. If omitted, auto-detects. */\n isNvr?: boolean;\n }): Promise<Buffer> {\n const timeoutMs = params.timeoutMs ?? 30_000;\n const ffmpegPath = params.ffmpegPath ?? \"ffmpeg\";\n\n const snapParams: {\n channel?: number;\n time: Date;\n endTime?: Date;\n snapType?: \"main\" | \"sub\";\n timeoutMs?: number;\n isNvr?: boolean;\n } = {\n time: params.time,\n timeoutMs,\n };\n if (params.channel !== undefined) snapParams.channel = params.channel;\n if (params.endTime !== undefined) snapParams.endTime = params.endTime;\n if (params.snapType !== undefined) snapParams.snapType = params.snapType;\n if (params.isNvr !== undefined) snapParams.isNvr = params.isNvr;\n\n const snap = await this.getVideoclipThumbnail(snapParams);\n\n return this.decodeCoverPreviewFrameToJpeg({\n frame: snap.frame,\n encoding: snap.encoding,\n ffmpegPath,\n timeoutMs,\n });\n }\n\n private async decodeCoverPreviewFrameToJpeg(params: {\n frame: Buffer;\n encoding: string;\n ffmpegPath: string;\n timeoutMs: number;\n }): Promise<Buffer> {\n const encodingUpper = params.encoding.toUpperCase();\n const fmts: string[] = [];\n if (encodingUpper.includes(\"265\") || encodingUpper.includes(\"HEVC\")) {\n fmts.push(\"hevc\", \"h264\");\n } else {\n fmts.push(\"h264\", \"hevc\");\n }\n\n const tryDecode = (fmt: string): Promise<Buffer> => {\n return new Promise<Buffer>((resolve, reject) => {\n const chunks: Buffer[] = [];\n let stderr = \"\";\n let timedOut = false;\n\n const ff = spawn(params.ffmpegPath, [\n \"-hide_banner\",\n \"-loglevel\",\n \"error\",\n \"-f\",\n fmt,\n \"-i\",\n \"pipe:0\",\n \"-frames:v\",\n \"1\",\n \"-f\",\n \"image2\",\n \"-c:v\",\n \"mjpeg\",\n \"-q:v\",\n \"2\",\n \"pipe:1\",\n ]);\n\n const timeout = setTimeout(() => {\n timedOut = true;\n ff.kill(\"SIGKILL\");\n reject(new Error(`ffmpeg timed out after ${params.timeoutMs}ms`));\n }, params.timeoutMs);\n\n ff.stdout.on(\"data\", (chunk: Buffer) => chunks.push(chunk));\n ff.stderr.on(\"data\", (data: Buffer) => (stderr += data.toString()));\n\n ff.on(\"close\", (code) => {\n clearTimeout(timeout);\n if (timedOut) return;\n\n if (code !== 0) {\n reject(new Error(`ffmpeg exited with code ${code}: ${stderr}`));\n return;\n }\n\n const imageBuffer = Buffer.concat(chunks);\n if (imageBuffer.length === 0) {\n reject(new Error(`ffmpeg produced no output. stderr: ${stderr}`));\n return;\n }\n\n resolve(imageBuffer);\n });\n\n ff.on(\"error\", (err) => {\n clearTimeout(timeout);\n if (timedOut) return;\n reject(new Error(`ffmpeg error: ${err.message}`));\n });\n\n ff.stdin.end(params.frame);\n });\n };\n\n let lastErr: unknown;\n for (const fmt of fmts) {\n try {\n return await tryDecode(fmt);\n } catch (e) {\n lastErr = e;\n }\n }\n\n const msg = lastErr instanceof Error ? lastErr.message : String(lastErr);\n throw new Error(\n `Failed to decode CoverPreview frame to JPEG (encoding=${params.encoding}). Last error: ${msg}`,\n );\n }\n\n /**\n * Get a JPEG snapshot from a specific recording file at a given offset.\n *\n * This is a lower-level version that takes a fileName directly instead of searching.\n *\n * @param params - Parameters for the snapshot\n * @returns JPEG image bytes\n */\n async snapshotFromRecording(params: {\n /** Channel number (0-based) */\n channel?: number;\n /** Recording file name/path */\n fileName: string;\n /** Seek position in seconds from the start of the recording (default: 0) */\n seekSeconds?: number;\n /** Stream type (\"mainStream\" or \"subStream\", default: \"subStream\") */\n streamType?: RecordingStreamType;\n /** Path to ffmpeg binary (default: \"ffmpeg\" from PATH) */\n ffmpegPath?: string;\n /** Timeout in milliseconds for ffmpeg (default: 30000) */\n timeoutMs?: number;\n }): Promise<Buffer> {\n await this.client.login();\n\n const channel = this.normalizeChannel(params.channel);\n const streamType = params.streamType ?? \"subStream\";\n const ffmpegPath = params.ffmpegPath ?? \"ffmpeg\";\n const timeoutMs = params.timeoutMs ?? 30_000;\n const seekSeconds = params.seekSeconds ?? 0;\n\n // Get RTMP VOD URL\n const rtmpUrl = await this.getVodRtmpUrl({\n channel,\n fileName: params.fileName,\n streamType,\n ensureEnabled: true,\n });\n\n // Use ffmpeg to extract a single frame\n return new Promise<Buffer>((resolve, reject) => {\n const chunks: Buffer[] = [];\n let stderr = \"\";\n let timedOut = false;\n\n const ff = spawn(ffmpegPath, [\n \"-hide_banner\",\n \"-loglevel\",\n \"error\",\n \"-rtmp_live\",\n \"live\",\n ...(seekSeconds > 0 ? [\"-ss\", seekSeconds.toFixed(3)] : []),\n \"-i\",\n rtmpUrl,\n \"-frames:v\",\n \"1\",\n \"-f\",\n \"image2\",\n \"-c:v\",\n \"mjpeg\",\n \"-q:v\",\n \"2\",\n \"pipe:1\",\n ]);\n\n const timeout = setTimeout(() => {\n timedOut = true;\n ff.kill(\"SIGKILL\");\n reject(new Error(`ffmpeg timed out after ${timeoutMs}ms`));\n }, timeoutMs);\n\n ff.stdout.on(\"data\", (chunk: Buffer) => {\n chunks.push(chunk);\n });\n\n ff.stderr.on(\"data\", (data: Buffer) => {\n stderr += data.toString();\n });\n\n ff.on(\"close\", (code) => {\n clearTimeout(timeout);\n if (timedOut) return;\n\n if (code !== 0) {\n reject(new Error(`ffmpeg exited with code ${code}: ${stderr}`));\n return;\n }\n\n const imageBuffer = Buffer.concat(chunks);\n if (imageBuffer.length === 0) {\n reject(new Error(`ffmpeg produced no output. stderr: ${stderr}`));\n return;\n }\n\n resolve(imageBuffer);\n });\n\n ff.on(\"error\", (err) => {\n clearTimeout(timeout);\n if (timedOut) return;\n reject(new Error(`ffmpeg error: ${err.message}`));\n });\n });\n }\n\n /**\n * Build an RTMP VOD/playback URL for a given recording.\n *\n * This follows the same logic:\n * `rtmp://<host>:<rtmpPort>/vod/<filename-with-\"/\"->\"%20\">?channel=<ch>&stream=<type>&user=<u>&password=<p>`\n *\n * Note: this is intended for *export/streaming via playback* (strategy B), not for bit-identical file download.\n */\n async getVodRtmpUrl(params: {\n channel: number;\n fileName: string;\n streamType?: RecordingStreamType;\n /** Ensure RTMP is enabled on the device before returning the URL (default true). */\n ensureEnabled?: boolean;\n /** Override RTMP port if known. */\n rtmpPort?: number;\n }): Promise<string> {\n // Note: login() is not called here to avoid unnecessary reconnections\n // The caller should ensure the client is already connected and logged in via ensureClient()\n const channel = this.normalizeChannel(params.channel);\n const streamType = params.streamType ?? \"mainStream\";\n const ensureEnabled = params.ensureEnabled ?? true;\n\n let rtmpPort = params.rtmpPort;\n try {\n const ports = await this.getNetPort();\n const rtmp = ports.rtmp;\n const enable = typeof rtmp?.enable === \"number\" ? rtmp.enable : undefined;\n const port = typeof rtmp?.port === \"number\" ? rtmp.port : undefined;\n if (rtmpPort == null && port != null && Number.isFinite(port) && port > 0)\n rtmpPort = port;\n if (ensureEnabled && enable === 0) {\n await this.setPortEnabled({ port: \"rtmp\", enable: true });\n }\n } catch {\n // Best-effort: if NetPort is unavailable, assume defaults.\n }\n\n if (rtmpPort == null) rtmpPort = 1935;\n\n const streamTypeInt = streamType === \"subStream\" ? 1 : 0;\n\n // Most cameras return absolute paths like `/mnt/sda/Mp4Record/...` but RTMP VOD usually\n // expects a path starting at `Mp4Record/...`.\n let source = params.fileName.trim();\n const idx = source.indexOf(\"Mp4Record/\");\n if (idx >= 0) source = source.slice(idx);\n source = source.replace(/^\\/+/, \"\");\n\n // Replace '/' with '%20' for vod paths.\n const vodPath = source.replace(/\\//g, \"%20\").replace(/ /g, \"%20\");\n const user = encodeURIComponent(this.username);\n const pass = encodeURIComponent(this.password);\n\n return `rtmp://${this.host}:${rtmpPort}/vod/${vodPath}?channel=${channel}&stream=${streamTypeInt}&user=${user}&password=${pass}`;\n }\n\n /**\n * Predownload/export a recording locally as an MP4 file (strategy B).\n *\n * This is intended to be reliable for battery cameras where bit-identical\n * FileInfoList download (class 0x6482) can time out.\n *\n * Requirements:\n * - `ffmpeg` must be available in PATH.\n * - The device must expose an RTMP VOD/playback stream for the given `fileName`.\n */\n async predownloadRecordingMp4(params: {\n channel: number;\n fileName: string;\n outputPath: string;\n streamType?: RecordingStreamType;\n /** If true, attempt to wake a sleeping battery camera before download (default false). */\n ensureAwake?: boolean;\n /** Override ffmpeg binary path (default: \"ffmpeg\" from PATH). */\n ffmpegPath?: string;\n /** Overwrite outputPath if it already exists (default true). */\n overwrite?: boolean;\n }): Promise<void> {\n await this.client.login();\n\n const channel = this.normalizeChannel(params.channel);\n const streamType = params.streamType ?? \"mainStream\";\n const ensureAwake = params.ensureAwake ?? false;\n const ffmpegPath = params.ffmpegPath ?? \"ffmpeg\";\n const overwrite = params.overwrite ?? true;\n\n await mkdir(dirname(params.outputPath), { recursive: true });\n\n const runOnce = async (): Promise<void> => {\n if (ensureAwake) {\n // Best-effort: keep battery cams awake for the heavy transfer.\n await this.wakeUp(channel, { waitAfterWakeMs: 1500, attempts: 3 });\n }\n\n const rtmpUrl = await this.getVodRtmpUrl({\n channel,\n fileName: params.fileName,\n streamType,\n ensureEnabled: true,\n });\n\n await new Promise<void>((resolve, reject) => {\n const ff = spawn(ffmpegPath, [\n \"-hide_banner\",\n \"-loglevel\",\n \"error\",\n ...(overwrite ? [\"-y\"] : []),\n \"-rtmp_live\",\n \"live\",\n \"-i\",\n rtmpUrl,\n \"-c\",\n \"copy\",\n \"-movflags\",\n \"frag_keyframe+empty_moov\",\n \"-f\",\n \"mp4\",\n params.outputPath,\n ]);\n\n let stderr = \"\";\n ff.stderr.on(\"data\", (d) => {\n stderr += String(d);\n });\n\n ff.on(\"close\", (code) => {\n if (code === 0) return resolve();\n reject(new Error(`ffmpeg exited with code ${code}\\n${stderr}`));\n });\n\n ff.on(\"error\", reject);\n });\n };\n\n try {\n await runOnce();\n } catch (e) {\n // One retry helps with battery cams going to sleep / stale sessions.\n if (ensureAwake) {\n try {\n await this.wakeUp(channel, {\n waitAfterWakeMs: 3000,\n attempts: 3,\n reconnect: true,\n });\n } catch {\n // ignore\n }\n }\n await runOnce();\n }\n }\n\n /**\n * Generate a single-frame screenshot from a local MP4 file at the given timestamp.\n *\n * Requirements:\n * - `ffmpeg` must be available in PATH, or provide `ffmpegPath`.\n */\n async generateMp4Screenshot(params: {\n inputPath: string;\n outputPath: string;\n atSeconds: number;\n /** Override ffmpeg binary path (default: \"ffmpeg\" from PATH). */\n ffmpegPath?: string;\n }): Promise<void> {\n const ffmpegPath = params.ffmpegPath ?? \"ffmpeg\";\n const atSeconds =\n Number.isFinite(params.atSeconds) && params.atSeconds >= 0\n ? params.atSeconds\n : 0;\n\n await mkdir(dirname(params.outputPath), { recursive: true });\n\n await new Promise<void>((resolve, reject) => {\n const ff = spawn(ffmpegPath, [\n \"-hide_banner\",\n \"-loglevel\",\n \"error\",\n \"-y\",\n \"-ss\",\n String(atSeconds),\n \"-i\",\n params.inputPath,\n \"-frames:v\",\n \"1\",\n \"-q:v\",\n \"2\",\n params.outputPath,\n ]);\n\n let stderr = \"\";\n ff.stderr.on(\"data\", (d) => {\n stderr += String(d);\n });\n\n ff.on(\"close\", (code) => {\n if (code === 0) return resolve();\n reject(\n new Error(`ffmpeg screenshot exited with code ${code}\\n${stderr}`),\n );\n });\n\n ff.on(\"error\", reject);\n });\n }\n\n /**\n * Download a recording via Baichuan FileInfoList download (cmdId=13, class=0x6482).\n * Returns raw bytes (often an mp4/flv/ps payload depending on firmware/camera).\n */\n async fileInfoListDownload(params: {\n channel: number;\n fileName: string;\n /** Optional UID; if omitted, the library will attempt to infer/discover it. */\n uid?: string;\n timeoutMs?: number;\n }): Promise<Buffer> {\n await this.client.login();\n\n const dbg = this.client.getDebugConfig?.();\n const logger = this.logger;\n const trace = (message: string): void =>\n recordingsTraceLog(dbg, logger, \"fileInfoListDownload\", message);\n\n const channel = this.normalizeChannel(params.channel);\n const uid = await this.ensureUidForRecordings(channel, params.uid);\n const headerChannelIdOverride =\n this.resolveHeaderChannelIdForLogicalChannel(channel);\n\n const headerChannelCandidates = [\n headerChannelIdOverride,\n channel + 1,\n undefined,\n 250,\n 0,\n 251,\n ].filter((v, i, a) => a.indexOf(v) === i);\n const xmlChannelIdCandidates = [\n channel,\n channel + 1,\n headerChannelIdOverride,\n 0,\n 1,\n ]\n .filter((v): v is number => typeof v === \"number\" && Number.isFinite(v))\n .filter((v, i, a) => a.indexOf(v) === i);\n\n const totalTimeoutMs = params.timeoutMs ?? 120_000;\n const startedAt = Date.now();\n const streamTypeCandidates = [0, 2];\n let lastErr: unknown;\n let attempts = 0;\n const maxAttempts = 12;\n\n trace(\n `attempt budget: channel=${channel} uid=${uid || \"(missing)\"} headerChannelId=[${headerChannelCandidates.map((v) => (v == null ? \"(default)\" : String(v))).join(\",\")}] xmlChannelId=[${xmlChannelIdCandidates.join(\",\")}] headerStreamType=[${streamTypeCandidates.join(\",\")}] totalTimeoutMs=${totalTimeoutMs} maxAttempts=${maxAttempts}`,\n );\n\n for (const xmlCh of xmlChannelIdCandidates) {\n const payloadXml = buildFileInfoListDownloadXml({\n channel,\n uid,\n fileName: params.fileName,\n xmlChannelId: xmlCh,\n });\n\n for (const chId of headerChannelCandidates) {\n for (const st of streamTypeCandidates) {\n const remaining = totalTimeoutMs - (Date.now() - startedAt);\n if (remaining <= 0) break;\n if (attempts >= maxAttempts) break;\n attempts++;\n const attemptTimeoutMs = Math.min(5_000, remaining);\n trace(\n `attempt=${attempts}/${maxAttempts} file=${params.fileName} xmlCh=${xmlCh} headerCh=${chId == null ? \"(default)\" : chId} headerStreamType=${st} attemptTimeoutMs=${attemptTimeoutMs}`,\n );\n try {\n return await this.client.sendBinary({\n cmdId: BC_CMD_ID_FILE_INFO_LIST_DOWNLOAD,\n channel,\n ...(typeof chId === \"number\" ? { channelIdOverride: chId } : {}),\n messageClass: BC_CLASS_FILE_DOWNLOAD,\n extensionXml: buildBinaryExtensionXml(channel),\n payloadXml,\n streamType: st,\n timeoutMs: attemptTimeoutMs,\n });\n } catch (e) {\n lastErr = e;\n const msg = e instanceof Error ? e.message : String(e);\n trace(`attempt=${attempts} failed: ${msg}`);\n // Timeouts can be variant-dependent; continue while within budget.\n if (msg.includes(\"timeout\")) continue;\n if (\n !msg.includes(\"rejected\") &&\n !msg.includes(\"responseCode=400\")\n ) {\n break;\n }\n }\n }\n if (attempts >= maxAttempts) break;\n }\n if (attempts >= maxAttempts) break;\n }\n\n throw lastErr instanceof Error\n ? lastErr\n : new Error(String(lastErr ?? \"FileInfoList download failed\"));\n }\n\n /**\n * Download a recording via paged FileInfoList (cmdId=14 OPEN, 15 GET, 16 CLOSE).\n * This method is used by some cameras (e.g., TrackMix PoE) where cmdId=5/13 return empty.\n * It opens the file, retrieves data in chunks, and closes the session.\n */\n async fileInfoListPagedDownload(params: {\n channel: number;\n fileName: string;\n uid?: string;\n timeoutMs?: number;\n }): Promise<Buffer> {\n await this.client.login();\n\n const dbg = this.client.getDebugConfig?.();\n const logger = this.logger;\n const trace = (message: string): void =>\n recordingsTraceLog(dbg, logger, \"fileInfoListPagedDownload\", message);\n\n const channel = this.normalizeChannel(params.channel);\n const uid = await this.ensureUidForRecordings(channel, params.uid);\n\n trace(\n `Starting paged download: channel=${channel}, uid=${uid}, fileName=${params.fileName}`,\n );\n\n const sendXml = async (p: {\n cmdId: number;\n payloadXml?: string;\n timeoutMs?: number;\n }): Promise<string> => {\n return await this.client.sendXml({\n cmdId: p.cmdId,\n ...(p.payloadXml != null ? { payloadXml: p.payloadXml } : {}),\n ...(p.timeoutMs != null ? { timeoutMs: p.timeoutMs } : {}),\n });\n };\n\n const sendBinary = async (p: {\n cmdId: number;\n payloadXml?: string;\n timeoutMs?: number;\n }): Promise<Buffer> => {\n return await this.client.sendBinary({\n cmdId: p.cmdId,\n ...(p.payloadXml != null ? { payloadXml: p.payloadXml } : {}),\n ...(p.timeoutMs != null ? { timeoutMs: p.timeoutMs } : {}),\n });\n };\n\n const result = await downloadRecordingViaFileInfoListPaged({\n sendXml,\n sendBinary,\n channel,\n uid,\n fileName: params.fileName,\n timeoutMs: params.timeoutMs ?? 120_000,\n });\n\n trace(`Paged download completed: ${result.length} bytes`);\n return result;\n }\n\n /**\n * Download a recording via FileInfoList replay (cmdId=5) using the PCAP-observed binary chunk flow.\n * This is native-only and does NOT involve CGI/HTTP.\n */\n async fileInfoListReplayBinaryDownload(params: {\n channel: number;\n fileName: string;\n /** Optional UID; if omitted, the library will attempt to infer/discover it. */\n uid?: string;\n timeoutMs?: number;\n }): Promise<Buffer> {\n await this.client.login();\n\n const dbg = this.client.getDebugConfig?.();\n const logger = this.logger;\n const trace = (message: string): void =>\n recordingsTraceLog(\n dbg,\n logger,\n \"fileInfoListReplayBinaryDownload\",\n message,\n );\n\n const channel = this.normalizeChannel(params.channel);\n const headerChannelIdOverride =\n this.resolveHeaderChannelIdForLogicalChannel(channel);\n const ident = params.fileName;\n // Auto-detect streamType from fileName\n const streamType = this.determineStreamTypeFromFileName(params.fileName);\n\n // For NVR, use the resolved headerChannelId or 82.\n // For standalone cameras, do NOT override channelId - let sendBinary use the\n // host channelId (typically 250). PCAP analysis shows some cameras (e.g. H265)\n // reject channelId=0 with responseCode=400, but accept the hostChannelId.\n const isNvr = headerChannelIdOverride != null;\n\n // PCAP Analysis (2025-06): The Reolink app does NOT include <uid> in the XML\n // for standalone cameras. Including it causes responseCode=400 on some H265 cameras.\n // Only get/require UID for NVR configurations where it's required.\n let uid: string | undefined;\n if (isNvr) {\n uid = await this.ensureUidForRecordings(channel, params.uid);\n }\n\n // PCAP Analysis (2025-06): The Reolink app uses the standard FileInfoList format with\n // <FileInfo><Id>...</Id><supportSub>0</supportSub><playSpeed>1</playSpeed><streamType>mainStream</streamType></FileInfo>\n // For standalone cameras (non-NVR), do NOT include <uid> in the XML.\n const payloadXml = ident.includes(\"/\")\n ? buildFileInfoListReplayByIdXml({\n channel,\n xmlChannelId: 0, // PCAP-verified: xmlChannelId=0 works\n id: ident,\n ...(uid ? { uid } : {}),\n streamType,\n })\n : buildFileInfoListReplayByNameXml({\n channel,\n xmlChannelId: 0,\n name: ident,\n ...(uid ? { uid } : {}),\n streamType,\n });\n\n const timeoutMs = params.timeoutMs ?? 120_000;\n\n trace(\n `download: channel=${channel} uid=${uid || \"(missing)\"} ident=${ident} streamType=${streamType} isNvr=${isNvr} timeoutMs=${timeoutMs}`,\n );\n\n try {\n return await this.client.sendBinary({\n cmdId: BC_CMD_ID_FILE_INFO_LIST_REPLAY,\n channel,\n ...(isNvr ? { channelIdOverride: headerChannelIdOverride ?? 82 } : {}),\n msgNumOverride: 0,\n messageClass: BC_CLASS_MODERN_24,\n payloadXml,\n streamType: 0,\n timeoutMs,\n });\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n trace(`download failed: ${msg}`);\n throw e;\n }\n }\n\n async downloadRecording(params: DownloadRecordingParams): Promise<Buffer> {\n // Use replay queue to serialize all download operations on this socket\n return this.enqueueReplayOperation(async () => {\n await this.client.login();\n\n const channel = this.normalizeChannel(params.channel);\n const uid = await this.ensureUidForRecordings(channel, params.uid);\n const fileName = params.fileName;\n\n let replayErr: unknown;\n try {\n return await this.fileInfoListReplayBinaryDownload({\n channel,\n uid,\n fileName,\n ...(params.timeoutMs != null ? { timeoutMs: params.timeoutMs } : {}),\n });\n } catch (e) {\n replayErr = e;\n }\n\n let downloadErr: unknown;\n try {\n return await this.fileInfoListDownload({\n channel,\n uid,\n fileName,\n ...(params.timeoutMs != null ? { timeoutMs: params.timeoutMs } : {}),\n });\n } catch (e) {\n downloadErr = e;\n }\n\n // Third fallback: paged download via cmdId=14/15/16\n // This works for TrackMix PoE and other cameras where cmdId=5/13 return empty\n try {\n const result = await this.fileInfoListPagedDownload({\n channel,\n uid,\n fileName,\n ...(params.timeoutMs != null ? { timeoutMs: params.timeoutMs } : {}),\n });\n if (result.length > 0) {\n return result;\n }\n } catch (e) {\n // Fall through to error\n }\n\n const replayMsg =\n replayErr instanceof Error\n ? replayErr.message\n : replayErr != null\n ? String(replayErr)\n : \"\";\n const dlMsg =\n downloadErr instanceof Error\n ? downloadErr.message\n : downloadErr != null\n ? String(downloadErr)\n : \"\";\n // Native-only: do not fall back to HTTP/CGI.\n throw new Error(\n `Baichuan download failed (native-only). replay(cmdId=${BC_CMD_ID_FILE_INFO_LIST_REPLAY}) err=${replayMsg || \"(unknown)\"}; download(cmdId=${BC_CMD_ID_FILE_INFO_LIST_DOWNLOAD}) err=${dlMsg}`,\n );\n });\n }\n\n /**\n * Download a recording and demux it to Annex-B format.\n *\n * The raw Baichuan download returns BcMedia-formatted data which needs to be\n * demuxed to extract the video frames. This method downloads the recording\n * and demuxes it into a single Annex-B buffer that can be played by ffmpeg\n * or converted to MP4.\n *\n * Example usage:\n * ```ts\n * const result = await api.downloadRecordingDemuxed({ fileName, channel });\n * // Save as .h264 or .h265 file\n * await fs.writeFile(\"recording.h264\", result.annexB);\n * // Convert to MP4 with ffmpeg:\n * // ffmpeg -i recording.h264 -c copy recording.mp4\n * ```\n *\n * @param params - Download parameters\n * @returns Demuxed recording with Annex-B video data and stats\n */\n async downloadRecordingDemuxed(params: DownloadRecordingParams): Promise<{\n /** Concatenated video frames in Annex-B format (H.264 or H.265) */\n annexB: Buffer;\n /** Detected video codec */\n videoType: BcMediaVideoType | null;\n /** Statistics about the demuxed recording */\n stats: {\n bytesIn: number;\n bytesOut: number;\n packets: number;\n videoPackets: number;\n audioPackets: number;\n keyframes: number;\n };\n }> {\n const raw = await this.downloadRecording(params);\n\n const frames: Buffer[] = [];\n const decoder = new BcMediaAnnexBDecoder({\n strict: false,\n logger: this.logger,\n onVideoAccessUnit: ({ annexB }) => {\n frames.push(annexB);\n },\n });\n\n decoder.push(raw);\n\n const stats = decoder.getStats();\n\n return {\n annexB: Buffer.concat(frames),\n videoType: stats.videoType,\n stats: {\n bytesIn: stats.bytesIn,\n bytesOut: stats.bytesOut,\n packets: stats.packets,\n videoPackets: stats.videoPackets,\n audioPackets: stats.audioPackets,\n keyframes: stats.keyframes,\n },\n };\n }\n\n /**\n * Get a recording as a ready-to-play MP4 file with video and audio muxed together.\n *\n * This method downloads a recording via Baichuan protocol, demuxes it, and uses\n * ffmpeg to mux video+audio into a single MP4 file.\n *\n * Example usage:\n * ```ts\n * const { mp4, stats } = await api.getRecordingVideo({\n * channel: 0,\n * fileName: \"/mnt/sda/Mp4Record/2026-01-22/Rec_20260122_000320.mp4\"\n * });\n *\n * // Save directly as playable MP4\n * await fs.writeFile(\"recording.mp4\", mp4);\n * ```\n *\n * @param params - Download parameters\n * @returns MP4 buffer with muxed video+audio and statistics\n */\n async getRecordingVideo(\n params: DownloadRecordingParams & {\n /** Path to ffmpeg binary (default: \"ffmpeg\" from PATH) */\n ffmpegPath?: string;\n },\n ): Promise<GetRecordingVideoResult> {\n const raw = await this.downloadRecording(params);\n\n const videoFrames: { annexB: Buffer; microseconds: number }[] = [];\n const audioFrames: Buffer[] = [];\n let audioCodec: BcMediaAudioType | null = null;\n\n const decoder = new BcMediaAnnexBDecoder({\n strict: false,\n logger: this.logger,\n onVideoAccessUnit: ({ annexB, microseconds }) => {\n videoFrames.push({ annexB, microseconds });\n },\n onAudioFrame: ({ audioType, data }) => {\n if (audioCodec == null) audioCodec = audioType;\n audioFrames.push(data);\n },\n });\n\n decoder.push(raw);\n\n const decoderStats = decoder.getStats();\n const videoCodec = decoderStats.videoType ?? \"H264\";\n\n // Determine FPS - prefer timestamps over info FPS for correct audio sync\n // The info FPS tells what the camera records at, but the actual frames transmitted\n // may be subsampled (e.g., 30fps recording -> 15fps transmission for bandwidth)\n // Using timestamps ensures video duration matches audio duration\n let fps: number;\n let durationSeconds: number;\n\n if (videoFrames.length >= 2) {\n const firstTs = videoFrames[0]!.microseconds;\n const lastTs = videoFrames[videoFrames.length - 1]!.microseconds;\n const durationUs = lastTs - firstTs;\n\n if (durationUs > 0) {\n // Calculate from timestamps - most reliable for A/V sync\n durationSeconds = durationUs / 1_000_000;\n fps = (videoFrames.length - 1) / durationSeconds;\n } else {\n // Fallback to info FPS if timestamps are invalid\n const infoFps = decoderStats.infos[0]?.fps;\n fps = infoFps && infoFps > 0 ? infoFps : 15;\n durationSeconds = videoFrames.length / fps;\n }\n } else {\n // Not enough frames, use info FPS\n const infoFps = decoderStats.infos[0]?.fps;\n fps = infoFps && infoFps > 0 ? infoFps : 15;\n durationSeconds = videoFrames.length / fps;\n }\n\n // Round FPS to common values if close\n if (fps > 14 && fps < 16) fps = 15;\n else if (fps > 23 && fps < 26) fps = 25;\n else if (fps > 29 && fps < 31) fps = 30;\n else fps = Math.round(fps * 100) / 100;\n\n const videoData = Buffer.concat(videoFrames.map((f) => f.annexB));\n const audioData =\n audioFrames.length > 0 ? Buffer.concat(audioFrames) : null;\n const hasAudio = audioData != null && audioData.length > 0;\n\n // Mux video+audio with ffmpeg\n const mp4 = await this.muxToMp4({\n videoData,\n videoCodec,\n audioData,\n audioCodec,\n fps,\n durationHint: durationSeconds,\n ...(params.ffmpegPath ? { ffmpegPath: params.ffmpegPath } : {}),\n });\n\n return {\n mp4,\n stats: {\n bytesIn: decoderStats.bytesIn,\n videoBytesOut: decoderStats.bytesOut,\n audioBytesOut: decoderStats.audioBytesOut,\n videoPackets: decoderStats.videoPackets,\n audioPackets: decoderStats.audioPackets,\n keyframes: decoderStats.keyframes,\n fps,\n durationSeconds,\n videoCodec,\n audioCodec,\n hasAudio,\n },\n };\n }\n\n /**\n * Get a JPEG thumbnail from a recording file.\n *\n * This method uses the playback snapshot protocol to extract a frame from the\n * beginning of a recording and converts it to JPEG. Requests are queued with\n * limited concurrency (max 2 simultaneous) to avoid overwhelming the camera.\n *\n * Example usage:\n * ```ts\n * const jpeg = await api.getRecordingThumbnail({\n * channel: 0,\n * fileName: \"/mnt/sda/Mp4Record/2026-01-22/Rec_20260122_000320.mp4\"\n * });\n *\n * // Save as JPEG file\n * await fs.writeFile(\"thumbnail.jpg\", jpeg);\n * ```\n *\n * @param params - Thumbnail parameters\n * @returns JPEG buffer\n */\n async getRecordingThumbnail(params: {\n /** Channel number (default: 0) */\n channel?: number;\n /** Recording file name/path */\n fileName: string;\n /** Timeout in ms (default: 15000) */\n timeoutMs?: number;\n /** Path to ffmpeg binary (default: \"ffmpeg\") */\n ffmpegPath?: string;\n }): Promise<Buffer> {\n const channel = this.normalizeChannel(params.channel ?? 0);\n // Auto-detect streamType from fileName\n const streamType = this.determineStreamTypeFromFileName(params.fileName);\n const timeoutMs = params.timeoutMs ?? 15_000;\n const ffmpegPath = params.ffmpegPath ?? \"ffmpeg\";\n\n // Use de-duplication key for this thumbnail request\n const dedupKey = `thumbnail:${channel}:${params.fileName}`;\n\n // Enqueue with de-duplication - if same request is in progress, returns existing promise\n return this.enqueueReplayOperation(async () => {\n this.logger?.debug?.(\n `[getRecordingThumbnail] Extracting thumbnail via streaming: channel=${channel}, file=${params.fileName}, streamType=${streamType}`,\n );\n\n // Start replay stream to capture first keyframe\n const { stream, stop } = await this.startRecordingReplayStream({\n channel,\n fileName: params.fileName,\n timeoutMs,\n });\n\n try {\n // Wait for first keyframe that contains parameter sets (SPS/PPS for H.264, VPS/SPS/PPS for H.265)\n const keyframe = await new Promise<{\n data: Buffer;\n videoType: BcMediaVideoType;\n }>((resolve, reject) => {\n const timeout = setTimeout(() => {\n reject(\n new Error(\"Timeout waiting for keyframe with parameter sets\"),\n );\n }, timeoutMs);\n\n const hasH264ParamSets = (data: Buffer): boolean => {\n // Look for SPS (NAL type 7) and PPS (NAL type 8) in Annex-B stream\n let hasSps = false;\n let hasPps = false;\n let i = 0;\n while (i < data.length - 4) {\n // Find start code (00 00 00 01 or 00 00 01)\n if (data[i] === 0 && data[i + 1] === 0) {\n let nalStart = -1;\n if (data[i + 2] === 0 && data[i + 3] === 1) {\n nalStart = i + 4;\n } else if (data[i + 2] === 1) {\n nalStart = i + 3;\n }\n if (nalStart >= 0 && nalStart < data.length) {\n const nalByte = data[nalStart];\n if (nalByte !== undefined) {\n const nalType = nalByte & 0x1f;\n if (nalType === 7) hasSps = true;\n if (nalType === 8) hasPps = true;\n if (hasSps && hasPps) return true;\n }\n i = nalStart;\n continue;\n }\n }\n i++;\n }\n return hasSps && hasPps;\n };\n\n const hasH265ParamSets = (data: Buffer): boolean => {\n // Look for VPS (32), SPS (33), PPS (34) in Annex-B stream\n let hasVps = false;\n let hasSps = false;\n let hasPps = false;\n let i = 0;\n while (i < data.length - 4) {\n if (data[i] === 0 && data[i + 1] === 0) {\n let nalStart = -1;\n if (data[i + 2] === 0 && data[i + 3] === 1) {\n nalStart = i + 4;\n } else if (data[i + 2] === 1) {\n nalStart = i + 3;\n }\n if (nalStart >= 0 && nalStart < data.length) {\n const nalByte = data[nalStart];\n if (nalByte !== undefined) {\n const nalType = (nalByte >> 1) & 0x3f;\n if (nalType === 32) hasVps = true;\n if (nalType === 33) hasSps = true;\n if (nalType === 34) hasPps = true;\n if (hasVps && hasSps && hasPps) return true;\n }\n i = nalStart;\n continue;\n }\n }\n i++;\n }\n return hasVps && hasSps && hasPps;\n };\n\n const onFrame = (au: {\n data: Buffer;\n isKeyframe: boolean;\n videoType: \"H264\" | \"H265\";\n }) => {\n if (!au.isKeyframe) return;\n\n // Verify the keyframe contains parameter sets\n const hasParams =\n au.videoType === \"H265\"\n ? hasH265ParamSets(au.data)\n : hasH264ParamSets(au.data);\n\n if (!hasParams) {\n // Log but keep waiting for a complete keyframe\n this.logger?.debug?.(\n `[getRecordingThumbnail] Keyframe missing parameter sets, waiting for next: codec=${au.videoType}, size=${au.data.length}`,\n );\n return;\n }\n\n clearTimeout(timeout);\n stream.off(\"videoAccessUnit\", onFrame);\n resolve({\n data: au.data,\n videoType: au.videoType,\n });\n };\n\n stream.on(\"videoAccessUnit\", onFrame);\n stream.once(\"error\", (err) => {\n clearTimeout(timeout);\n stream.off(\"videoAccessUnit\", onFrame);\n reject(err);\n });\n stream.once(\"close\", () => {\n clearTimeout(timeout);\n stream.off(\"videoAccessUnit\", onFrame);\n reject(new Error(\"Stream closed before keyframe received\"));\n });\n });\n\n this.logger?.debug?.(\n `[getRecordingThumbnail] Got keyframe with params: ${keyframe.data.length} bytes, codec=${keyframe.videoType}`,\n );\n\n // Convert keyframe to JPEG using ffmpeg\n const jpeg = await this.convertFrameToJpeg({\n frameData: keyframe.data,\n videoCodec: keyframe.videoType,\n ffmpegPath,\n });\n\n this.logger?.debug?.(\n `[getRecordingThumbnail] Thumbnail extracted: ${jpeg.length} bytes`,\n );\n\n return jpeg;\n } finally {\n await stop().catch(() => {});\n }\n }, dedupKey);\n }\n\n /**\n * Convert a raw video keyframe to JPEG using ffmpeg.\n */\n private async convertFrameToJpeg(params: {\n frameData: Buffer;\n videoCodec: BcMediaVideoType;\n ffmpegPath?: string;\n }): Promise<Buffer> {\n const { spawn } = await import(\"node:child_process\");\n const ffmpeg = params.ffmpegPath ?? \"ffmpeg\";\n const inputFormat = params.videoCodec === \"H265\" ? \"hevc\" : \"h264\";\n\n return new Promise<Buffer>((resolve, reject) => {\n const args = [\n \"-hide_banner\",\n \"-loglevel\",\n \"error\",\n \"-f\",\n inputFormat,\n \"-i\",\n \"pipe:0\",\n \"-frames:v\",\n \"1\",\n \"-f\",\n \"image2\",\n \"-c:v\",\n \"mjpeg\",\n \"-q:v\",\n \"2\",\n \"pipe:1\",\n ];\n\n const proc = spawn(ffmpeg, args, {\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n });\n\n const chunks: Buffer[] = [];\n let stderr = \"\";\n\n proc.stdout.on(\"data\", (chunk: Buffer) => chunks.push(chunk));\n proc.stderr.on(\"data\", (chunk: Buffer) => {\n stderr += chunk.toString();\n });\n\n proc.on(\"close\", (code) => {\n if (code !== 0 || chunks.length === 0) {\n reject(\n new Error(\n `ffmpeg failed to convert frame to JPEG (code=${code}): ${stderr}`,\n ),\n );\n return;\n }\n resolve(Buffer.concat(chunks));\n });\n\n proc.on(\"error\", reject);\n\n proc.stdin.write(params.frameData);\n proc.stdin.end();\n });\n }\n\n /**\n * Get a recording as MP4 by streaming it frame-by-frame.\n *\n * This is an alternative to getRecordingVideo() that uses the replay streaming\n * protocol instead of bulk download. Useful for cameras that don't support\n * the download protocol (cmdId=5/13) but support streaming playback.\n *\n * @param params - Streaming parameters\n * @returns MP4 buffer with video (audio not supported in streaming mode)\n */\n async getRecordingVideoViaStreaming(params: {\n channel?: number;\n fileName: string;\n /** Maximum streaming duration in ms (default: 300000 = 5 min) */\n maxDurationMs?: number;\n /** Idle timeout - stop if no frames received for this duration (default: 10000ms) */\n idleTimeoutMs?: number;\n /** Path to ffmpeg binary (default: \"ffmpeg\" from PATH) */\n ffmpegPath?: string;\n }): Promise<{\n mp4: Buffer;\n stats: {\n videoPackets: number;\n keyframes: number;\n fps: number;\n durationSeconds: number;\n videoCodec: BcMediaVideoType;\n };\n }> {\n const channel = this.normalizeChannel(params.channel ?? 0);\n const maxDurationMs = params.maxDurationMs ?? 300_000; // 5 min max\n const idleTimeoutMs = params.idleTimeoutMs ?? 10_000; // 10s idle timeout\n\n this.logger?.info?.(\n `[getRecordingVideoViaStreaming] Starting stream: channel=${channel}, file=${params.fileName}`,\n );\n\n const { stream, stop } = await this.startRecordingReplayStream({\n channel,\n fileName: params.fileName,\n timeoutMs: 30_000,\n });\n\n const videoFrames: Buffer[] = [];\n let videoCodec: BcMediaVideoType = \"H264\";\n let keyframes = 0;\n let lastFrameAt = Date.now();\n const startedAt = Date.now();\n\n // Collect frames until stream ends or timeout\n await new Promise<void>((resolve) => {\n let resolved = false;\n const finish = () => {\n if (resolved) return;\n resolved = true;\n clearInterval(checkInterval);\n resolve();\n };\n\n // Handle video frames\n stream.on(\"videoAccessUnit\", (frame) => {\n lastFrameAt = Date.now();\n videoFrames.push(frame.data);\n videoCodec = frame.videoType;\n if (frame.isKeyframe) keyframes++;\n\n // Log progress\n if (videoFrames.length % 100 === 0) {\n this.logger?.debug?.(\n `[getRecordingVideoViaStreaming] Collected ${videoFrames.length} frames (${keyframes} keyframes)`,\n );\n }\n });\n\n // Handle stream close (end of recording)\n stream.on(\"close\", () => {\n this.logger?.info?.(\n `[getRecordingVideoViaStreaming] Stream closed after ${videoFrames.length} frames`,\n );\n finish();\n });\n\n // Handle errors\n stream.on(\"error\", (err) => {\n this.logger?.warn?.(\n `[getRecordingVideoViaStreaming] Stream error: ${err.message}`,\n );\n finish();\n });\n\n // Check for timeout/idle periodically\n const checkInterval = setInterval(() => {\n const now = Date.now();\n const elapsed = now - startedAt;\n const idleTime = now - lastFrameAt;\n\n // Max duration exceeded\n if (elapsed > maxDurationMs) {\n this.logger?.info?.(\n `[getRecordingVideoViaStreaming] Max duration reached (${elapsed}ms)`,\n );\n finish();\n return;\n }\n\n // Idle timeout - no frames for a while means recording ended\n if (videoFrames.length > 0 && idleTime > idleTimeoutMs) {\n this.logger?.info?.(\n `[getRecordingVideoViaStreaming] Idle timeout (${idleTime}ms with no frames)`,\n );\n finish();\n return;\n }\n }, 1000);\n });\n\n // Stop the stream\n try {\n await stop();\n } catch {\n // Ignore stop errors\n }\n\n if (videoFrames.length === 0) {\n throw new Error(\n \"No video frames received from streaming - recording may be empty or streaming not supported\",\n );\n }\n\n // Estimate FPS from frame count and duration\n const streamDuration = (Date.now() - startedAt) / 1000;\n const estimatedFps =\n streamDuration > 0 ? Math.round(videoFrames.length / streamDuration) : 15;\n // Use common FPS values\n const fps =\n estimatedFps >= 28\n ? 30\n : estimatedFps >= 23\n ? 25\n : estimatedFps >= 13\n ? 15\n : 10;\n\n this.logger?.info?.(\n `[getRecordingVideoViaStreaming] Collected ${videoFrames.length} frames, ${keyframes} keyframes, estimated ${fps} fps`,\n );\n\n const videoData = Buffer.concat(videoFrames);\n const durationSeconds = videoFrames.length / fps;\n\n // Mux to MP4 (no audio in streaming mode)\n const mp4 = await this.muxToMp4({\n videoData,\n videoCodec,\n audioData: null,\n audioCodec: null,\n fps,\n ...(params.ffmpegPath ? { ffmpegPath: params.ffmpegPath } : {}),\n });\n\n return {\n mp4,\n stats: {\n videoPackets: videoFrames.length,\n keyframes,\n fps,\n durationSeconds,\n videoCodec,\n },\n };\n }\n\n /**\n * Internal helper to mux video+audio into MP4 using ffmpeg.\n */\n private async muxToMp4(params: {\n videoData: Buffer;\n videoCodec: BcMediaVideoType;\n audioData: Buffer | null;\n audioCodec: BcMediaAudioType | null;\n fps: number;\n durationHint?: number;\n ffmpegPath?: string;\n }): Promise<Buffer> {\n const { spawn } = await import(\"node:child_process\");\n const { randomUUID } = await import(\"node:crypto\");\n const fs = await import(\"node:fs/promises\");\n const os = await import(\"node:os\");\n const path = await import(\"node:path\");\n\n const ffmpeg = params.ffmpegPath ?? \"ffmpeg\";\n const tmpDir = os.tmpdir();\n const id = randomUUID();\n\n const videoFormat = params.videoCodec === \"H265\" ? \"hevc\" : \"h264\";\n const videoPath = path.join(tmpDir, `reolink-${id}.${videoFormat}`);\n const outputPath = path.join(tmpDir, `reolink-${id}.mp4`);\n\n let audioPath: string | null = null;\n if (params.audioData && params.audioData.length > 0 && params.audioCodec) {\n const audioExt = params.audioCodec === \"Aac\" ? \"aac\" : \"raw\";\n audioPath = path.join(tmpDir, `reolink-${id}.${audioExt}`);\n }\n\n try {\n // Write temp files\n await fs.writeFile(videoPath, params.videoData);\n if (audioPath && params.audioData) {\n await fs.writeFile(audioPath, params.audioData);\n }\n\n // Build ffmpeg args\n const args: string[] = [\"-hide_banner\", \"-loglevel\", \"error\", \"-y\"];\n\n // Video input with framerate\n // Using -r on input tells ffmpeg to interpret the raw stream at this rate\n if (params.fps > 0) {\n args.push(\"-r\", String(params.fps));\n }\n args.push(\"-f\", videoFormat, \"-i\", videoPath);\n\n // Audio input (if present)\n if (audioPath && params.audioCodec) {\n if (params.audioCodec === \"Aac\") {\n // AAC ADTS has its own timestamps, ffmpeg will read them\n args.push(\"-f\", \"aac\", \"-i\", audioPath);\n } else {\n // ADPCM: s16le format, mono, 8000Hz sample rate\n args.push(\"-f\", \"s16le\", \"-ar\", \"8000\", \"-ac\", \"1\", \"-i\", audioPath);\n }\n }\n\n // Output options: copy codecs, fragmented MP4 for streaming compatibility\n args.push(\"-c:v\", \"copy\");\n if (audioPath && params.audioCodec) {\n if (params.audioCodec === \"Aac\") {\n // AAC in ADTS format needs bitstream filter for MP4 muxing\n args.push(\"-c:a\", \"copy\", \"-bsf:a\", \"aac_adtstoasc\");\n } else {\n // ADPCM needs transcoding to AAC\n args.push(\"-c:a\", \"aac\");\n }\n // NOTE: Do NOT use -shortest as it can drop audio when video has no timestamps\n }\n\n // Set output framerate to match input\n if (params.fps > 0) {\n args.push(\"-r\", String(params.fps));\n }\n\n args.push(\n \"-movflags\",\n \"frag_keyframe+empty_moov+default_base_moof\",\n \"-f\",\n \"mp4\",\n outputPath,\n );\n\n // Run ffmpeg\n await new Promise<void>((resolve, reject) => {\n const p = spawn(ffmpeg, args, { stdio: [\"ignore\", \"ignore\", \"pipe\"] });\n let stderr = \"\";\n\n p.stderr.on(\"data\", (d: Buffer) => {\n stderr += d.toString();\n });\n\n p.on(\"error\", (e) => {\n reject(new Error(`ffmpeg spawn error: ${e.message}`));\n });\n\n p.on(\"close\", (code) => {\n if (code === 0) {\n resolve();\n } else {\n reject(\n new Error(\n `ffmpeg exited with code ${code}: ${stderr.slice(-1000)}`,\n ),\n );\n }\n });\n });\n\n // Read output\n return await fs.readFile(outputPath);\n } finally {\n // Cleanup temp files\n await fs.unlink(videoPath).catch(() => {});\n if (audioPath) await fs.unlink(audioPath).catch(() => {});\n await fs.unlink(outputPath).catch(() => {});\n }\n }\n\n /**\n * Subscribe to events (motion/AI/visitor) via Baichuan.\n * cmd_id: 31 (subscribe_events)\n * After subscribing, events will be emitted via client.on(\"event\", ...)\n */\n async subscribeEvents(): Promise<void> {\n await this.client.login();\n // NOTE: Some battery firmwares reject the old \"channelId=251 Extension\" approach with responseCode=421.\n // Send MSG_ID 31 with *empty* body and channel_id set to the camera channel (0-based).\n const channel = this.client.getConfiguredChannel?.() ?? 0;\n\n const attempts: Array<{\n label: string;\n params: Parameters<BaichuanClient[\"sendFrame\"]>[0];\n }> = [\n {\n label: `channelId=${channel} bodyLen=0`,\n params: {\n cmdId: 31,\n channelIdOverride: channel,\n messageClass: BC_CLASS_MODERN_24,\n },\n },\n {\n // Use ch_id=251 for push subscription.\n label: \"push channelId=251 bodyLen=0\",\n params: {\n cmdId: 31,\n channelIdOverride: 251,\n messageClass: BC_CLASS_MODERN_24,\n },\n },\n {\n label: \"host channelId=250 bodyLen=0\",\n params: {\n cmdId: 31,\n channelIdOverride: 250,\n messageClass: BC_CLASS_MODERN_24,\n },\n },\n {\n label: \"legacy Extension channelId=251\",\n params: {\n cmdId: 31,\n channelIdOverride: 250,\n messageClass: BC_CLASS_MODERN_24,\n extensionXml: `<?xml version=\"1.0\" encoding=\"UTF-8\" ?><Extension version=\"1.1\"><channelId>251</channelId></Extension>`,\n },\n },\n ];\n\n let lastCode: number | undefined;\n for (const a of attempts) {\n const frame = await this.client.sendFrame({\n ...a.params,\n timeoutMs: 10_000,\n });\n lastCode = frame.header.responseCode;\n if (frame.header.responseCode === 200) {\n this.client.subscribed = true;\n this.client.refreshKeepAlive?.();\n return;\n }\n // Keep trying other variants.\n (this.logger.debug ?? this.logger.log).call(\n this.logger,\n `[ReolinkBaichuanApi] subscribeEvents rejected (${a.label}) responseCode=${frame.header.responseCode}`,\n );\n }\n\n this.client.subscribed = false;\n this.client.refreshKeepAlive?.();\n throw new Error(\n `subscribeEvents failed: camera rejected cmdId=31 (last responseCode=${lastCode ?? \"unknown\"})`,\n );\n }\n\n /**\n * Subscribe to events for all \"own\" channels.\n *\n * Intended for NVR/HomeHub setups where multiple channels share the same host.\n *\n * Implementation notes:\n * - Send cmd_id=31 with empty body and 0-based header channel_id per channel.\n * - some firmwares instead accept a single push subscription (header channelId=251).\n */\n async subscribeToAllEvents(options?: {\n /** Max channels to probe when enumerating (default 16). */\n maxChannels?: number;\n /** Timeout for each subscribe attempt (default 10000ms). */\n timeoutMs?: number;\n }): Promise<void> {\n await this.client.login();\n\n const maxChannels = options?.maxChannels ?? 16;\n const timeoutMs = options?.timeoutMs ?? 10_000;\n\n // Prefer channels discovered via cmd_id 145 push.\n // Filter out empty slots (state==none) and cap to maxChannels when present.\n const channels = Array.from(this.channelPushData.entries())\n .filter(([, v]) => (v.stateLower ?? v.state).toLowerCase() !== \"none\")\n .map(([ch]) => ch)\n .filter((ch) => Number.isFinite(ch))\n .sort((a, b) => a - b)\n .slice(0, maxChannels);\n\n let okCount = 0;\n let lastCode: number | undefined;\n for (const channel of channels) {\n const frame = await this.client.sendFrame({\n cmdId: 31,\n channelIdOverride: Number(channel),\n messageClass: BC_CLASS_MODERN_24,\n timeoutMs,\n });\n lastCode = frame.header.responseCode;\n if (frame.header.responseCode === 200) {\n okCount++;\n } else {\n (this.logger.debug ?? this.logger.log).call(\n this.logger,\n `[ReolinkBaichuanApi] subscribeToAllEvents rejected (channel=${channel}) responseCode=${frame.header.responseCode}`,\n );\n }\n }\n\n if (okCount > 0) {\n this.client.subscribed = true;\n this.client.refreshKeepAlive?.();\n return;\n }\n\n // Fallback for firmwares that only accept a single push subscription.\n try {\n await this.subscribeEvents();\n return;\n } catch {\n // ignore; throw consolidated error below.\n }\n\n this.client.subscribed = false;\n this.client.refreshKeepAlive?.();\n throw new Error(\n `subscribeToAllEvents failed: camera rejected cmdId=31 for all channels (last responseCode=${lastCode ?? \"unknown\"})`,\n );\n }\n\n /**\n * Unsubscribe from events.\n */\n async unsubscribeEvents(): Promise<void> {\n // Note: There's no explicit unsubscribe, but closing connection unsubscribes\n // For now, we just mark as unsubscribed\n this.client.subscribed = false;\n // For BCUDP/battery cameras: allow the camera to sleep when idle.\n this.client.refreshKeepAlive?.();\n }\n\n /**\n * Check current motion and AI state and dispatch events if state changed.\n * This is called immediately after subscription and periodically during polling.\n */\n private async checkAndDispatchCurrentState(\n channel: number = 0,\n ): Promise<void> {\n try {\n await this.checkAndDispatchMotionState(channel);\n await this.checkAndDispatchAiState(channel);\n } catch (e) {\n // Log but don't throw - state checking should be best-effort\n const msg = formatErrorForLog(e);\n (this.logger.warn ?? this.logger.error)?.call(\n this.logger,\n `[ReolinkBaichuanApi] Error checking current state (ch=${channel})${formatClientIoForLog(this)}: ${msg}`,\n );\n }\n }\n\n private async checkAndDispatchMotionState(channel: number): Promise<void> {\n let motionState: boolean;\n try {\n motionState = await this.getMotionState(channel);\n } catch (error) {\n // NOTE: Scrypted logger often JSON-stringifies Error -> \"{}\".\n // Log an explicit message so we can identify failing endpoints (cmdId=46).\n const msg = formatErrorForLog(error);\n (this.logger.warn ?? this.logger.error)?.call(\n this.logger,\n `[ReolinkBaichuanApi] getMotionState failed (cmdId=46 ch=${channel})${formatClientIoForLog(this)}: ${msg}`,\n );\n return;\n }\n\n if (motionState !== this.lastMotionState) {\n this.lastMotionState = motionState;\n if (motionState) {\n const event: ReolinkSimpleEvent = {\n type: \"motion\",\n channel,\n timestamp: Date.now(),\n };\n this.dispatchSimpleEvent(event);\n }\n }\n }\n\n private async checkAndDispatchAiState(channel: number): Promise<void> {\n if (this.aiStatePollingDisabled) return;\n\n try {\n const aiState = await this.getAiState(channel);\n if (!aiState || aiState.alarm_state === undefined) return;\n\n const aiStateChanged =\n !this.lastAiState ||\n this.lastAiState.alarm_state !== aiState.alarm_state ||\n this.lastAiState.support !== aiState.support;\n\n if (!aiStateChanged) return;\n\n this.lastAiState = aiState;\n if (aiState.alarm_state === 1) {\n const event: ReolinkSimpleEvent = {\n type: \"other\",\n channel,\n timestamp: Date.now(),\n };\n this.dispatchSimpleEvent(event);\n }\n } catch (error) {\n // Some firmwares/NVRs reject cmd 342 and may also tear down the TCP session.\n // To avoid a reconnect loop, disable AI polling after the first failure.\n this.aiStatePollingDisabled = true;\n\n const msg = formatErrorForLog(error);\n if (!msg.includes(\"not supported\") && !msg.includes(\"unsupported\")) {\n (this.logger.debug ?? this.logger.log)?.call(\n this.logger,\n \"[ReolinkBaichuanApi] getAiState failed; disabling AI polling\",\n error,\n );\n }\n\n if (!this.aiStatePollingDisabledLogged) {\n this.aiStatePollingDisabledLogged = true;\n this.logger.debug?.(\n \"[ReolinkBaichuanApi] AI polling disabled after getAiState failure\",\n error,\n );\n }\n }\n }\n\n /**\n * Start periodic polling of motion and AI state (every 5 seconds).\n * Only starts if there are listeners and polling is not already running.\n * Polling is disabled for UDP/battery cameras to avoid waking them unnecessarily.\n */\n private startStatePolling(): void {\n // Polling is opt-in: default is push-only.\n // Keep this guard here even if callsites already check it.\n const pollingEnabled = this.client.isStatePollingEnabled?.() ?? false;\n if (!pollingEnabled) {\n this.stopStatePolling();\n return;\n }\n\n // Only poll if there are listeners\n if (this.simpleEventListeners.size === 0) {\n return;\n }\n\n // Multi-channel hosts (NVR/Home Hub) should rely on push events. Polling tends to be noisy\n // (and can trigger reconnect loops) especially when some channels are sleeping/offline.\n if (this.channelPushData.size > 1) {\n return;\n }\n\n // Don't poll for UDP/battery cameras - they should rely on event pushes only\n const isUdp = this.client.getTransport?.() === \"udp\";\n if (isUdp) {\n return;\n }\n\n // Don't start if already running\n if (this.statePollingInterval) {\n return;\n }\n\n // Poll every 5 seconds (TCP only)\n this.statePollingInterval = setInterval(async () => {\n try {\n // Only poll if there are still listeners\n if (this.simpleEventListeners.size === 0) {\n this.stopStatePolling();\n return;\n }\n\n const channel = this.client.getConfiguredChannel?.() ?? 0;\n await this.checkAndDispatchCurrentState(channel);\n } catch (e) {\n // Never allow polling errors to crash callers.\n const msg = formatErrorForLog(e);\n this.logger.debug?.(\n `[ReolinkBaichuanApi] state polling tick failed${formatClientIoForLog(this)}: ${msg}`,\n );\n }\n }, 5000);\n }\n\n /**\n * Stop periodic polling of motion and AI state.\n */\n private stopStatePolling(): void {\n if (this.statePollingInterval) {\n clearInterval(this.statePollingInterval);\n this.statePollingInterval = undefined;\n }\n }\n\n /**\n * UDP/battery cameras don't support safe active polling, but we still want sleeping/awake events.\n * This timer uses getSleepStatus() which is purely passive (no network requests).\n */\n private startUdpSleepInference(): void {\n if (this.simpleEventListeners.size === 0) {\n this.stopUdpSleepInference();\n return;\n }\n\n const isUdp = this.client.getTransport?.() === \"udp\";\n if (!isUdp) {\n this.stopUdpSleepInference();\n return;\n }\n\n if (this.udpSleepInferenceInterval) return;\n\n const pollOnce = () => {\n // Stop if transport changed or listeners are gone.\n if (\n this.simpleEventListeners.size === 0 ||\n this.client.getTransport?.() !== \"udp\"\n ) {\n this.stopUdpSleepInference();\n return;\n }\n\n const channel = this.client.getConfiguredChannel?.() ?? 0;\n const status = this.getSleepStatus({ channel });\n if (status.state === \"unknown\") return;\n\n const prev = this.udpLastInferredSleepStateByChannel.get(channel);\n this.udpLastInferredSleepStateByChannel.set(channel, status.state);\n\n // On first observation, only emit if sleeping (awake is the default and would be noisy).\n if (prev === undefined) {\n if (status.state === \"sleeping\") {\n this.dispatchSimpleEvent({\n type: \"sleeping\",\n channel,\n timestamp: Date.now(),\n });\n }\n return;\n }\n\n if (prev !== status.state) {\n this.dispatchSimpleEvent({\n type: status.state === \"sleeping\" ? \"sleeping\" : \"awake\",\n channel,\n timestamp: Date.now(),\n });\n }\n };\n\n // Run immediately for quicker feedback.\n pollOnce();\n\n this.udpSleepInferenceInterval = setInterval(() => {\n try {\n pollOnce();\n } catch (e) {\n // Never let inference crash callers.\n this.logger.debug?.(\n `[ReolinkBaichuanApi] udp sleep inference tick failed${formatClientIoForLog(this)}: ${formatErrorForLog(e)}`,\n );\n }\n }, this.udpSleepInferenceIntervalMs);\n }\n\n private stopUdpSleepInference(): void {\n if (this.udpSleepInferenceInterval) {\n clearInterval(this.udpSleepInferenceInterval);\n this.udpSleepInferenceInterval = undefined;\n }\n this.udpLastInferredSleepStateByChannel.clear();\n }\n\n /**\n * GetEvents via Baichuan (legacy - use subscribeEvents for real-time events).\n * cmd_id: 33 (Motion/AI/Visitor event)\n * Note: Events are typically pushed via cmd_id 33, not requested directly\n * Use subscribeEvents() to receive event pushes\n */\n async getEvents(channel?: number): Promise<Events> {\n // Note: Events are typically pushed, not requested\n // cmd_id 33 is used for event pushes, cmd_id 31 is for subscribing\n // This is a placeholder - actual implementation may need event subscription\n const cmdId = 33; // Event push\n const xml = await this.sendXml({\n cmdId,\n ...(channel !== undefined ? { channel } : {}),\n });\n const ch = this.normalizeChannel(channel);\n const nowMs = Date.now();\n\n return parseEventsFromGetEventsXml({ xml, channel: ch, nowMs });\n }\n\n /**\n * Get two-way audio capability via Baichuan.\n * cmd_id: 10 (checks if two-way audio is supported)\n * Returns true if two-way audio is available.\n *\n * Note: Both \"mixAudioStream\" and \"followVideoStream\" modes support two-way audio.\n * The difference is how audio is mixed with the video stream.\n */\n async getTwoWayAudioConfig(channel?: number): Promise<TwoWayAudioConfig> {\n const cmdId = 10; // Two-way audio check\n const xml = await this.sendXml({\n cmdId,\n ...(channel !== undefined ? { channel } : {}),\n });\n // Check for audioStreamMode - both mixAudioStream and followVideoStream support two-way audio\n const audioStreamMode = getXmlText(xml, \"audioStreamMode\");\n // Both modes support two-way audio, just different mixing strategies\n const enabled =\n audioStreamMode === \"mixAudioStream\" ||\n audioStreamMode === \"followVideoStream\";\n\n const config: TwoWayAudioConfig = {\n channel: channel ?? 0,\n enabled,\n };\n if (audioStreamMode) {\n config.mode = audioStreamMode;\n }\n return config;\n }\n\n /**\n * Start video stream via Baichuan protocol.\n * Video stream subscription implementation.\n *\n * Uses MSG_ID_VIDEO command with Preview XML payload containing:\n * - channelId: Channel ID (1-based)\n * - handle: Stream handle (0 for main, 256 for sub, 1024 for extern)\n * - streamType: Stream name (\"mainStream\", \"subStream\", \"externStream\")\n *\n * @param channel - Channel number (0-based)\n * @param profile - Stream profile (\"main\" | \"sub\" | \"ext\")\n * @param options - Optional settings including variant and dedicated client\n * @returns Promise that resolves when stream request is sent\n */\n async startVideoStream(\n channel?: number,\n profile: StreamProfile = \"sub\",\n options?: {\n /** Native-only: request TrackMix tele/autotrack variants (usually on NVR/Hub). */\n variant?: NativeVideoStreamVariant;\n /**\n * Dedicated client to use for this stream. If provided, the command is sent\n * on this client instead of the main API client. This is essential when using\n * dedicated sockets for streaming to avoid frame routing issues.\n */\n client?: BaichuanClient;\n },\n ): Promise<void> {\n const ch = this.normalizeChannel(channel);\n // Use the same 0-based channel_id everywhere (header, Extension, payload).\n const channelId = ch;\n\n const variant: NativeVideoStreamVariant = options?.variant ?? \"default\";\n\n // Use dedicated client if provided, otherwise use the main API client\n const targetClient = options?.client ?? this.client;\n\n // Map profile to handle and stream_type values.\n // handle: 0 for main, 256 for sub, 1024 for extern\n // streamType in header:\n // - 0 main/ext (default)\n // - 1 sub (default)\n // - 2 main (autotrack/telephoto)\n // - 3 sub (autotrack/telephoto)\n const profileConfig: Record<\n StreamProfile,\n { handle: number; streamType: number; streamName: string }\n > = {\n main: {\n handle: 0,\n streamType: variant === \"default\" ? 0 : 2,\n // For preview XML, keep the canonical stream name; the variant is selected via header streamType.\n streamName: \"mainStream\",\n },\n sub: {\n handle: 256,\n streamType: variant === \"default\" ? 1 : 3,\n streamName: \"subStream\",\n },\n ext: { handle: 1024, streamType: 0, streamName: \"externStream\" },\n };\n\n if (variant !== \"default\" && profile === \"ext\") {\n throw new Error(\n `Invalid native stream variant for profile: ${profile} (variant=${variant})`,\n );\n }\n\n const config = profileConfig[profile];\n if (!config) {\n throw new Error(`Invalid stream profile: ${profile}`);\n }\n if (!config.streamName) {\n throw new Error(\n `Stream name not found for profile: ${profile}, config: ${JSON.stringify(config)}`,\n );\n }\n\n // Build Preview XML payload\n // BcXml serializes as <body>...</body> with Preview inside\n // IMPORTANT: channelId is NOT in Preview XML - it's handled via channelId in header\n // The working format (response_code 200) is Preview WITHOUT channelId\n const streamName = config.streamName;\n // Debug: verify streamName is defined\n if (typeof streamName !== \"string\") {\n throw new Error(\n `streamName is not a string: ${typeof streamName}, value: ${streamName}, config: ${JSON.stringify(config)}`,\n );\n }\n const payloadXml = buildPreviewXml(config.handle, streamName, channelId);\n\n // PCAP-observed Hub/NVR Preview request for \"tele\" view uses Preview v1.1 and keeps header streamType=0.\n // We observed two distinct patterns:\n // - Sub tele: <channelId>CH</channelId> <handle>512+CH</handle> <streamType>mobileStream</streamType>\n // - Main tele: <channelId>CH</channelId> <handle>1024+CH</handle> <streamType>externStream</streamType>\n // Also, some firmwares appear to use 0-based CH, others 1-based.\n const telePreviewStreamType =\n variant === \"telephoto\" && profile === \"main\"\n ? \"externStream\"\n : variant === \"telephoto\" && profile === \"sub\"\n ? \"mobileStream\"\n : undefined;\n const teleHandleBase =\n variant === \"telephoto\" && profile === \"main\"\n ? 1024\n : variant === \"telephoto\" && profile === \"sub\"\n ? 512\n : undefined;\n const teleChannelIdCandidates =\n variant === \"telephoto\" &&\n telePreviewStreamType &&\n teleHandleBase !== undefined\n ? Array.from(\n new Set(\n [channelId, channelId + 1].filter(\n (n) => Number.isFinite(n) && n >= 0,\n ),\n ),\n )\n : [];\n\n // Log stream request details for debugging\n // if (this.logger?.log) {\n // try {\n // this.logger.log(\n // `[ReolinkBaichuanApi] startVideoStream REQUEST: channel=${ch}, profile=${profile}, variant=${variant}, streamType=${config.streamType}, handle=${config.handle}, streamName=${streamName}`\n // );\n // } catch {\n // // Ignore logging errors\n // }\n // }\n\n // Subscribe (MSG_ID_VIDEO, msg_num) BEFORE sending the command.\n // On some BCUDP/battery models, the start-stream request can sporadically timeout;\n // retry a few times and ensure we unsubscribe on failures.\n const isUdp = targetClient.getTransport?.() === \"udp\";\n const maxAttempts = isUdp ? 3 : 1;\n let lastError: unknown;\n\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n // NOTE: must be atomic. Two parallel startVideoStream() calls (e.g. composite wider+tele)\n // can otherwise pick the same msgNum and cause stream packet mixups.\n const msgNum = targetClient.reserveNextMsgNum();\n targetClient.subscribeVideoStream(BC_CMD_ID_VIDEO, msgNum);\n\n // Optimistically publish msgNum immediately so stream consumers can start filtering\n // even if the NVR/Hub takes a long time to reply to the start request.\n this.activeVideoMsgNums.set(`${ch}:${profile}:${variant}`, msgNum);\n\n try {\n const baseParams: Parameters<typeof targetClient.sendFrame>[0] = {\n cmdId: BC_CMD_ID_VIDEO,\n channel: ch,\n channelIdOverride: channelId,\n msgNumOverride: msgNum,\n extensionXml: buildChannelExtensionXml(channelId),\n payloadXml,\n messageClass: BC_CLASS_MODERN_24,\n streamType: config.streamType,\n // Some NVR firmwares are slow or flaky replying to the VIDEO start command.\n // The stream may still start, but waiting a bit longer reduces false timeouts.\n timeoutMs: 20_000,\n };\n\n // Try the PCAP-observed tele Preview v1.1 request first (and try both 0-based and 1-based channelId tags),\n // then fall back to the legacy request.\n let frame:\n | Awaited<ReturnType<typeof targetClient.sendFrame>>\n | undefined;\n if (\n teleChannelIdCandidates.length > 0 &&\n telePreviewStreamType &&\n teleHandleBase !== undefined\n ) {\n for (const teleChannelIdTag of teleChannelIdCandidates) {\n try {\n frame = await targetClient.sendFrame({\n ...baseParams,\n // Client traffic shows no Extension XML on VIDEO start.\n extensionXml: \"\",\n payloadXml: buildPreviewXmlV11({\n channelId: teleChannelIdTag,\n handle: teleHandleBase + teleChannelIdTag,\n streamType: telePreviewStreamType,\n }),\n streamType: 0,\n });\n break;\n } catch {\n // continue\n }\n }\n }\n if (!frame) frame = await targetClient.sendFrame(baseParams);\n\n // if (this.logger?.log) {\n // try {\n // this.logger.log(\n // `[ReolinkBaichuanApi] startVideoStream response: channel=${ch}, profile=${profile}, variant=${variant}, streamType=${config.streamType}, responseCode=${frame.header.responseCode}, msgNum=${frame.header.msgNum}`\n // );\n // } catch {\n // // Ignore logging errors\n // }\n // }\n\n if (frame.header.responseCode !== 200) {\n throw new Error(\n `Video stream request rejected (response_code ${frame.header.responseCode}). Expected response_code 200, camera returned ${frame.header.responseCode}`,\n );\n }\n\n // Remember msgNum so we can stop the stream with the same msgNum.\n this.activeVideoMsgNums.set(\n `${ch}:${profile}:${variant}`,\n frame.header.msgNum,\n );\n\n // Success.\n return;\n } catch (error) {\n lastError = error;\n try {\n targetClient.unsubscribeVideoStream(BC_CMD_ID_VIDEO, msgNum);\n } catch {\n // ignore\n }\n\n // If the request failed, clear the optimistic mapping.\n this.activeVideoMsgNums.delete(`${ch}:${profile}:${variant}`);\n\n if (attempt < maxAttempts) {\n await new Promise((resolve) => setTimeout(resolve, 250));\n continue;\n }\n }\n }\n\n throw lastError instanceof Error ? lastError : new Error(String(lastError));\n\n // Success - stream should start and frames will arrive as push events with cmd_id 3\n\n // Check for response code 200 (success)\n // Expect response_code: 200 in the reply\n // If response_code is not 200, the stream request was rejected\n // Note: sendXml doesn't expose response_code directly, but it throws on 400 errors\n // For video streaming, we might need to check the actual frame response_code\n }\n\n /**\n * Returns the msgNum associated with an active video stream subscription, if any.\n * This can be used by stream consumers to filter incoming cmd_id=3 frames and\n * avoid mixing multiple concurrent streams on the same Baichuan client.\n */\n getActiveVideoMsgNum(\n channel?: number,\n profile: StreamProfile = \"sub\",\n ): number | undefined {\n const ch = this.normalizeChannel(channel);\n return this.activeVideoMsgNums.get(`${ch}:${profile}:default`);\n }\n\n getActiveVideoMsgNumWithVariant(\n channel: number,\n profile: StreamProfile,\n variant: NativeVideoStreamVariant = \"default\",\n ): number | undefined {\n const ch = this.normalizeChannel(channel);\n return this.activeVideoMsgNums.get(`${ch}:${profile}:${variant}`);\n }\n\n /**\n * Stop video stream via Baichuan protocol.\n * Stop video stream subscription.\n *\n * Uses MSG_ID_VIDEO_STOP command with Preview XML payload (without stream_type).\n *\n * @param channel - Channel number (0-based)\n * @param profile - Stream profile (\"main\" | \"sub\" | \"ext\")\n * @param options - Optional settings including variant and dedicated client\n */\n async stopVideoStream(\n channel?: number,\n profile: StreamProfile = \"sub\",\n options?: {\n /** Native-only: stop TrackMix tele/autotrack variants (must match the started variant). */\n variant?: NativeVideoStreamVariant;\n /**\n * Dedicated client to use for this stream. If provided, the command is sent\n * on this client instead of the main API client. Must match the client used\n * in startVideoStream.\n */\n client?: BaichuanClient;\n },\n ): Promise<void> {\n const ch = this.normalizeChannel(channel);\n const channelId = ch;\n\n const variant: NativeVideoStreamVariant = options?.variant ?? \"default\";\n\n // Use dedicated client if provided, otherwise use the main API client\n const targetClient = options?.client ?? this.client;\n\n // Map profile to handle value\n const profileConfig: Record<\n StreamProfile,\n { handle: number; streamType: number }\n > = {\n main: { handle: 0, streamType: variant === \"default\" ? 0 : 2 },\n sub: { handle: 256, streamType: variant === \"default\" ? 1 : 3 },\n ext: { handle: 1024, streamType: 0 },\n };\n\n if (variant !== \"default\" && profile === \"ext\") {\n throw new Error(\n `Invalid native stream variant for profile: ${profile} (variant=${variant})`,\n );\n }\n\n const config = profileConfig[profile];\n\n const teleHandleBase =\n variant === \"telephoto\" && profile === \"main\"\n ? 1024\n : variant === \"telephoto\" && profile === \"sub\"\n ? 512\n : undefined;\n const teleChannelIdCandidates =\n variant === \"telephoto\" && teleHandleBase !== undefined\n ? Array.from(\n new Set(\n [channelId, channelId + 1].filter(\n (n) => Number.isFinite(n) && n >= 0,\n ),\n ),\n )\n : [];\n\n const key = `${ch}:${profile}:${variant}`;\n const msgNum = this.activeVideoMsgNums.get(key);\n this.activeVideoMsgNums.delete(key);\n\n // Send VIDEO_STOP with the same msg_num as VIDEO.\n // Some cameras don't reliably reply; treat this as best-effort with a short timeout.\n try {\n const attempts: Array<{\n extensionXml: string;\n payloadXml: string;\n streamType: number;\n }> = [];\n\n // Hub/NVR multifocal tele streams are started with Preview v1.1 + streamType=0 and a handle derived from channelId.\n if (teleChannelIdCandidates.length > 0 && teleHandleBase !== undefined) {\n for (const teleChannelIdTag of teleChannelIdCandidates) {\n const handle = teleHandleBase + teleChannelIdTag;\n attempts.push({\n extensionXml: \"\",\n payloadXml: buildPreviewStopXmlV11({\n channelId: teleChannelIdTag,\n handle,\n }),\n streamType: 0,\n });\n // Some firmwares accept v1.0 stop with channelId present.\n attempts.push({\n extensionXml: \"\",\n payloadXml: buildPreviewStopXml(handle, teleChannelIdTag),\n streamType: 0,\n });\n }\n }\n\n // Legacy stop.\n attempts.push({\n extensionXml: buildChannelExtensionXml(channelId),\n payloadXml: buildPreviewStopXml(config.handle, channelId),\n streamType: config.streamType,\n });\n\n // If we started tele via the hub path, streamType was 0; try that too.\n if (variant === \"telephoto\" && profile !== \"ext\") {\n attempts.push({\n extensionXml: buildChannelExtensionXml(channelId),\n payloadXml: buildPreviewStopXml(config.handle, channelId),\n streamType: 0,\n });\n }\n\n for (const a of attempts) {\n try {\n await targetClient.sendFrame({\n cmdId: BC_CMD_ID_VIDEO_STOP,\n channel: ch,\n channelIdOverride: channelId,\n extensionXml: a.extensionXml,\n payloadXml: a.payloadXml,\n messageClass: BC_CLASS_MODERN_24,\n streamType: a.streamType,\n ...(msgNum !== undefined ? { msgNumOverride: msgNum } : {}),\n timeoutMs: 2000,\n });\n break;\n } catch {\n // continue\n }\n }\n } catch {\n // ignore\n } finally {\n // IMPORTANT: startVideoStream subscribes (MSG_ID_VIDEO=3, msgNum) to filter push frames and\n // to drive keepalive/idle-disconnect decisions in BaichuanClient.\n // Always unsubscribe when stopping the stream, even if VIDEO_STOP times out.\n try {\n if (msgNum !== undefined)\n targetClient.unsubscribeVideoStream(BC_CMD_ID_VIDEO, msgNum);\n } catch {\n // ignore\n }\n }\n }\n\n // --------------------\n // PTZ Control APIs\n // --------------------\n\n /**\n * Get PTZ preset list via Baichuan.\n * cmd_id: 190 (MSG_ID_GET_PTZ_PRESET)\n *\n * @param channel - Channel number (0-based)\n * @returns Array of PTZ presets\n */\n async getPtzPresets(channel?: number): Promise<PtzPreset[]> {\n const ch = this.normalizeChannel(channel);\n // Use the same channel_id everywhere (header, Extension, payload).\n // This is 0-based.\n const channelId = ch;\n let xml = \"\";\n try {\n xml = await this.sendXml({\n cmdId: BC_CMD_ID_GET_PTZ_PRESET,\n channel: ch,\n channelIdOverride: channelId,\n extensionXml: buildChannelExtensionXml(channelId),\n messageClass: BC_CLASS_MODERN_24,\n streamType: 0,\n });\n } catch (e) {\n // Non-PTZ cameras commonly respond with `responseCode=400` and empty body.\n // Treat that as \"unsupported\" and return an empty list so higher-level flows\n // (e.g. device add / capability probing) can continue.\n const msg = e instanceof Error ? e.message : String(e);\n if (msg.includes(\"responseCode 400, empty body\")) return [];\n throw e;\n }\n\n const parsed = this.parsePtzPresetList(xml);\n const presets: PtzPreset[] = parsed\n // Expose only enabled presets (enable=\"1\" or not present). Disabled presets should not show up in UI.\n .filter((p) => p.enable === undefined || p.enable === \"1\")\n // Treat empty name as deleted/disabled.\n .filter((p) => p.name === undefined || String(p.name).trim() !== \"\")\n .map((p) => ({\n id: p.id,\n name: p.name ?? `Preset ${p.id}`,\n }));\n\n return presets;\n }\n\n private parsePtzPresetList(\n xml: string,\n ): Array<{ id: number; name?: string; enable?: string }> {\n const parsed: Array<{ id: number; name?: string; enable?: string }> = [];\n const presetMatches = xml.matchAll(/<preset\\b[^>]*>([\\s\\S]*?)<\\/preset>/gi);\n for (const match of presetMatches) {\n const presetXml = match[1] ?? \"\";\n const idText = /<id>([^<]*)<\\/id>/i.exec(presetXml)?.[1];\n if (!idText) continue;\n const id = Number(idText);\n if (!Number.isFinite(id)) continue;\n const nameMatch = /<name>([^<]*)<\\/name>/i.exec(presetXml);\n const enableMatch = /<enable>([^<]*)<\\/enable>/i.exec(presetXml);\n\n const entry: { id: number; name?: string; enable?: string } = { id };\n const name = nameMatch?.[1];\n const enable = enableMatch?.[1];\n if (name !== undefined) entry.name = name;\n if (enable !== undefined) entry.enable = enable;\n parsed.push(entry);\n }\n return parsed;\n }\n\n private async getPtzPresetsRaw(\n channel: number,\n ): Promise<Array<{ id: number; name?: string; enable?: string }>> {\n const ch = this.normalizeChannel(channel);\n const channelId = ch;\n let xml = \"\";\n try {\n xml = await this.sendXml({\n cmdId: BC_CMD_ID_GET_PTZ_PRESET,\n channel: ch,\n channelIdOverride: channelId,\n extensionXml: buildChannelExtensionXml(channelId),\n messageClass: BC_CLASS_MODERN_24,\n streamType: 0,\n });\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n if (msg.includes(\"responseCode 400, empty body\")) return [];\n throw e;\n }\n return this.parsePtzPresetList(xml);\n }\n\n /**\n * Send PTZ control command (pan/tilt/zoom).\n * cmd_id: 18 (MSG_ID_PTZ_CONTROL)\n *\n * @param channel - Channel number (0-based)\n * @param command - PTZ command\n * @returns Promise that resolves when command is sent\n */\n async ptz(command: PtzCommand): Promise<void>;\n async ptz(channel: number, command: PtzCommand): Promise<void>;\n async ptz(\n channelOrCommand: number | PtzCommand,\n command?: PtzCommand,\n ): Promise<void> {\n const ch =\n typeof channelOrCommand === \"number\"\n ? this.normalizeChannel(channelOrCommand)\n : 0;\n const resolvedCommand =\n typeof channelOrCommand === \"number\" ? command! : channelOrCommand;\n // Use the same channel_id in meta header, Extension and payload XML.\n // This is 0-based.\n const channelId = ch;\n\n const direction = resolvePtzDirection(resolvedCommand);\n const speed = resolvePtzSpeed(direction, resolvedCommand.speed);\n\n const payloadXml = buildPtzControlXml(channelId, direction, speed);\n\n // Include Extension with channel_id for PTZ commands.\n const extensionXml = buildChannelExtensionXml(channelId);\n\n // Subscribe before sending PTZ commands\n // However, sendFrame already handles the response via pending map using cmdId:messageKey\n // The subscribe is for routing responses, which sendFrame already does\n // So we don't need explicit subscribeVideoStream here\n\n // Use sendFrame to check response_code (expects 200)\n const frame = await this.client.sendFrame({\n cmdId: BC_CMD_ID_PTZ_CONTROL,\n channel: ch,\n channelIdOverride: channelId,\n extensionXml,\n payloadXml,\n messageClass: BC_CLASS_MODERN_24,\n streamType: 0,\n });\n\n if (frame.header.responseCode !== 200) {\n const errorDetails = extractFrameErrorDetails({\n client: this.client,\n frame,\n });\n throw new Error(\n `PTZ control rejected (response_code ${frame.header.responseCode})${errorDetails}`,\n );\n }\n\n // If action is \"start\", send a stop after a short delay.\n // Some integrations need to tune the movement amount per command.\n if (resolvedCommand.action === \"start\" && direction !== \"stop\") {\n const autoStopMs = resolvedCommand.autoStopMs ?? 500;\n if (autoStopMs > 0) {\n setTimeout(() => {\n this.ptz(ch, {\n action: \"stop\",\n command: resolvedCommand.command,\n }).catch(() => {\n // Ignore stop errors\n });\n }, autoStopMs);\n }\n }\n }\n\n /**\n * Move to PTZ preset position.\n * cmd_id: 19 (MSG_ID_PTZ_CONTROL_PRESET)\n *\n * @param channel - Channel number (0-based)\n * @param presetId - Preset ID\n */\n async moveToPtzPreset(presetId: number, channel?: number): Promise<void>;\n async moveToPtzPreset(channel: number, presetId: number): Promise<void>;\n async moveToPtzPreset(arg1: number, arg2?: number): Promise<void> {\n // If 2 args are provided, interpret as (channel, presetId).\n const ch =\n arg2 === undefined\n ? this.normalizeChannel(undefined)\n : this.normalizeChannel(arg1);\n const presetId = arg2 === undefined ? arg1 : arg2;\n const channelId = ch;\n const payloadXml = buildPtzPresetXml(channelId, presetId, \"toPos\");\n\n // Include extension with channel_id for PTZ preset commands\n const extensionXml = buildChannelExtensionXml(channelId);\n\n const frame = await this.client.sendFrame({\n cmdId: BC_CMD_ID_PTZ_CONTROL_PRESET,\n channel: ch,\n channelIdOverride: channelId,\n extensionXml,\n payloadXml,\n messageClass: BC_CLASS_MODERN_24,\n streamType: 0,\n });\n\n if (frame.header.responseCode !== 200) {\n throw new Error(\n `PTZ preset move rejected (response_code ${frame.header.responseCode})`,\n );\n }\n }\n\n /**\n * Save current position as PTZ preset.\n * cmd_id: 19 (MSG_ID_PTZ_CONTROL_PRESET)\n *\n * @param channel - Channel number (0-based)\n * @param presetId - Preset ID\n * @param name - Preset name\n */\n async setPtzPreset(\n presetId: number,\n name: string,\n channel?: number,\n ): Promise<void>;\n async setPtzPreset(\n channel: number,\n presetId: number,\n name: string,\n ): Promise<void>;\n async setPtzPreset(\n arg1: number,\n arg2: number | string,\n arg3?: string | number,\n ): Promise<void> {\n const ch =\n typeof arg2 === \"string\"\n ? this.normalizeChannel(arg3 as number | undefined)\n : this.normalizeChannel(arg1);\n const presetId = typeof arg2 === \"string\" ? arg1 : (arg2 as number);\n const name = typeof arg2 === \"string\" ? arg2 : (arg3 as string);\n const channelId = ch;\n // Important: some firmwares will keep a deleted preset \"disabled\" (enable=0) and will omit it from cmd190.\n // Sending enable=1 ensures the slot becomes visible again.\n const payloadXml = buildPtzPresetXmlV2(channelId, presetId, \"setPos\", {\n name,\n enable: 1,\n });\n\n // Include extension with channel_id for PTZ preset commands\n const extensionXml = buildChannelExtensionXml(channelId);\n\n const frame = await this.client.sendFrame({\n cmdId: BC_CMD_ID_PTZ_CONTROL_PRESET,\n channel: ch,\n channelIdOverride: channelId,\n extensionXml,\n payloadXml,\n messageClass: BC_CLASS_MODERN_24,\n streamType: 0,\n });\n\n if (frame.header.responseCode !== 200) {\n throw new Error(\n `PTZ preset save rejected (response_code ${frame.header.responseCode})`,\n );\n }\n }\n\n /**\n * Best-effort delete/disable a PTZ preset.\n *\n * Note: firmware behavior varies. Many cameras include an <enable> flag in the preset list.\n * This method attempts to set enable=0 for the preset.\n */\n async deletePtzPreset(presetId: number, channel?: number): Promise<void>;\n async deletePtzPreset(channel: number, presetId: number): Promise<void>;\n async deletePtzPreset(arg1: number, arg2?: number): Promise<void> {\n // If 2 args are provided, interpret as (channel, presetId).\n const ch =\n arg2 === undefined\n ? this.normalizeChannel(undefined)\n : this.normalizeChannel(arg1);\n const presetId = arg2 === undefined ? arg1 : arg2;\n const channelId = ch;\n\n const extensionXml = buildChannelExtensionXml(channelId);\n\n // Grab current name (if any). Some firmwares will only accept disable when the name is preserved.\n let currentName: string | undefined;\n try {\n const before = await this.getPtzPresetsRaw(ch);\n currentName = before.find((p) => p.id === presetId)?.name;\n } catch {\n // ignore\n }\n\n const attempts = buildDeletePtzPresetAttempts({\n channelId,\n presetId,\n ...(currentName ? { currentName } : {}),\n });\n\n const { any200, lastError } = await runDeletePtzPresetAttempts({\n attempts,\n send: async (payloadXml, _label) => {\n const frame = await this.client.sendFrame({\n cmdId: BC_CMD_ID_PTZ_CONTROL_PRESET,\n channel: ch,\n channelIdOverride: channelId,\n extensionXml,\n payloadXml,\n messageClass: BC_CLASS_MODERN_24,\n streamType: 0,\n });\n\n return { responseCode: frame.header.responseCode };\n },\n verify: async () => {\n // Verify removal/disable. Some firmwares return 200 but do not apply the change.\n // Important: consider enable=0 or empty name as \"deleted\" even if the slot still exists.\n const after = await this.getPtzPresetsRaw(ch);\n const entry = after.find((p) => p.id === presetId);\n return isPresetEffectivelyDeleted(entry);\n },\n });\n\n // Many firmwares accept the request (200) but ignore it; don't block the caller.\n // The plugin can still hide the preset by removing it from the enabled list.\n if (any200) {\n this.logger.warn(\n \"PTZ presets (baichuan): deletePtzPreset did not take effect (firmware ignored request)\",\n {\n channel: ch,\n channelId,\n presetId,\n },\n );\n return;\n }\n\n throw lastError instanceof Error\n ? lastError\n : new Error(\"PTZ preset delete failed\");\n }\n\n /**\n * Get current PTZ position.\n * cmd_id: 433 (Get PTZ position)\n *\n * @param channel - Channel number (0-based)\n * @returns PTZ position (pan and tilt)\n */\n async getPtzPosition(channel?: number): Promise<PtzPosition> {\n const ch = this.normalizeChannel(channel);\n const xml = await this.sendXml({\n cmdId: BC_CMD_ID_GET_PTZ_POSITION,\n channel: ch,\n });\n\n const panText = getXmlText(xml, \"pPos\");\n const tiltText = getXmlText(xml, \"tPos\");\n\n const result: PtzPosition = {};\n if (panText !== undefined) {\n result.pan = Number(panText);\n }\n if (tiltText !== undefined) {\n result.tilt = Number(tiltText);\n }\n\n return result;\n }\n\n /**\n * Read zoom/focus min/max/current positions.\n * cmd_id: 294 (MSG_ID_GET_ZOOM_FOCUS)\n */\n async getZoomFocus(channel?: number): Promise<ZoomFocusStatus> {\n const ch = this.normalizeChannel(channel);\n const channelId = ch;\n const xml = await this.sendXml({\n cmdId: BC_CMD_ID_GET_ZOOM_FOCUS,\n channel: ch,\n channelIdOverride: channelId,\n extensionXml: buildChannelExtensionXml(channelId),\n messageClass: BC_CLASS_MODERN_24,\n streamType: 0,\n });\n\n const parseTriplet = (sectionTag: string): ZoomFocusTriplet | undefined => {\n const sectionMatch = new RegExp(\n `<${sectionTag}>([\\\\s\\\\S]*?)</${sectionTag}>`,\n ).exec(xml);\n const sectionXml = sectionMatch?.[1];\n if (!sectionXml) return undefined;\n const maxPos = getXmlText(sectionXml, \"maxPos\");\n const minPos = getXmlText(sectionXml, \"minPos\");\n const curPos = getXmlText(sectionXml, \"curPos\");\n if (maxPos === undefined || minPos === undefined || curPos === undefined)\n return undefined;\n return {\n maxPos: Number(maxPos),\n minPos: Number(minPos),\n curPos: Number(curPos),\n };\n };\n\n const out: ZoomFocusStatus = {};\n const zoom = parseTriplet(\"zoom\");\n const focus = parseTriplet(\"focus\");\n if (zoom) out.zoom = zoom;\n if (focus) out.focus = focus;\n return out;\n }\n\n /**\n * Zoom to a given zoom factor, where 1.0 is normal.\n * Uses movePos where 1000 == 1.0x.\n * cmd_id: 295 (MSG_ID_SET_ZOOM_FOCUS)\n */\n async zoomToFactor(zoomFactor: number, channel?: number): Promise<void>;\n async zoomToFactor(channel: number, zoomFactor: number): Promise<void>;\n async zoomToFactor(arg1: number, arg2?: number): Promise<void> {\n const ch = arg2 === undefined ? 0 : this.normalizeChannel(arg1);\n const zoomFactor = arg2 === undefined ? arg1 : arg2;\n const channelId = ch;\n const current = await this.getZoomFocus(ch);\n const zoom = current.zoom;\n if (!zoom) {\n throw new Error(\n \"Camera did not return <zoom> info (zoom may be unsupported)\",\n );\n }\n\n const requestedPos = Math.round(zoomFactor * 1000);\n const movePos = Math.min(zoom.maxPos, Math.max(zoom.minPos, requestedPos));\n\n const payloadXml = buildStartZoomFocusXml(channelId, movePos);\n const extensionXml = buildChannelExtensionXml(channelId);\n\n const frame = await this.client.sendFrame({\n cmdId: BC_CMD_ID_SET_ZOOM_FOCUS,\n channel: ch,\n channelIdOverride: channelId,\n extensionXml,\n payloadXml,\n messageClass: BC_CLASS_MODERN_24,\n streamType: 0,\n });\n\n if (frame.header.responseCode !== 200) {\n throw new Error(\n `Zoom rejected (response_code ${frame.header.responseCode})`,\n );\n }\n }\n\n // --------------------\n // Battery Info API\n // --------------------\n\n private parseBatteryInfoXml(\n xml: string,\n channel: number,\n ): Partial<BatteryInfo> {\n const parseNum = (v: string | undefined): number | undefined => {\n if (v === undefined) return undefined;\n const n = Number(v);\n return Number.isFinite(n) ? n : undefined;\n };\n\n // Prefer parsing the matching <BatteryInfo> block when a list is returned.\n const batteryInfoBlocks = getXmlBlocks(xml, \"BatteryInfo\");\n const preferredBlock =\n batteryInfoBlocks.find(\n (b) => getXmlText(b, \"channelId\") === String(channel),\n ) ??\n batteryInfoBlocks[0] ??\n xml;\n\n const out: Partial<BatteryInfo> = {};\n\n const batteryPercent = parseNum(\n getXmlText(preferredBlock, \"batteryPercent\"),\n );\n if (batteryPercent !== undefined) out.batteryPercent = batteryPercent;\n\n const chargeStatus = getXmlText(preferredBlock, \"chargeStatus\");\n if (chargeStatus !== undefined) out.chargeStatus = chargeStatus;\n\n const adapterStatus = getXmlText(preferredBlock, \"adapterStatus\");\n if (adapterStatus !== undefined) out.adapterStatus = adapterStatus;\n\n const voltage = parseNum(getXmlText(preferredBlock, \"voltage\"));\n if (voltage !== undefined) out.voltage = voltage;\n\n const current = parseNum(getXmlText(preferredBlock, \"current\"));\n if (current !== undefined) out.current = current;\n\n const temperature = parseNum(getXmlText(preferredBlock, \"temperature\"));\n if (temperature !== undefined) out.temperature = temperature;\n\n const lowPower = parseNum(getXmlText(preferredBlock, \"lowPower\"));\n if (lowPower !== undefined) out.lowPower = lowPower;\n\n const batteryVersion = parseNum(\n getXmlText(preferredBlock, \"batteryVersion\"),\n );\n if (batteryVersion !== undefined) out.batteryVersion = batteryVersion;\n\n return out;\n }\n\n /**\n * Best-effort sleeping inference for battery/BCUDP cameras.\n *\n * This method does NOT send any request to the camera.\n * Rule: consider the camera sleeping if, in the last 10 seconds,\n * we only received/sent Baichuan commands that are known to be non-waking.\n */\n getSleepStatus(opts?: {\n /** Window to inspect (ms). Default: 10_000. */\n windowMs?: number;\n /** Back-compat alias for `windowMs`. */\n idleMs?: number;\n channel?: number;\n /** List of cmdIds that do NOT wake the camera. If omitted, uses default values. */\n nonWakingCmdIds?: number[];\n /** Back-compat alias for `nonWakingCmdIds`. */\n ignoreCmdIds?: number[];\n }): SleepStatus {\n const windowMs = opts?.windowMs ?? opts?.idleMs ?? 10_000;\n const nonWakingCmdIds = new Set<number>(\n opts?.nonWakingCmdIds ??\n opts?.ignoreCmdIds ?? [\n BC_CMD_ID_UDP_KEEP_ALIVE,\n BC_CMD_ID_GET_BATTERY_INFO_LIST,\n BC_CMD_ID_GET_BATTERY_INFO,\n BC_CMD_ID_FLOODLIGHT_STATUS_LIST,\n ],\n );\n const transport = this.client.getTransport?.();\n if (transport !== \"udp\") {\n return {\n state: \"unknown\",\n reason: \"sleep inference supported only for UDP/battery\",\n };\n }\n\n // If we are actively streaming, treat the device as awake.\n // This check lives in the client and includes cross-client streaming activity within the same process.\n if (\n this.activeVideoMsgNums.size > 0 ||\n this.rtspServers.size > 0 ||\n this.client.isDeviceStreamingActive?.()\n ) {\n return { state: \"awake\", reason: \"active streaming\" };\n }\n\n const socketConnected = this.client.isSocketConnected?.() ?? false;\n\n const now = Date.now();\n const cutoff = now - windowMs;\n\n const rx = (this.client.getRxHistory?.() ?? []).filter(\n (h) => h.atMs >= cutoff,\n );\n const tx = (this.client.getTxHistory?.() ?? []).filter(\n (h) => h.atMs >= cutoff,\n );\n\n // If we've had absolutely no activity in the window, treat as sleeping (best-effort).\n // This matches the intent: no waking commands observed recently.\n if (rx.length === 0 && tx.length === 0) {\n return {\n state: \"sleeping\",\n reason: `no rx/tx activity in last ${windowMs}ms${socketConnected ? \"\" : \" (socket disconnected)\"}`,\n idleMs: windowMs,\n };\n }\n\n const firstWakingRx = rx.find((h) => !nonWakingCmdIds.has(h.cmdId));\n if (firstWakingRx) {\n return {\n state: \"awake\",\n reason: `waking rx cmdId=${firstWakingRx.cmdId} responseCode=${firstWakingRx.responseCode} seen ${now - firstWakingRx.atMs}ms ago`,\n lastRxAtMs: firstWakingRx.atMs,\n idleMs: now - firstWakingRx.atMs,\n };\n }\n\n const firstWakingTx = tx.find((h) => !nonWakingCmdIds.has(h.cmdId));\n if (firstWakingTx) {\n return {\n state: \"awake\",\n reason: `waking tx cmdId=${firstWakingTx.cmdId} seen ${now - firstWakingTx.atMs}ms ago`,\n idleMs: now - firstWakingTx.atMs,\n };\n }\n\n return {\n state: \"sleeping\",\n reason: `only non-waking cmdIds observed in last ${windowMs}ms (non-waking: ${Array.from(nonWakingCmdIds).join(\",\")})`,\n idleMs: windowMs,\n };\n }\n\n /**\n * Active sleep probe using a non-waking command with a short timeout.\n *\n * Why this exists:\n * - Passive inference can be noisy (keepalives, other clients, separate stream paths).\n * - A short-timeout probe answers: \"is the camera responding right now?\".\n *\n * Important caveats:\n * - If *another* client keeps the camera awake, the probe will return awake (correct: it's awake).\n * - Do NOT call this in a tight loop; it will generate traffic and can prevent sleep.\n */\n async probeSleepStatus(opts?: {\n channel?: number;\n /** Default: 700ms */\n timeoutMs?: number;\n /** Default: 1 */\n attempts?: number;\n /** Default: 5000ms (returns cached status if called more frequently) */\n minIntervalMs?: number;\n /** Override command used for probing. Default: battery info (253). */\n cmdId?: number;\n }): Promise<SleepStatus> {\n const transport = this.client.getTransport?.();\n if (transport !== \"udp\") {\n return {\n state: \"unknown\",\n reason: \"sleep probe supported only for UDP/battery\",\n };\n }\n\n // If we are actively streaming, treat the device as awake.\n // This check lives in the client and includes cross-client streaming activity within the same process.\n if (\n this.activeVideoMsgNums.size > 0 ||\n this.rtspServers.size > 0 ||\n this.client.isDeviceStreamingActive?.()\n ) {\n return { state: \"awake\", reason: \"active streaming\" };\n }\n\n const now = Date.now();\n const minIntervalMs = opts?.minIntervalMs ?? 5_000;\n if (this.lastSleepProbe && now - this.lastSleepProbe.atMs < minIntervalMs) {\n return {\n ...this.lastSleepProbe.status,\n reason: `${this.lastSleepProbe.status.reason} (cached)`,\n };\n }\n\n // Avoid implicitly forcing a login/reconnect as part of a \"sleep check\".\n if (!this.client.isSocketConnected()) {\n const status: SleepStatus = {\n state: \"unknown\",\n reason: \"udp socket not connected\",\n };\n this.lastSleepProbe = { atMs: now, status };\n return status;\n }\n if (!this.client.loggedIn) {\n const status: SleepStatus = { state: \"unknown\", reason: \"not logged in\" };\n this.lastSleepProbe = { atMs: now, status };\n return status;\n }\n\n const ch = this.normalizeChannel(opts?.channel);\n const timeoutMs = opts?.timeoutMs ?? 700;\n const attempts = Math.max(1, opts?.attempts ?? 1);\n const cmdId = opts?.cmdId ?? BC_CMD_ID_GET_BATTERY_INFO; // 253\n\n for (let i = 0; i < attempts; i++) {\n try {\n const frame = await this.client.sendFrame({\n cmdId,\n channel: ch,\n timeoutMs,\n });\n const status: SleepStatus = {\n state: frame.header.responseCode === 200 ? \"awake\" : \"unknown\",\n reason: `probe cmdId=${cmdId} responseCode=${frame.header.responseCode}`,\n };\n this.lastSleepProbe = { atMs: Date.now(), status };\n return status;\n } catch (e) {\n // On timeout, interpret as sleeping (best-effort). Other errors remain unknown.\n const msg = e instanceof Error ? e.message : String(e);\n const isTimeout =\n msg.includes(\"Baichuan timeout\") ||\n msg.toLowerCase().includes(\"timeout\");\n if (isTimeout) {\n const status: SleepStatus = {\n state: \"sleeping\",\n reason: `probe timeout cmdId=${cmdId} timeoutMs=${timeoutMs}`,\n };\n this.lastSleepProbe = { atMs: Date.now(), status };\n return status;\n }\n // Retry on transient errors if attempts > 1.\n if (i === attempts - 1) {\n const status: SleepStatus = {\n state: \"unknown\",\n reason: `probe error cmdId=${cmdId}: ${msg}`,\n };\n this.lastSleepProbe = { atMs: Date.now(), status };\n return status;\n }\n }\n }\n\n const fallback: SleepStatus = {\n state: \"unknown\",\n reason: \"probe exhausted\",\n };\n this.lastSleepProbe = { atMs: Date.now(), status: fallback };\n return fallback;\n }\n\n /**\n * Get battery status for battery-powered cameras, including sleep state.\n * This is a comprehensive API that returns battery info AND checks if the camera is sleeping.\n * cmd_id: 253 (MSG_ID_BATTERY_INFO)\n *\n * @param channel - Channel number (0-based)\n * @returns Battery information including sleep status\n */\n async getBatteryStatus(channel?: number): Promise<BatteryInfo> {\n const ch = this.normalizeChannel(channel);\n\n // First: no-wake inference. If we're likely sleeping, don't send any request.\n // This avoids the common pitfall where the \"sleep check\" itself prevents sleep.\n const sleepStatus = this.getSleepStatus({ channel: ch });\n if (sleepStatus.state === \"sleeping\") {\n return { channel: ch, sleeping: true };\n }\n\n try {\n // First, try to get battery info\n // If the camera is sleeping, this may timeout or fail\n const xml = await Promise.race([\n this.sendXml({ cmdId: BC_CMD_ID_GET_BATTERY_INFO, channel: ch }),\n new Promise<string>((_, reject) =>\n setTimeout(() => reject(new Error(\"Timeout\")), 5000),\n ),\n ]);\n\n const result: BatteryInfo = {\n channel: ch,\n sleeping: false, // Camera responded, so it's awake\n };\n\n Object.assign(result, this.parseBatteryInfoXml(xml, ch));\n\n return result;\n } catch (error) {\n // If the command times out or fails, the camera may be sleeping OR the path is broken.\n const result: BatteryInfo = {\n channel: ch,\n };\n\n const inferred = this.getSleepStatus({ channel: ch });\n if (inferred.state === \"sleeping\") result.sleeping = true;\n\n // If we got an error that's not a timeout, we still don't know the battery status\n // But we can infer it's sleeping if it failed to respond\n if (error instanceof Error && error.message === \"Timeout\") {\n // Camera didn't respond within 5 seconds, possibly sleeping\n if (result.sleeping == null) result.sleeping = true;\n } else {\n // Other error, but still mark as potentially sleeping\n }\n\n return result;\n }\n }\n\n /**\n * Get battery information via Baichuan.\n * cmd_id: 253 (MSG_ID_BATTERY_INFO)\n *\n * Note: Battery info can be pushed via events (cmd_id 252 BatteryInfoList), but on-demand request\n * is cmd_id 253.\n * For checking sleep state without polling/waking, use getSleepStatus() instead.\n *\n * @param channel - Channel number (0-based)\n * @returns Battery information\n */\n async getBatteryInfo(channel?: number): Promise<BatteryInfo> {\n const ch = this.normalizeChannel(channel);\n\n // IMPORTANT (battery/BCUDP): avoid forcing a reconnect/login just to fetch battery info.\n // Many battery cameras will deliberately drop the BCUDP session when sleeping (D2C_DISC).\n // If we auto-login here, the periodic poller will keep waking the camera.\n const transport = this.client.getTransport?.();\n if (transport === \"udp\") {\n if (!this.client.isSocketConnected?.() || !this.client.loggedIn) {\n return { channel: ch, sleeping: true };\n }\n }\n\n // Use the raw client call to avoid `ReolinkBaichuanApi.sendXml` auto-login + 400-empty-body relogin loop.\n const xml = await this.client.sendXml({\n cmdId: BC_CMD_ID_GET_BATTERY_INFO,\n channel: ch,\n });\n\n const result: BatteryInfo = {\n channel: ch,\n };\n\n Object.assign(result, this.parseBatteryInfoXml(xml, ch));\n\n return result;\n }\n\n /**\n * Wake up a sleeping battery camera by sending a \"waking command\".\n * WAKING_COMMANDS like GetEnc (cmd_id 56) can wake up sleeping cameras.\n *\n * @param channel - Channel number (0-based)\n * @param waitAfterWake - Optional delay in milliseconds after sending wake command (default: 1500ms)\n */\n async wakeUp(\n channel?: number,\n options?: number | WakeUpOptions,\n ): Promise<void> {\n const ch = this.normalizeChannel(channel);\n const opts: WakeUpOptions =\n typeof options === \"number\"\n ? { waitAfterWakeMs: options }\n : (options ?? {});\n\n const timeoutMs = opts.timeoutMs ?? 20_000;\n const attempts = opts.attempts ?? 3;\n const waitAfterWakeMs = opts.waitAfterWakeMs ?? 1500;\n const backoffMs = opts.backoffMs ?? 1500;\n\n const isUdp = this.client.getTransport?.() === \"udp\";\n const reconnect = opts.reconnect ?? isUdp;\n\n let lastError: unknown;\n for (let attempt = 1; attempt <= attempts; attempt++) {\n try {\n // Use GetEnc (cmd_id 56) which is a WAKING_COMMAND.\n // If the session is stale (common on battery/BCUDP), this may timeout.\n await this.getEncXml(ch, { timeoutMs });\n\n if (waitAfterWakeMs > 0) await sleepMs(waitAfterWakeMs);\n return;\n } catch (e) {\n lastError = e;\n\n // Common cases when the camera is sleeping or the session/socket is stale:\n // - timeout waiting for reply\n // - socket closed\n const msg = e instanceof Error ? e.message : String(e);\n const looksLikeTimeout = msg.includes(\"Baichuan timeout\");\n const looksLikeClosed =\n msg.toLowerCase().includes(\"socket closed\") ||\n msg.toLowerCase().includes(\"stream closed\");\n\n if (attempt < attempts) {\n if (reconnect && (looksLikeTimeout || looksLikeClosed)) {\n try {\n // Force a fresh connect+login on next attempt.\n this.client.loggedIn = false;\n await this.client.close();\n } catch {\n // ignore\n }\n }\n\n if (backoffMs > 0) await sleepMs(backoffMs);\n continue;\n }\n }\n }\n\n throw lastError instanceof Error ? lastError : new Error(String(lastError));\n }\n\n /**\n * Check if a camera is sleeping.\n *\n * This is difficult to determine directly. The method attempts to:\n * 1. Check if we can successfully get battery info (non-waking command)\n * 2. If that fails with timeout or connection error, the camera might be sleeping\n *\n * Note: GetBatteryInfo is a NONE_WAKING_COMMAND, so it won't wake up the camera.\n * However, if the camera is sleeping, it may timeout or fail to respond.\n *\n * @param channel - Channel number (0-based)\n * @returns true if camera appears to be sleeping, false otherwise\n */\n async isSleeping(channel?: number): Promise<boolean> {\n const ch = this.normalizeChannel(channel);\n try {\n // Try to get battery info (non-waking command)\n // If camera is sleeping, this should timeout or fail\n await Promise.race([\n this.getBatteryInfo(ch),\n new Promise((_, reject) =>\n setTimeout(() => reject(new Error(\"Timeout\")), 5000),\n ),\n ]);\n // If we got a response, camera is not sleeping\n return false;\n } catch (error) {\n // If we get a timeout or connection error, camera might be sleeping\n // However, it could also be a network issue or camera offline\n // We can't be 100% sure, but a timeout suggests sleeping\n return true;\n }\n }\n\n // --------------------\n // PIR State APIs\n // --------------------\n\n /**\n * Get PIR (Passive Infrared) detection settings via Baichuan.\n * cmd_id: 212 (MSG_ID_GET_PIR_ALARM)\n *\n * @param channel - Channel number (0-based)\n * @returns PIR state information\n */\n async getPirInfo(channel?: number): Promise<PirState> {\n const ch = this.normalizeChannel(channel);\n const xml = await this.sendXml({\n cmdId: BC_CMD_ID_GET_PIR_INFO,\n channel: ch,\n });\n return parsePirInfoFromXml({ xml, channel: ch });\n }\n\n /**\n * Set PIR (Passive Infrared) detection settings via Baichuan.\n * cmd_id: 213 (MSG_ID_START_PIR_ALARM)\n *\n * @param channel - Channel number (0-based)\n * @param params - PIR settings (enable is required)\n */\n async setPirInfo(\n params: {\n enable: number;\n sensitive?: number;\n reduceAlarm?: number;\n interval?: number;\n },\n channel?: number,\n ): Promise<void>;\n async setPirInfo(\n channel: number,\n params: {\n enable: number;\n sensitive?: number;\n reduceAlarm?: number;\n interval?: number;\n },\n ): Promise<void>;\n async setPirInfo(\n arg1:\n | number\n | {\n enable: number;\n sensitive?: number;\n reduceAlarm?: number;\n interval?: number;\n },\n arg2?:\n | number\n | {\n enable: number;\n sensitive?: number;\n reduceAlarm?: number;\n interval?: number;\n },\n ): Promise<void> {\n const channel =\n typeof arg1 === \"number\" ? arg1 : (arg2 as number | undefined);\n const params =\n typeof arg1 === \"number\"\n ? (arg2 as {\n enable: number;\n sensitive?: number;\n reduceAlarm?: number;\n interval?: number;\n })\n : arg1;\n const ch = this.normalizeChannel(channel);\n\n const toPirSensitivityRaw = (appValue: number): number => {\n // Inverse mapping of getPirInfo(): raw = 101 - app.\n return Math.trunc(101 - appValue);\n };\n\n const toBoolishNumber = (v: unknown): number | undefined => {\n if (v === undefined || v === null) return undefined;\n if (typeof v === \"boolean\") return v ? 1 : 0;\n const n = Number(v);\n return Number.isFinite(n) ? n : undefined;\n };\n\n // First get current settings to modify\n const currentXml = await this.sendXml({\n cmdId: BC_CMD_ID_GET_PIR_INFO,\n channel: ch,\n });\n\n // Parse and modify XML\n let modifiedXml = currentXml;\n\n if (params.enable !== undefined) {\n modifiedXml = modifiedXml.replace(\n /<enable>[^<]*<\\/enable>/,\n `<enable>${params.enable}</enable>`,\n );\n }\n if (params.sensitive !== undefined) {\n const raw = toPirSensitivityRaw(params.sensitive);\n modifiedXml = modifiedXml.replace(\n /<sensiValue>[^<]*<\\/sensiValue>/,\n `<sensiValue>${raw}</sensiValue>`,\n );\n }\n if (params.reduceAlarm !== undefined) {\n const n = toBoolishNumber(params.reduceAlarm);\n if (n !== undefined) {\n modifiedXml = modifiedXml.replace(\n /<reduceFalseAlarm>[^<]*<\\/reduceFalseAlarm>/,\n `<reduceFalseAlarm>${n}</reduceFalseAlarm>`,\n );\n }\n }\n if (params.interval !== undefined) {\n modifiedXml = modifiedXml.replace(\n /<interval>[^<]*<\\/interval>/,\n `<interval>${params.interval}</interval>`,\n );\n }\n\n await this.sendXml({\n cmdId: BC_CMD_ID_SET_PIR_INFO,\n channel: ch,\n payloadXml: modifiedXml,\n });\n }\n\n // --------------------\n // Motion Detection Set API\n // --------------------\n\n /**\n * Set motion detection settings via Baichuan.\n * cmd_id: 47 (SetMdAlarm)\n *\n * @param channel - Channel number (0-based)\n * @param enabled - Enable/disable motion detection\n * @param sensitivity - Sensitivity level (optional)\n */\n async setMotionDetection(\n enabled: boolean,\n sensitivity?: number,\n channel?: number,\n ): Promise<void>;\n async setMotionDetection(\n channel: number,\n enabled: boolean,\n sensitivity?: number,\n ): Promise<void>;\n async setMotionDetection(\n arg1: number | boolean,\n arg2?: boolean | number,\n arg3?: number,\n ): Promise<void> {\n const channel = typeof arg1 === \"number\" ? arg1 : arg3;\n const enabled = typeof arg1 === \"number\" ? (arg2 as boolean) : arg1;\n const sensitivity =\n typeof arg1 === \"number\" ? arg3 : (arg2 as number | undefined);\n const ch = this.normalizeChannel(channel);\n // First get current settings\n const currentXml = await this.sendXml({\n cmdId: BC_CMD_ID_GET_MOTION_ALARM,\n channel: ch,\n });\n\n // Parse and modify XML\n // Expected format: <sensInfoNew><enable>...</enable><sensitivityDefault>...</sensitivityDefault></sensInfoNew>\n let modifiedXml = currentXml;\n\n if (enabled !== undefined) {\n modifiedXml = modifiedXml.replace(\n /<enable>[^<]*<\\/enable>/,\n `<enable>${enabled ? \"1\" : \"0\"}</enable>`,\n );\n }\n if (sensitivity !== undefined) {\n modifiedXml = modifiedXml.replace(\n /<sensitivityDefault>[^<]*<\\/sensitivityDefault>/,\n `<sensitivityDefault>${sensitivity}</sensitivityDefault>`,\n );\n }\n\n await this.sendXml({\n cmdId: BC_CMD_ID_SET_MOTION_ALARM,\n channel: ch,\n payloadXml: modifiedXml,\n });\n }\n\n // --------------------\n // AI Detection Set API\n // --------------------\n\n /**\n * Set AI detection settings via Baichuan.\n * cmd_id: 343 (SetAiAlarm)\n *\n * @param channel - Channel number (0-based)\n * @param aiType - AI type (e.g., \"people\", \"vehicle\", \"dog_cat\", \"face\", \"package\")\n * @param sensitivity - Sensitivity level (optional)\n * @param stayTime - Stay time/delay (optional)\n */\n async setAiDetection(\n aiType: string,\n sensitivity?: number,\n stayTime?: number,\n channel?: number,\n ): Promise<void>;\n async setAiDetection(\n channel: number,\n aiType: string,\n sensitivity?: number,\n stayTime?: number,\n ): Promise<void>;\n async setAiDetection(\n arg1: number | string,\n arg2?: string | number,\n arg3?: number,\n arg4?: number,\n ): Promise<void> {\n const channel = typeof arg1 === \"number\" ? arg1 : arg4;\n const aiType = typeof arg1 === \"number\" ? (arg2 as string) : arg1;\n const sensitivity =\n typeof arg1 === \"number\" ? arg3 : (arg2 as number | undefined);\n const stayTime = typeof arg1 === \"number\" ? arg4 : arg3;\n const ch = this.normalizeChannel(channel);\n\n const resolvedAiType = await this.resolveAiTypeForSetAiDetection(\n ch,\n aiType,\n );\n\n // First get current settings for this AI type.\n // Correct cmd 342 payload: <AiDetectCfg><chn>0-based</chn><type>people</type></AiDetectCfg>\n const getXml = `<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n <body>\n <AiDetectCfg version=\"1.1\">\n <chn>${ch}</chn>\n <type>${xmlEscape(resolvedAiType)}</type>\n </AiDetectCfg>\n </body>`;\n\n const currentXml = await this.sendXml({\n cmdId: BC_CMD_ID_GET_AI_ALARM,\n channel: ch,\n payloadXml: getXml,\n });\n\n // Parse and modify XML\n let modifiedXml = currentXml;\n\n if (sensitivity !== undefined) {\n modifiedXml = modifiedXml.replace(\n /<sensitivity>[^<]*<\\/sensitivity>/,\n `<sensitivity>${sensitivity}</sensitivity>`,\n );\n }\n if (stayTime !== undefined) {\n modifiedXml = modifiedXml.replace(\n /<stayTime>[^<]*<\\/stayTime>/,\n `<stayTime>${stayTime}</stayTime>`,\n );\n }\n\n await this.sendXml({\n cmdId: BC_CMD_ID_SET_AI_ALARM,\n channel: ch,\n payloadXml: modifiedXml,\n });\n }\n\n // --------------------\n // Siren/Audio Alarm APIs\n // --------------------\n\n /**\n * Get siren/audio alarm status via Baichuan.\n * cmd_id: 547 (GetAudioAlarm - push event, not a request)\n *\n * Note: Siren status is typically pushed via events (cmd_id 547), not requested directly.\n * This method attempts to get the status, but it may not work on all cameras.\n *\n * @param channel - Channel number (0-based)\n * @returns Siren status\n */\n async getSiren(channel?: number): Promise<SirenState> {\n // Note: cmd_id 547 is typically a push event, not a request\n // We try to get it, but it may not work on all cameras\n try {\n const xml = await this.sendXml({\n cmdId: BC_CMD_ID_GET_AUDIO_ALARM,\n ...(channel !== undefined ? { channel } : {}),\n });\n\n // Parse siren status from XML\n // Expected format: <SirenStatus><status>...</status></SirenStatus>\n const status = getXmlText(xml, \"status\");\n return {\n enabled: status === \"1\",\n };\n } catch {\n // If request fails, return default (siren status is typically pushed, not requested)\n return { enabled: false };\n }\n }\n\n /**\n * Play siren/audio alarm via Baichuan.\n * cmd_id: 263 (MSG_ID_PLAY_AUDIO)\n *\n * @param channel - Channel number (0-based, optional for hub-level)\n * @param on - Enable/disable siren (for manual mode)\n * @param duration - Number of times to play (for times mode)\n */\n async setSiren(\n on?: boolean,\n duration?: number,\n channel?: number,\n ): Promise<void>;\n async setSiren(\n channel: number | undefined,\n on?: boolean,\n duration?: number,\n ): Promise<void>;\n async setSiren(\n arg1?: number | boolean,\n arg2?: boolean | number,\n arg3?: number,\n ): Promise<void> {\n const channel =\n typeof arg1 === \"boolean\" ? (arg3 ?? 0) : (arg1 as number | undefined);\n const on = typeof arg1 === \"boolean\" ? arg1 : (arg2 as boolean | undefined);\n const duration =\n typeof arg1 === \"boolean\" ? (arg2 as number | undefined) : arg3;\n\n const channelId = channel !== undefined ? channel + 1 : undefined;\n let payloadXml: string;\n\n if (duration !== undefined) {\n // Times mode: play siren a specific number of times\n payloadXml = buildSirenTimesXml(channelId, duration);\n } else {\n // Manual mode: turn siren on/off\n const enable = on ? 1 : 0;\n payloadXml = buildSirenManualXml(channelId, enable);\n }\n\n try {\n await this.sendXml({\n cmdId: BC_CMD_ID_AUDIO_ALARM_PLAY,\n ...(channel !== undefined ? { channel } : {}),\n payloadXml,\n });\n } catch (error) {\n // If manual mode fails, try times mode with 2 times\n if (on === true && duration === undefined) {\n payloadXml = buildSirenTimesXml(channelId, 2);\n await this.sendXml({\n cmdId: BC_CMD_ID_AUDIO_ALARM_PLAY,\n ...(channel !== undefined ? { channel } : {}),\n payloadXml,\n });\n } else {\n throw error;\n }\n }\n }\n\n // --------------------\n // White LED/Floodlight APIs\n // --------------------\n\n /**\n * Get white LED/floodlight state via Baichuan.\n * cmd_id: 289 (GetWhiteLed/Floodlight)\n *\n * @param channel - Channel number (0-based)\n * @returns White LED state\n */\n async getWhiteLedState(channel?: number): Promise<WhiteLedState> {\n const ch = this.normalizeChannel(channel);\n const xml = await this.sendXml({\n cmdId: BC_CMD_ID_GET_WHITE_LED,\n channel: ch,\n });\n return parseWhiteLedStateFromXml(xml);\n }\n\n /**\n * Set white LED/floodlight state via Baichuan.\n * cmd_id: 288 (SetWhiteLed state) or 290 (SetWhiteLed task)\n *\n * @param channel - Channel number (0-based)\n * @param on - Enable/disable white LED\n * @param brightness - Brightness level (optional)\n */\n async setWhiteLedState(\n on?: boolean,\n brightness?: number,\n channel?: number,\n ): Promise<void>;\n async setWhiteLedState(\n channel: number,\n on?: boolean,\n brightness?: number,\n ): Promise<void>;\n async setWhiteLedState(\n arg1?: number | boolean,\n arg2?: boolean | number,\n arg3?: number,\n ): Promise<void> {\n const channel = typeof arg1 === \"number\" ? arg1 : (arg3 ?? 0);\n const on =\n typeof arg1 === \"number\"\n ? (arg2 as boolean | undefined)\n : (arg1 as boolean | undefined);\n const brightness =\n typeof arg1 === \"number\" ? arg3 : (arg2 as number | undefined);\n const ch = this.normalizeChannel(channel);\n\n // Many firmwares use:\n // - cmd 288: FloodlightManual (write) for manual on/off\n // - cmd 290: FloodlightTask (write) for task config / brightness\n // Historically we sent a <WhiteLed> payload which can yield 400 on many cameras.\n if (on !== undefined) {\n try {\n const payloadXml = buildWhiteLedManualPayloadXml(ch, on);\n await this.sendXml({\n cmdId: BC_CMD_ID_SET_WHITE_LED_STATE,\n channel: ch,\n payloadXml,\n });\n } catch {\n // Fallback: use task XML returned by cmd 289, update <enable>/<state>/<status> and send with cmd 290.\n const currentXml = await this.sendXml({\n cmdId: BC_CMD_ID_GET_WHITE_LED,\n channel: ch,\n });\n const modifiedXml = applyWhiteLedOnOffToXml(currentXml, on);\n\n await this.sendXml({\n cmdId: BC_CMD_ID_SET_WHITE_LED_TASK,\n channel: ch,\n payloadXml: modifiedXml,\n });\n }\n }\n\n if (brightness !== undefined) {\n const currentXml = await this.sendXml({\n cmdId: BC_CMD_ID_GET_WHITE_LED,\n channel: ch,\n });\n const modifiedXml = applyWhiteLedBrightnessToXml(currentXml, brightness);\n\n await this.sendXml({\n cmdId: BC_CMD_ID_SET_WHITE_LED_TASK,\n channel: ch,\n payloadXml: modifiedXml,\n });\n }\n }\n\n /**\n * Get floodlight-on-motion state via FloodlightTask (cmdId=289).\n *\n * Returns whether the floodlight turns on automatically when motion is detected.\n * This is controlled by the `alarmMode` field in FloodlightTask.\n *\n * @param channel - Channel number (0-based)\n * @returns FloodlightTaskState with floodlightOnMotion, enabled, brightness, duration, detectType\n *\n * @example\n * const state = await api.getFloodlightOnMotion(0);\n * console.log(state.floodlightOnMotion); // true if floodlight turns on when motion detected\n */\n async getFloodlightOnMotion(channel?: number): Promise<FloodlightTaskState> {\n const ch = this.normalizeChannel(channel);\n const xml = await this.sendXml({\n cmdId: BC_CMD_ID_GET_WHITE_LED,\n channel: ch,\n });\n return parseFloodlightTaskFromXml(xml);\n }\n\n /**\n * Set floodlight-on-motion state via FloodlightTask (cmdId=290).\n *\n * Enables or disables the floodlight turning on automatically when motion is detected.\n * This modifies both `alarmMode` and `enable` fields in FloodlightTask.\n *\n * @param on - true to enable floodlight on motion, false to disable\n * @param channel - Channel number (0-based)\n *\n * @example\n * // Enable floodlight on motion\n * await api.setFloodlightOnMotion(true, 0);\n * // Disable floodlight on motion\n * await api.setFloodlightOnMotion(false, 0);\n */\n async setFloodlightOnMotion(on: boolean, channel?: number): Promise<void> {\n const ch = this.normalizeChannel(channel);\n\n // GET current FloodlightTask XML\n const currentXml = await this.sendXml({\n cmdId: BC_CMD_ID_GET_WHITE_LED,\n channel: ch,\n });\n\n // Modify alarmMode and enable fields\n const modifiedXml = applyFloodlightOnMotionToXml(currentXml, on);\n\n // SET via cmdId 290\n await this.sendXml({\n cmdId: BC_CMD_ID_SET_WHITE_LED_TASK,\n channel: ch,\n payloadXml: modifiedXml,\n });\n }\n\n /**\n * Set floodlight settings (duration, detectType, brightness) via FloodlightTask (cmdId=290).\n *\n * This allows configuring floodlight parameters without changing the enable state.\n *\n * @param channel - Channel number (0-based)\n * @param settings - Floodlight settings to apply\n *\n * @example\n * await api.setFloodlightSettings(0, {\n * duration: 300, // 5 minutes\n * detectType: 'people,vehicle',\n * brightness: 80,\n * });\n */\n async setFloodlightSettings(\n channel: number | undefined,\n settings: {\n duration?: number;\n detectType?: string;\n brightness?: number;\n },\n ): Promise<void> {\n const ch = this.normalizeChannel(channel);\n\n // GET current FloodlightTask XML\n const currentXml = await this.sendXml({\n cmdId: BC_CMD_ID_GET_WHITE_LED,\n channel: ch,\n });\n\n // Apply settings\n const modifiedXml = applyFloodlightSettingsToXml(currentXml, settings);\n\n // SET via cmdId 290\n await this.sendXml({\n cmdId: BC_CMD_ID_SET_WHITE_LED_TASK,\n channel: ch,\n payloadXml: modifiedXml,\n });\n }\n\n // --------------------\n // Ability Info API\n // --------------------\n\n /**\n * Get device abilities/capabilities via Baichuan.\n * cmd_id: 151 (MSG_ID_ABILITY_INFO)\n *\n * Returns a dictionary of device capabilities and their version numbers.\n * This is used to determine what features are supported by the device.\n *\n * The token used requests all available sections: system, streaming, PTZ, IO, security,\n * replay, disk, network, alarm, record, video, image.\n *\n * @param username - Username for the request (required)\n * @returns Dictionary of capability names to version numbers or values, keyed by channel number or \"Host\"\n */\n async getAbilityInfo(): Promise<\n Partial<\n Record<number | \"Host\", Record<string, number | string | undefined>>\n >\n > {\n // Return type matches DeviceAbilities from types.ts\n const user = this.client.username;\n const extensionXml = buildAbilityInfoExtensionXml(user);\n\n const xml = await this.sendXml({\n cmdId: BC_CMD_ID_ABILITY_INFO,\n extensionXml,\n });\n\n return parseAbilityInfoXml(xml);\n }\n\n /**\n * Get ability/capability version for a specific capability and channel.\n * This is a convenience method that wraps getAbilityInfo.\n *\n * @param username - Username for the request\n * @param capability - Capability name (e.g., \"reboot\", \"rtsp\", \"netPort\")\n * @param channel - Channel number (optional, None/null for host-level)\n * @returns Version number (0 = not supported, >0 = supported with that version)\n */\n async getAbilityVersion(\n capability: string,\n channel?: number | null,\n ): Promise<number> {\n const abilities = await this.getAbilityInfo();\n const channelKey: number | \"Host\" =\n channel !== undefined && channel !== null ? channel : \"Host\";\n const channelAbilities = abilities[channelKey];\n\n if (!channelAbilities) {\n return 0;\n }\n\n const value = channelAbilities[capability];\n if (typeof value === \"number\") {\n return value;\n }\n\n // If value is a string, try to extract version number\n if (typeof value === \"string\") {\n const numValue = Number(value);\n return Number.isNaN(numValue) ? 0 : numValue;\n }\n\n return 0;\n }\n\n /**\n * Get device support info via Baichuan.\n * cmd_id: 199 (MSG_ID_SUPPORT)\n *\n * Returns host-level support info including ptzMode and per-channel flags (battery, ledCtrl, etc).\n */\n async getSupportInfo(options?: {\n timeoutMs?: number;\n messageClass?: number;\n }): Promise<SupportInfo | undefined> {\n const xml = await this.sendXml({\n cmdId: BC_CMD_ID_SUPPORT,\n ...(options?.timeoutMs != null ? { timeoutMs: options.timeoutMs } : {}),\n ...(options?.messageClass != null\n ? { messageClass: options.messageClass }\n : {}),\n });\n return parseSupportXml(xml);\n }\n\n /**\n * Best-effort floodlight support probe via cmd 289 (GetWhiteLed/Floodlight).\n *\n * Note: this probes ONLY the provided channel (no ch+1 fallback).\n */\n async probeFloodlightSupportByCmd289(\n channel: number,\n options?: { timeoutMs?: number },\n ): Promise<boolean> {\n const ch = this.normalizeChannel(channel);\n // cmd 289 can be slow behind NVR/Hub; avoid false negatives due to timeouts.\n const timeoutMs = options?.timeoutMs ?? 2500;\n\n try {\n const xml = await this.sendXml({\n cmdId: BC_CMD_ID_GET_WHITE_LED,\n channel: ch,\n timeoutMs,\n });\n this.logger.debug(\n `probeFloodlightSupportByCmd289: received XML for channel ${ch}:\\n${xml}`,\n );\n\n return /(<FloodlightTask\\b|<FloodlightManual\\b|<FloodlightStatusList\\b|<WhiteLed\\b)/i.test(\n xml,\n );\n } catch {\n return false;\n }\n }\n\n /**\n * Probe autotracking support via AiCfg (cmd 299).\n *\n * Uses smartTrackMode > 0 as the indicator for autotracking capability.\n * This is more reliable than autoPt in SupportInfo which can be a false positive\n * (e.g., NVR channels report autoPt=1 but don't actually support autotracking).\n *\n * @param channel - Channel number (0-based)\n * @param options - Optional timeout\n * @returns true if autotracking is supported, false otherwise\n */\n async probeAutotrackingSupport(\n channel: number,\n options?: { timeoutMs?: number },\n ): Promise<boolean> {\n const ch = this.normalizeChannel(channel);\n const timeoutMs = options?.timeoutMs ?? 1500;\n\n try {\n const xml = await this.sendXml({ cmdId: 299, channel: ch, timeoutMs });\n const smartTrackModeRaw = getXmlText(xml, \"smartTrackMode\");\n const smartTrackMode = Number(smartTrackModeRaw ?? 0);\n return smartTrackMode > 0;\n } catch {\n return false;\n }\n }\n\n /**\n * Returns AI object-detection types for a channel via cmd 299 (AiCfg).\n *\n * Uses <detectType> as the source of truth and returns a normalized string list.\n */\n async getAiDetectTypes(\n channel: number,\n options?: { timeoutMs?: number },\n ): Promise<string[] | undefined> {\n const ch = this.normalizeChannel(channel);\n const timeoutMs = options?.timeoutMs ?? 1500;\n\n try {\n const xml = await this.sendXml({ cmdId: 299, channel: ch, timeoutMs });\n const detectTypeRaw = (getXmlText(xml, \"detectType\") ?? \"\").trim();\n if (!detectTypeRaw) return undefined;\n\n const list = detectTypeRaw\n .split(\",\")\n .map((s) => s.trim())\n .filter(Boolean);\n\n return list.length > 0 ? list : undefined;\n } catch {\n return undefined;\n }\n }\n\n /**\n * Get device capabilities for a specific channel.\n *\n * This method uses a simplified, deterministic approach:\n * - SupportInfo (cmd 199) is the single source of truth for most flags\n * - AbilityInfo (cmd 151) provides fallback for PTZ/intercom\n * - AI detection types come from cmd 299\n * - PTZ presets are probed only if ptzPreset > 0 in SupportInfo\n *\n * Results are cached for 5 minutes per channel.\n *\n * @param channel - Channel number (0-based). Defaults to 0.\n * @returns Device capabilities including abilities, support info, presets, and AI objects\n */\n async getDeviceCapabilities(\n channel?: number,\n ): Promise<DeviceCapabilitiesResult> {\n const ch = this.normalizeChannel(channel);\n\n // Check cache first\n const cached = this.deviceCapabilitiesCache.get(ch);\n if (\n cached &&\n Date.now() - cached.cachedAtMs <\n ReolinkBaichuanApi.CAPABILITIES_CACHE_TTL_MS\n ) {\n return cached.result;\n }\n\n // Fetch SupportInfo and AbilityInfo in parallel\n const [supportResult, abilitiesResult] = await Promise.allSettled([\n this.getSupportInfo({ timeoutMs: 5000 }),\n this.getAbilityInfo(),\n ]);\n\n const support =\n supportResult.status === \"fulfilled\" ? supportResult.value : undefined;\n const abilities =\n abilitiesResult.status === \"fulfilled\"\n ? abilitiesResult.value\n : undefined;\n\n // Find the best SupportItem for this channel\n const supportItem = this.pickBestSupportItem(support, ch);\n\n // Parse capabilities from SupportInfo (single source of truth)\n const capabilities = this.parseCapabilitiesFromSupport(\n ch,\n supportItem,\n support,\n abilities,\n );\n\n // Floodlight detection:\n // - If lightType >= 2: hasFloodlight = true (from parseCapabilitiesFromSupport)\n // - If ledCtrl > 0: hasFloodlight = true (NVR cameras report LED capabilities via ledCtrl bitmask)\n // - If lightType is undefined on standalone camera: probe cmd 289\n // - If lightType is 0 or 1 on standalone camera: hasFloodlight = false\n const item = supportItem as Record<string, unknown> | undefined;\n const lightType = item?.lightType as number | undefined;\n const ledCtrl = item?.ledCtrl as number | undefined;\n const ptzType = item?.ptzType as number | undefined;\n const supportVolume = item?.supportVolume as number | undefined;\n const supportPirSch = item?.supportPirSch as number | undefined;\n\n // Track if device is NVR for debug info\n const isNvr = await this.isNvrDevice();\n\n // For NVR: use ledCtrl > 0 as indicator (reliable for battery cameras like Argus)\n // For standalone: lightType >= 2 or probe if undefined\n if (isNvr) {\n // On NVR, ledCtrl indicates LED control capabilities for the connected camera\n capabilities.hasFloodlight = (ledCtrl ?? 0) > 0;\n } else if (lightType === undefined) {\n // Standalone camera with unknown lightType: probe cmd 289\n const probed = await this.probeFloodlightSupportByCmd289(ch, {\n timeoutMs: 2500,\n });\n capabilities.hasFloodlight = probed;\n }\n // else: lightType is defined, parseCapabilitiesFromSupport already set hasFloodlight\n\n // Build features from SupportInfo\n const features = this.parseFeaturesFromSupport(support);\n\n // Get AI detection types (cmd 299)\n const objects = await this.getAiDetectTypes(ch, { timeoutMs: 1500 });\n\n // Probe autotracking support via AiCfg (cmd 299)\n // smartTrackMode > 0 indicates the device truly supports autotracking\n // Note: autoPt in SupportInfo can be a false positive (e.g., NVR channels report autoPt=1\n // but aiTrack.ver=0 in CGI abilities and smartTrackMode=0 in AiCfg)\n const autotrackingProbed = await this.probeAutotrackingSupport(ch, {\n timeoutMs: 1500,\n });\n capabilities.hasAutotracking = autotrackingProbed;\n\n // Get PTZ presets if supported\n let presets: PtzPreset[] | undefined;\n if (capabilities.hasPresets) {\n try {\n presets = await this.getPtzPresets(ch);\n // Update hasPresets based on actual results\n capabilities.hasPresets = presets.length > 0;\n } catch {\n capabilities.hasPresets = false;\n }\n }\n\n // Build debug info with all entities used for capability detection\n const debug: DeviceCapabilitiesDebugInfo = {\n channel: ch,\n channelId1Based: ch + 1,\n transport: this.client.getTransport?.() ?? \"tcp\",\n encryptionKind: this.client.enc?.kind ?? \"none\",\n loggedIn: this.client.loggedIn,\n subscribed: this.client.subscribed,\n abilitiesAvailable: Boolean(abilities),\n supportAvailable: Boolean(support),\n isNvr,\n ...(lightType !== undefined && { lightType }),\n ...(ledCtrl !== undefined && { ledCtrl }),\n ...(ptzType !== undefined && { ptzType }),\n ...(supportVolume !== undefined && { supportVolume }),\n ...(supportPirSch !== undefined && { supportPirSch }),\n ...(supportItem?.chnID !== undefined && {\n supportItemChnID: supportItem.chnID,\n }),\n ...(abilities && {\n abilityMergedKeyCount: Object.keys(abilities).length,\n }),\n ...(support?.items && { supportItemCount: support.items.length }),\n };\n\n const result: DeviceCapabilitiesResult = {\n capabilities,\n debug,\n ...(abilities && { abilities }),\n ...(support && { support }),\n ...(presets && { presets }),\n ...(objects && { objects }),\n ...(features && { features }),\n };\n\n // Cache the result\n this.deviceCapabilitiesCache.set(ch, {\n result,\n cachedAtMs: Date.now(),\n });\n\n return result;\n }\n\n /**\n * Clear the device capabilities cache for a specific channel or all channels.\n */\n clearCapabilitiesCache(channel?: number): void {\n if (channel !== undefined) {\n this.deviceCapabilitiesCache.delete(channel);\n } else {\n this.deviceCapabilitiesCache.clear();\n }\n }\n\n /**\n * Pick the best SupportItem for a channel.\n * Prefers items without a name (capability items) over named items (googleHome, amazonAlexa).\n */\n private pickBestSupportItem(\n support: SupportInfo | undefined,\n channel: number,\n ): SupportItem | undefined {\n if (!support?.items?.length) return undefined;\n\n const candidates = support.items.filter((i) => i.chnID === channel);\n if (!candidates.length) return undefined;\n\n // Score items: prefer those without name and with more capability fields\n const score = (item: SupportItem): number => {\n const anyItem = item as Record<string, unknown>;\n let result = 0;\n // Prefer items without name (capability items vs named features)\n if (anyItem.name == null) result += 100;\n // Prefer items with more capability fields\n const capabilityKeys = [\n \"ptzType\",\n \"ptzControl\",\n \"ptzPreset\",\n \"ledCtrl\",\n \"lightType\",\n \"battery\",\n \"audioVersion\",\n \"motion\",\n \"encCtrl\",\n \"newIspCfg\",\n \"remoteAbility\",\n \"aitype\",\n \"videoClip\",\n \"snap\",\n ];\n for (const k of capabilityKeys) {\n if (anyItem[k] !== undefined) result += 3;\n }\n return result;\n };\n\n return candidates.sort((a, b) => score(b) - score(a))[0];\n }\n\n /**\n * Parse device capabilities from SupportInfo.\n * Uses SupportInfo as the single source of truth with AbilityInfo as fallback.\n */\n private parseCapabilitiesFromSupport(\n channel: number,\n supportItem: SupportItem | undefined,\n support: SupportInfo | undefined,\n abilities: DeviceAbilities | undefined,\n ): import(\"./types\").DeviceCapabilities {\n const truthy = (v: unknown): boolean => {\n if (typeof v === \"number\") return v > 0;\n if (typeof v === \"string\") {\n const n = Number(v);\n return Number.isFinite(n) ? n > 0 : v.length > 0 && v !== \"0\";\n }\n return Boolean(v);\n };\n\n const item = supportItem as Record<string, unknown> | undefined;\n const ptzMode = support?.ptzMode?.toLowerCase();\n\n // PTZ: from ptzType/ptzControl in SupportItem or ptzMode from SupportInfo\n const ptzType = item ? truthy(item.ptzType) : false;\n const ptzControl = item ? truthy(item.ptzControl) : false;\n const hasPtzFromItem = ptzType || ptzControl;\n const hasPtzFromMode = ptzMode\n ? ptzMode !== \"none\" && ptzMode !== \"0\"\n : false;\n\n // PTZ sub-capabilities from ptzMode\n const hasPanTilt = ptzMode\n ? ptzMode.includes(\"pt\") || ptzMode === \"ptz\"\n : hasPtzFromItem;\n const hasZoom = ptzMode ? ptzMode.includes(\"z\") : hasPtzFromItem;\n\n // Presets: from ptzPreset in SupportItem\n const hasPresets = item ? truthy(item.ptzPreset) : false;\n\n // Battery: from battery in SupportItem\n const hasBattery = item ? truthy(item.battery) : false;\n\n // Siren: from audioVersion or audioPlay ability\n // audioVersion > 0 indicates audio alarm support\n const hasSiren = item ? truthy(item.audioVersion) : false;\n\n // Floodlight: ONLY from lightType >= 2\n // lightType: 0 = no light, 1 = IR only, 2+ = controllable floodlight\n const lightType = item?.lightType;\n const hasFloodlight =\n typeof lightType === \"number\" ? lightType >= 2 : false;\n\n // PIR: from rfCfg, newRfCfg, or battery presence (battery cams often have PIR)\n const hasPir = item\n ? truthy(item.rfCfg) || truthy(item.newRfCfg) || truthy(item.rfVersion)\n : false;\n\n // Doorbell: from doorbellVersion in SupportItem\n const isDoorbell = item ? truthy(item.doorbellVersion) : false;\n\n // Intercom: from audioTalk in SupportInfo or ipcAudioTalk in SupportItem\n const hasIntercom =\n truthy(support?.audioTalk) || (item ? truthy(item.ipcAudioTalk) : false);\n\n return {\n channel,\n ...(ptzMode && { ptzMode }),\n hasPan: hasPanTilt,\n hasTilt: hasPanTilt,\n hasZoom,\n hasPresets,\n hasPtz: hasPtzFromItem || hasPtzFromMode || hasPanTilt || hasZoom,\n hasBattery,\n hasIntercom,\n hasSiren,\n hasFloodlight,\n hasPir,\n isDoorbell,\n // Autotracking: explicit flags only (autoPt or smartAI)\n // Note: the heuristic (ptzControl && aitype) was too aggressive and caused false positives\n // on cameras that have PTZ and AI detection but NOT autotracking capability.\n hasAutotracking: item\n ? truthy(item.autoPt) || truthy(item.smartAI)\n : false,\n };\n }\n\n /**\n * Parse support features from SupportInfo.\n */\n private parseFeaturesFromSupport(\n support: SupportInfo | undefined,\n ): DeviceSupportFlags | undefined {\n if (!support) return undefined;\n\n const truthy = (v: unknown): boolean => {\n if (typeof v === \"number\") return v > 0;\n if (typeof v === \"string\") {\n const n = Number(v);\n return Number.isFinite(n) ? n > 0 : v.length > 0 && v !== \"0\";\n }\n return Boolean(v);\n };\n\n return {\n rtsp: truthy(support.rtsp),\n onvif: truthy(support.onvif),\n wifi: truthy(support.wifi),\n record: truthy(support.record),\n ftp: truthy(support.ftp),\n email: truthy(support.email),\n pushAlarm: truthy(support.pushAlarm),\n audioTalk: truthy(support.audioTalk),\n };\n }\n\n /**\n * Analyzes channel capabilities for dual lens models.\n * Determines which channels support pan, tilt, zoom, motion detection, intercom, presets\n * and which streaming types are available (RTSP, RTMP, Native).\n *\n * @returns Detailed information about dual lens channels, including which channels support each capability\n *\n * @example\n * ```typescript\n * const analysis = await api.getDualLensChannelInfo();\n * if (analysis.isDualLens) {\n * // Get channels that support specific capabilities\n * const panChannels = analysis.capabilityChannels.pan; // [0, 1]\n * const zoomChannels = analysis.capabilityChannels.zoom; // [1] for TrackMix\n * const presetChannels = analysis.capabilityChannels.presets; // [0]\n *\n * // Send pan command to first channel that supports it\n * if (panChannels.length > 0) {\n * await api.ptzControl(panChannels[0], { pan: 1 });\n * }\n *\n * // Detailed info per channel\n * for (const ch of analysis.channels) {\n * console.log(`Channel ${ch.channel}: pan=${ch.hasPan}, tilt=${ch.hasTilt}, zoom=${ch.hasZoom}`);\n * console.log(` Motion: ${ch.hasMotion}, Intercom: ${ch.hasIntercom}, Presets: ${ch.hasPresets}`);\n * console.log(` Stream: RTSP=${ch.availableStreams.rtsp}, RTMP=${ch.availableStreams.rtmp}, Native=${ch.availableStreams.native}`);\n * }\n * }\n * ```\n */\n async getDualLensChannelInfo(\n channel: number,\n options?: {\n /** True when the camera is behind an NVR/Hub (tele lens is usually exposed as an autotrack/logic-channel variant). */\n onNvr?: boolean;\n },\n ): Promise<DualLensChannelAnalysis> {\n const onNvr = options?.onNvr === true;\n const baseChannel = this.normalizeChannel(channel);\n\n // 1. Get device information\n let model: string | undefined;\n let channelNum: number | undefined;\n let supportInfo: SupportInfo | undefined;\n\n try {\n const deviceInfo = await this.getInfo(channel, { tags: [\"type\"] });\n model = deviceInfo.type?.trim();\n } catch {\n // ignore\n }\n\n try {\n const capabilities = await this.getDeviceCapabilities(channel);\n channelNum = capabilities.support?.channelNum;\n supportInfo = capabilities.support;\n } catch {\n // ignore\n }\n\n // 2. Check if it's a dual lens model\n // Try multiple sources for model name\n let normalizedModel = model ? model.trim() : undefined;\n\n // If model not found via getInfo, try getDeviceCapabilities or SupportInfo\n if (!normalizedModel && supportInfo) {\n // SupportInfo might have model info in items\n for (const item of supportInfo.items ?? []) {\n const typeInfo = item[\"typeInfo\"];\n if (typeof typeInfo === \"string\" && typeInfo.trim()) {\n normalizedModel = typeInfo.trim();\n break;\n }\n }\n }\n\n // More flexible matching: check exact match first, then partial match\n const checkModelMatch = (\n knownModels: Set<string>,\n modelToCheck: string,\n ): boolean => {\n if (!modelToCheck || modelToCheck.length === 0) return false;\n const lower = modelToCheck.toLowerCase().trim();\n for (const known of knownModels) {\n const knownLower = known.toLowerCase().trim();\n // Exact match (case-insensitive)\n if (lower === knownLower) return true;\n // Partial match: model contains known or known contains model\n // Also check if model starts with known or vice versa\n if (lower.includes(knownLower) || knownLower.includes(lower))\n return true;\n // Check for key words: \"trackmix\" or \"duo\"\n if (lower.includes(\"trackmix\") && knownLower.includes(\"trackmix\"))\n return true;\n if (lower.includes(\"duo\") && knownLower.includes(\"duo\")) return true;\n }\n return false;\n };\n\n const isDualMotionModel = normalizedModel\n ? checkModelMatch(DUAL_LENS_DUAL_MOTION_MODELS, normalizedModel)\n : false;\n const isSingleMotionModel = normalizedModel\n ? checkModelMatch(DUAL_LENS_SINGLE_MOTION_MODELS, normalizedModel)\n : false;\n\n // Also check if channelNum suggests dual lens (2-3 channels)\n // Handle both number and string types for channelNum\n const channelNumValue =\n typeof channelNum === \"string\"\n ? Number.parseInt(channelNum, 10)\n : channelNum;\n const hasDualLensChannelCount =\n channelNumValue === 2 && Number.isFinite(channelNumValue);\n\n // Consider it dual lens if model matches OR if channelNum suggests it\n const isDualLens =\n isDualMotionModel || isSingleMotionModel || hasDualLensChannelCount;\n\n if (!isDualLens) {\n return {\n isDualLens: false,\n model: normalizedModel,\n streamChannelCount: channelNum,\n logicalChannelCount: channelNum,\n channels: [],\n capabilityChannels: {\n pan: [],\n tilt: [],\n zoom: [],\n motion: [],\n intercom: [],\n presets: [],\n },\n };\n }\n\n // 3. Determine dual lens type and available channels\n // If we detected via channelNum but model doesn't match, infer type from model name\n let dualLensType: \"dual_motion\" | \"single_motion\" | undefined =\n isDualMotionModel\n ? \"dual_motion\"\n : isSingleMotionModel\n ? \"single_motion\"\n : undefined;\n\n // If we detected via channelNum but model doesn't match known types exactly,\n // try to infer from model name pattern\n if (!dualLensType && hasDualLensChannelCount) {\n const modelLower = normalizedModel?.toLowerCase() ?? \"\";\n if (modelLower.includes(\"trackmix\")) {\n dualLensType = \"single_motion\";\n } else if (modelLower.includes(\"duo\")) {\n dualLensType = \"dual_motion\";\n } else if (channelNumValue === 2) {\n // Default to single_motion for 2-channel devices (TrackMix behavior)\n dualLensType = \"single_motion\";\n }\n }\n\n // For SINGLE_MOTION_MODELS (TrackMix): stream_channels=[0,1] but channels=[0]\n // For DUAL_MOTION_MODELS (Duo): different behavior\n const streamChannels: number[] = [];\n const logicalChannels: number[] = [];\n\n if (dualLensType === \"single_motion\") {\n // TrackMix: stream channels 0 and 1, but only channel 0 has motion/controls\n if (onNvr) {\n // NVR/Hub often exposes the tele lens as a variant on the same channel.\n streamChannels.push(baseChannel);\n } else {\n streamChannels.push(0, 1);\n }\n logicalChannels.push(onNvr ? baseChannel : 0);\n } else if (dualLensType === \"dual_motion\") {\n // Duo: both channels have motion detection\n if (channelNumValue === 2) {\n streamChannels.push(0, 1);\n logicalChannels.push(0, 1);\n } else {\n streamChannels.push(0);\n logicalChannels.push(0);\n }\n } else {\n // Fallback: use channelNum if available\n if (\n channelNumValue &&\n Number.isFinite(channelNumValue) &&\n channelNumValue >= 2\n ) {\n for (let i = 0; i < channelNumValue; i++) {\n streamChannels.push(i);\n logicalChannels.push(i);\n }\n } else {\n // Default: assume 2 channels\n streamChannels.push(0, 1);\n logicalChannels.push(0);\n }\n }\n\n // 4. Analyze each channel\n const channelInfos: DualLensChannelInfo[] = [];\n\n for (const ch of streamChannels) {\n try {\n // Get capabilities for this channel\n const chCapabilities = await this.getDeviceCapabilities(ch);\n const caps = chCapabilities.capabilities;\n const chSupport = chCapabilities.support;\n const chFeatures = chCapabilities.features;\n\n // Check motion detection\n // For SINGLE_MOTION: only channel 0 has motion\n // For DUAL_MOTION: both channels have motion\n let hasMotion = false;\n if (dualLensType === \"single_motion\") {\n hasMotion = ch === (onNvr ? baseChannel : 0); // Only main channel for TrackMix\n } else if (dualLensType === \"dual_motion\") {\n hasMotion = logicalChannels.includes(ch); // All logical channels for Duo\n } else {\n // Fallback: assume channel 0 has motion\n hasMotion = ch === 0;\n }\n\n // Check available streaming\n const availableStreams = {\n rtsp: false,\n rtmp: false,\n native: true, // Baichuan is always available\n };\n\n // RTSP: check from support or features\n if (chFeatures?.rtsp || chSupport?.rtsp) {\n availableStreams.rtsp = true;\n } else {\n // Try to verify if RTSP is available\n try {\n // RTSP is available if support.rtsp > 0\n const rtspVersion = chSupport?.rtsp;\n if (typeof rtspVersion === \"number\" && rtspVersion > 0) {\n availableStreams.rtsp = true;\n }\n } catch {\n // ignore\n }\n }\n\n // RTMP: check from support or features\n const rtmpRaw = chSupport ? chSupport[\"rtmp\"] : undefined;\n if (typeof rtmpRaw === \"number\" && rtmpRaw > 0) {\n availableStreams.rtmp = true;\n }\n\n const makeLensVariant = (\n lensType: \"wide\" | \"telephoto\",\n ): NativeVideoStreamVariant => {\n if (lensType === \"wide\") return \"default\";\n // For Hub/NVR multifocal (TrackMix), the tele lens is requested via the telephoto variant.\n // Autotrack is a separate mode and should not be used to select the tele stream.\n return \"telephoto\";\n };\n\n // For TrackMix (single_motion) models, channel 1 (telephoto) has optical zoom\n // even if capabilities don't explicitly report it\n let hasZoom = caps.hasZoom ?? false;\n if (!onNvr && dualLensType === \"single_motion\" && ch === 1) {\n // Telephoto lens on TrackMix has zoom capability\n hasZoom = true;\n }\n\n const pushInfo = (lensType?: \"wide\" | \"telephoto\"): void => {\n channelInfos.push({\n channel: ch,\n hasPan: caps.hasPan ?? false,\n hasTilt: caps.hasTilt ?? false,\n hasZoom,\n hasMotion,\n hasIntercom: caps.hasIntercom ?? false,\n hasPresets: caps.hasPresets ?? false,\n ...(lensType ? { lensType } : {}),\n ...(lensType ? { variantType: makeLensVariant(lensType) } : {}),\n availableStreams,\n });\n };\n\n // On NVR/Hub TrackMix (single_motion) the two lenses share the same channel: return both lenses with different variantType.\n if (onNvr && dualLensType === \"single_motion\" && ch === baseChannel) {\n pushInfo(\"wide\");\n // Tele lens entry: ensure zoom=true (TrackMix tele lens has zoom)\n channelInfos.push({\n channel: ch,\n hasPan: caps.hasPan ?? false,\n hasTilt: caps.hasTilt ?? false,\n hasZoom: true,\n hasMotion,\n hasIntercom: caps.hasIntercom ?? false,\n hasPresets: caps.hasPresets ?? false,\n lensType: \"telephoto\",\n variantType: makeLensVariant(\"telephoto\"),\n availableStreams,\n });\n } else if (ch === 0) {\n pushInfo(\"wide\");\n } else if (ch === 1) {\n pushInfo(\"telephoto\");\n } else {\n pushInfo(undefined);\n }\n } catch (err) {\n // If it fails for a channel, continue with the others\n (this.logger.warn ?? this.logger.log).call(\n this.logger,\n `[ReolinkBaichuanApi] getDualLensChannelInfo: error in channel ${ch}: ${err}`,\n );\n }\n }\n\n // Build capability channel maps: for each capability, list all channels that support it\n const uniq = (xs: number[]): number[] => Array.from(new Set(xs));\n const capabilityChannels = {\n pan: uniq(channelInfos.filter((ch) => ch.hasPan).map((ch) => ch.channel)),\n tilt: uniq(\n channelInfos.filter((ch) => ch.hasTilt).map((ch) => ch.channel),\n ),\n zoom: uniq(\n channelInfos.filter((ch) => ch.hasZoom).map((ch) => ch.channel),\n ),\n motion: uniq(\n channelInfos.filter((ch) => ch.hasMotion).map((ch) => ch.channel),\n ),\n intercom: uniq(\n channelInfos.filter((ch) => ch.hasIntercom).map((ch) => ch.channel),\n ),\n presets: uniq(\n channelInfos.filter((ch) => ch.hasPresets).map((ch) => ch.channel),\n ),\n };\n\n return {\n isDualLens: true,\n dualLensType,\n model: normalizedModel,\n streamChannelCount: streamChannels.length,\n logicalChannelCount: logicalChannels.length,\n channels: channelInfos,\n capabilityChannels,\n };\n }\n\n /**\n * Create an RTSP server for a video stream.\n * Automatically detects video codec (H.264 or H.265) and configures ffmpeg accordingly.\n *\n * @param channel - Channel number (0-based)\n * @param profile - Stream profile (\"main\", \"sub\", or \"ext\")\n * @param options - RTSP server options (port, path, etc.)\n * @returns RTSP server instance\n *\n * @example\n * ```typescript\n * const rtspServer = await api.createRtspStream(0, \"main\", { listenPort: 8554 });\n * const rtspUrl = rtspServer.getRtspUrl();\n * console.log(`RTSP stream available at: ${rtspUrl}`);\n * // Use ffmpeg or VLC to connect: ffmpeg -i ${rtspUrl} output.mp4\n * ```\n */\n async createRtspStream(\n profile: StreamProfile,\n options?: RtspCreateOptions,\n ): Promise<BaichuanRtspServer>;\n async createRtspStream(\n channel: number,\n profile: StreamProfile,\n options?: RtspCreateOptions,\n ): Promise<BaichuanRtspServer>;\n async createRtspStream(\n channelOrProfile: number | StreamProfile,\n profileOrOptions?: StreamProfile | RtspCreateOptions,\n optionsMaybe?: RtspCreateOptions,\n ): Promise<BaichuanRtspServer> {\n const isChannelOverload = typeof channelOrProfile === \"number\";\n const ch = isChannelOverload ? this.normalizeChannel(channelOrProfile) : 0;\n\n let profile: StreamProfile;\n let options: RtspCreateOptions | undefined;\n if (isChannelOverload) {\n if (typeof profileOrOptions !== \"string\") {\n throw new Error(\n \"createRtspStream(channel, profile, options): missing or invalid profile\",\n );\n }\n profile = profileOrOptions;\n options = optionsMaybe;\n } else {\n profile = channelOrProfile;\n options =\n typeof profileOrOptions === \"object\" && profileOrOptions !== null\n ? profileOrOptions\n : undefined;\n }\n\n // Get stream metadata to determine codec\n let videoCodec: string | undefined;\n try {\n const metadata = await this.getStreamMetadata(ch);\n const stream = metadata.streams.find((s) => s.profile === profile);\n if (stream?.videoEncType) videoCodec = stream.videoEncType;\n } catch (error) {\n // If metadata fetch fails, codec will be auto-detected from stream\n this.logger.warn(\n `[ReolinkBaichuanApi] Could not fetch stream metadata, will auto-detect codec: ${error instanceof Error ? error.message : error}`,\n );\n }\n\n const rtspOptions: BaichuanRtspServerOptions = {\n api: this,\n channel: ch,\n profile,\n ...(options?.variant !== undefined ? { variant: options.variant } : {}),\n ...(options?.listenHost !== undefined\n ? { listenHost: options.listenHost }\n : {}),\n ...(options?.listenPort !== undefined\n ? { listenPort: options.listenPort }\n : {}),\n ...(options?.path !== undefined ? { path: options.path } : {}),\n logger: this.logger,\n };\n\n const server = new BaichuanRtspServer(rtspOptions);\n await server.start();\n\n // Track the server for cleanup\n this.rtspServers.add(server);\n\n // Remove from tracking when server is stopped\n server.once(\"close\", () => {\n this.rtspServers.delete(server);\n });\n\n return server;\n }\n\n /**\n * Build all available video stream options for a channel.\n * Returns RTSP, RTMP, and native Baichuan stream options.\n *\n * @returns Array of stream options\n */\n async buildVideoStreamOptions(options?: {\n channel?: number;\n compositeOnly?: boolean;\n onNvr?: boolean;\n lens?: NativeVideoStreamVariant;\n }): Promise<ReolinkVideoStreamOptionsResult> {\n const onNvr = options?.onNvr === true;\n const channel = options?.channel;\n const compositeOnly = options?.compositeOnly === true;\n\n const lensVariant: NativeVideoStreamVariant = options?.lens ?? \"default\";\n const cacheKey = JSON.stringify({\n channel: channel ?? null,\n onNvr,\n compositeOnly,\n lens: lensVariant,\n });\n\n const cached = this.videoStreamOptionsCache.get(cacheKey);\n\n const isNonEmpty = (r: ReolinkVideoStreamOptionsResult) =>\n r.nativeStreams.length > 0 ||\n r.rtspStreams.length > 0 ||\n r.rtmpStreams.length > 0;\n\n const cacheOrFallback = (result: ReolinkVideoStreamOptionsResult) => {\n // Never overwrite a good cached value with empty/transient results.\n if (isNonEmpty(result)) {\n this.videoStreamOptionsCache.set(cacheKey, result);\n return result;\n }\n\n if (cached && isNonEmpty(cached)) {\n return cached;\n }\n\n return result;\n };\n\n const logDebug = (msg: string, data?: unknown): void => {\n this.logger.debug(msg, data);\n };\n\n const wantWide = lensVariant === \"default\";\n const wantTele = lensVariant !== \"default\";\n\n const rtspStreams: ReolinkSupportedStream[] = [];\n const rtmpStreams: ReolinkSupportedStream[] = [];\n const nativeStreams: ReolinkSupportedStream[] = [];\n\n const ch = this.normalizeChannel(channel);\n\n // Best-effort: detect TrackMix model for stream variants.\n // TrackMix can expose the tele stream as RTSP `...Preview_<ch>_autotrack` (especially on NVR/Hub).\n let isMultiFocal = false;\n let model: string | undefined;\n let isTrackMix = false;\n try {\n // NOTE: cmd_id 318 (per-channel GetDevInfo) is primarily for NVR channels and may fail on\n // standalone cameras. Prefer host-level cmd_id 80 when not on NVR.\n const info = await this.getInfo(onNvr ? ch : undefined, {\n tags: [\"type\"],\n });\n model = typeof info.type === \"string\" ? info.type.toLowerCase() : \"\";\n isMultiFocal = isDualLenseModel(model);\n isTrackMix = model.includes(\"trackmix\");\n } catch (e) {\n logDebug(\n \"[ReolinkBaichuanApi] buildVideoStreamOptions: getInfo(type) failed\",\n {\n host: this.host,\n onNvr,\n channel,\n normalizedChannel: ch,\n err: e instanceof Error ? e.message : String(e),\n },\n );\n }\n\n logDebug(\"[ReolinkBaichuanApi] buildVideoStreamOptions: inputs\", {\n host: this.host,\n onNvr,\n channel,\n normalizedChannel: ch,\n compositeOnly,\n lens: options?.lens ?? \"default\",\n wantWide,\n wantTele,\n detected: { isMultiFocal, isTrackMix, model },\n });\n\n // Empirical: TrackMix behind NVR/Hub can expose RTMP (bcs/live/vod) even though\n // standalone multifocal devices often do not. Enable RTMP for multifocal only when onNvr.\n const rtmpEnabledForMultifocal = onNvr;\n\n // For composite streams (multifocal devices), return composite stream options.\n // IMPORTANT: this branch is only for \"composite\" (channel-less) streams.\n // Multifocal devices still expose per-channel RTSP/RTMP streams on the NVR.\n if (compositeOnly && !isMultiFocal) {\n logDebug(\n \"[ReolinkBaichuanApi] buildVideoStreamOptions: compositeOnly requested but device not detected multifocal; returning empty\",\n {\n host: this.host,\n channel,\n normalizedChannel: ch,\n model,\n isMultiFocal,\n },\n );\n const result = {\n nativeStreams,\n rtmpStreams,\n rtspStreams,\n };\n\n return cacheOrFallback(result);\n }\n\n if (isMultiFocal && (compositeOnly || channel === undefined)) {\n let widerMetadata: ChannelStreamMetadata | undefined;\n try {\n widerMetadata = await this.getStreamMetadata(0);\n } catch (e) {\n logDebug(\n \"[ReolinkBaichuanApi] buildVideoStreamOptions: getStreamMetadata(0) failed\",\n {\n host: this.host,\n err: e instanceof Error ? e.message : String(e),\n },\n );\n }\n\n const widerStreams = widerMetadata?.streams || [];\n const widerMain = widerStreams.find((s) => s.profile === \"main\");\n const widerMainIsH264 =\n typeof widerMain?.videoEncType === \"string\"\n ? widerMain.videoEncType.toLowerCase().includes(\"264\")\n : false;\n logDebug(\n \"[ReolinkBaichuanApi] buildVideoStreamOptions: composite branch metadata\",\n {\n host: this.host,\n widerStreamsCount: widerStreams.length,\n profiles: widerStreams.map((s) => s.profile),\n widerMainIsH264,\n },\n );\n\n // Expose two composite stream options (main/sub).\n // IMPORTANT:\n // - Default wider lens uses `sub` to reduce drift.\n // - If wider `main` is H.264, allow preferring it for the composite `main` option.\n const widerSubProfile: StreamProfile = widerStreams.some(\n (s) => s.profile === \"sub\",\n )\n ? \"sub\"\n : ((widerStreams[0]?.profile as StreamProfile) ?? \"sub\");\n const widerMainProfileIfOk: StreamProfile | undefined =\n widerMainIsH264 && widerStreams.some((s) => s.profile === \"main\")\n ? \"main\"\n : undefined;\n\n const compositeProfiles: StreamProfile[] = [\"main\", \"sub\"];\n for (const teleProfile of compositeProfiles) {\n const effectiveWiderProfile: StreamProfile =\n teleProfile === \"main\" && widerMainProfileIfOk\n ? widerMainProfileIfOk\n : widerSubProfile;\n const widerSelectedMetadata =\n widerStreams.find((s) => s.profile === effectiveWiderProfile) ??\n widerStreams[0];\n\n const compositeUrl = new URL(\n `baichuan://${this.host}/composite/profile/${teleProfile}`,\n );\n const compositeUrlWithAuth = new URL(\n `baichuan://${this.host}/composite/profile/${teleProfile}`,\n );\n compositeUrlWithAuth.username = this.username;\n compositeUrlWithAuth.password = this.password;\n\n // Explicit source ids:\n // - composite-native-<variant>-<wider>-<tele>\n // - composite-rtsp-<variant>-<wider>-<tele>\n nativeStreams.push({\n name: `Native composite ${teleProfile}`,\n id: `composite-native-${lensVariant}-${effectiveWiderProfile}-${teleProfile}`,\n container: \"rtp\",\n profile: teleProfile,\n lens: \"composite\",\n url: compositeUrl.toString(),\n urlWithAuth: compositeUrlWithAuth.toString(),\n ...(widerSelectedMetadata ? { metadata: widerSelectedMetadata } : {}),\n });\n\n // Only advertise RTSP-input composite when:\n // - RTSP is enabled on the device\n // - and the selected profiles are likely H.264\n let rtspEnabled = false;\n try {\n const netPort = await this.getNetPort();\n rtspEnabled = netPort.rtsp?.enable === 1;\n } catch {\n rtspEnabled = false;\n }\n\n // (The server will enforce H.264-only anyway; this avoids listing known-bad combos.)\n // On NVR/Hub TrackMix, tele RTSP is often exposed only as `main` (e.g. Preview_XX_autotrack).\n // In that case, the *output* composite profile can still be `sub`, while the tele *input* profile is `main`.\n const teleRtspInputProfile: StreamProfile =\n onNvr && isTrackMix && teleProfile === \"sub\" ? \"main\" : teleProfile;\n\n const widerIsH264 =\n !!widerSelectedMetadata &&\n String(widerSelectedMetadata.videoEncType ?? \"\")\n .toLowerCase()\n .includes(\"264\");\n // Gate only on the *selected* wide stream encoding (main may be H.265 while sub is H.264).\n // The server will still enforce constraints; this just avoids hiding valid combos.\n const canRtsp = widerIsH264;\n if (rtspEnabled && canRtsp) {\n nativeStreams.push({\n name: `RTSP composite ${teleProfile}`,\n id: `composite-rtsp-${lensVariant}-${effectiveWiderProfile}-${teleRtspInputProfile}`,\n container: \"rtp\",\n profile: teleProfile,\n lens: \"composite\",\n url: compositeUrl.toString(),\n urlWithAuth: compositeUrlWithAuth.toString(),\n ...(widerSelectedMetadata\n ? { metadata: widerSelectedMetadata }\n : {}),\n });\n }\n }\n\n // Note: composite output is still \"native\" (baichuan://), but it can optionally use RTSP as *inputs*.\n\n logDebug(\n \"[ReolinkBaichuanApi] buildVideoStreamOptions: composite branch result\",\n {\n host: this.host,\n nativeStreams: nativeStreams.map((s) => s.id),\n },\n );\n\n return {\n nativeStreams,\n rtmpStreams,\n rtspStreams,\n };\n }\n\n const guessRtspEncodingPrefix = (m?: StreamMetadata): \"h264\" | \"h265\" => {\n const enc =\n typeof m?.videoEncType === \"string\" ? m.videoEncType.toLowerCase() : \"\";\n if (enc.includes(\"265\")) return \"h265\";\n if (enc.includes(\"264\")) return \"h264\";\n return \"h264\";\n };\n\n const pushRtsp = (params: {\n channel: number;\n profile: StreamProfile;\n streamName: string;\n metadata?: StreamMetadata;\n lens?: ReolinkSupportedStream[\"lens\"];\n /** Force unprefixed `/Preview_` path (used by autotrack). */\n forceNoEncodingPrefix?: boolean;\n }): void => {\n // RTSP format (Reolink):\n // - /<encoding>Preview_<NN>_<stream>\n // - some firmwares use /Preview_<NN>_<stream> (no encoding prefix)\n const channelStr = String(params.channel + 1).padStart(2, \"0\");\n const encoding = params.forceNoEncodingPrefix\n ? \"\"\n : guessRtspEncodingPrefix(params.metadata);\n const prefix = encoding ? `${encoding}` : \"\";\n const rtspId = `${prefix}Preview_${channelStr}_${params.streamName}`;\n const rtspPath = `/${rtspId}`;\n\n const rtspUrl = new URL(`rtsp://${this.host}:${rtspPort}${rtspPath}`);\n const rtspUrlWithAuth = new URL(\n `rtsp://${this.host}:${rtspPort}${rtspPath}`,\n );\n rtspUrlWithAuth.username = this.username;\n rtspUrlWithAuth.password = this.password;\n\n rtspStreams.push({\n name: `RTSP ${params.profile}`,\n id: rtspId,\n container: \"rtsp\",\n channel: params.channel,\n profile: params.profile,\n streamName: params.streamName,\n ...(params.lens ? { lens: params.lens } : {}),\n url: rtspUrl.toString(),\n urlWithAuth: rtspUrlWithAuth.toString(),\n path: rtspPath,\n port: rtspPort,\n ...(params.metadata ? { metadata: params.metadata } : {}),\n });\n };\n\n const pushRtmp = (params: {\n channel: number;\n profile: StreamProfile;\n streamName: string;\n metadata?: StreamMetadata;\n lens?: ReolinkSupportedStream[\"lens\"];\n }): void => {\n // RTMP format (Reolink): /bcs/channel<ch>_<stream>.bcs?channel=<ch>&stream=<0|1>&user=...&password=...\n const streamType = params.profile === \"sub\" ? 1 : 0;\n const rtmpId = `${params.streamName}.bcs`;\n const rtmpPath = `/bcs/channel${params.channel}_${params.streamName}.bcs`;\n\n const rtmpUrl = new URL(`rtmp://${this.host}:${rtmpPort}${rtmpPath}`);\n rtmpUrl.searchParams.set(\"channel\", params.channel.toString());\n rtmpUrl.searchParams.set(\"stream\", streamType.toString());\n\n const rtmpUrlWithAuth = new URL(\n `rtmp://${this.host}:${rtmpPort}${rtmpPath}`,\n );\n rtmpUrlWithAuth.searchParams.set(\"channel\", params.channel.toString());\n rtmpUrlWithAuth.searchParams.set(\"stream\", streamType.toString());\n rtmpUrlWithAuth.searchParams.set(\"user\", this.username);\n rtmpUrlWithAuth.searchParams.set(\"password\", this.password);\n\n rtmpStreams.push({\n name: `RTMP ${params.profile}`,\n id: rtmpId,\n container: \"rtmp\",\n channel: params.channel,\n profile: params.profile,\n streamName: params.streamName,\n ...(params.lens ? { lens: params.lens } : {}),\n url: rtmpUrl.toString(),\n urlWithAuth: rtmpUrlWithAuth.toString(),\n path: rtmpPath,\n port: rtmpPort,\n streamType,\n ...(params.metadata ? { metadata: params.metadata } : {}),\n });\n };\n\n // Get network ports (RTSP/RTMP configuration)\n const netPort = await this.getNetPort();\n const rtspEnabled = netPort.rtsp?.enable === 1;\n const rtmpEnabled =\n (rtmpEnabledForMultifocal ? true : !isMultiFocal) &&\n netPort.rtmp?.enable === 1;\n const rtspPort = netPort.rtsp?.port ?? 554;\n const rtmpPort = netPort.rtmp?.port ?? 1935;\n\n // Get stream metadata to build options\n const streamMetadata = await this.getStreamMetadata(ch);\n const streams = streamMetadata?.streams || [];\n\n // Standalone TrackMix tele lens calls typically come in as channel=1 + lens=telephoto.\n // In that case, `streams` already corresponds to the tele channel, so build directly from it.\n // (Previous logic only fetched tele metadata when ch===0, which made tele-only calls return empty.)\n const isStandaloneTeleRequest =\n wantTele && isMultiFocal && !onNvr && ch === 1;\n\n // TrackMix without NVR usually exposes the tele stream as channel 1.\n // For UX, keep a single call useful by adding tele streams when available.\n // On NVR/Hub, channel 1 often doesn't exist; we add the RTSP `autotrack` variant instead.\n let teleStreams: StreamMetadata[] = [];\n if (isMultiFocal && !onNvr && ch === 0) {\n try {\n const teleMetadata = await this.getStreamMetadata(1);\n teleStreams = teleMetadata?.streams || [];\n } catch {\n teleStreams = [];\n }\n }\n\n const pushNative = (params: {\n channel: number;\n profile: StreamProfile;\n metadata?: StreamMetadata;\n lens: \"wide\" | \"telephoto\";\n id: string;\n streamName: string;\n nativeVariant?: Exclude<NativeVideoStreamVariant, \"default\">;\n }): void => {\n const nativeUrl = new URL(\n `baichuan://${this.host}/channel/${params.channel}/profile/${params.profile}`,\n );\n const nativeUrlWithAuth = new URL(\n `baichuan://${this.host}/channel/${params.channel}/profile/${params.profile}`,\n );\n if (params.nativeVariant) {\n nativeUrl.searchParams.set(\"variant\", params.nativeVariant);\n nativeUrlWithAuth.searchParams.set(\"variant\", params.nativeVariant);\n }\n nativeUrlWithAuth.username = this.username;\n nativeUrlWithAuth.password = this.password;\n\n nativeStreams.push({\n // Keep names stable: when requesting a specific lens, callers expect a single native main/sub.\n name: `Native ${params.profile}`,\n id: params.id,\n container: \"rtp\",\n channel: params.channel,\n profile: params.profile,\n streamName: params.streamName,\n lens: params.lens,\n ...(params.nativeVariant\n ? { nativeVariant: params.nativeVariant }\n : {}),\n url: nativeUrl.toString(),\n urlWithAuth: nativeUrlWithAuth.toString(),\n ...(params.metadata ? { metadata: params.metadata } : {}),\n });\n };\n\n const buildStandardStreams = (params: {\n lens: \"wide\" | \"telephoto\";\n channel: number;\n metadatas: StreamMetadata[];\n includeRtsp: boolean;\n includeRtmp: boolean;\n includeNative: boolean;\n nativeIdPrefix: string;\n }): void => {\n for (const metadata of params.metadatas) {\n const profile = metadata.profile as StreamProfile;\n\n // Preserve existing behavior: multifocal skips ext (and generally exposes only main/sub).\n if (isMultiFocal && profile === \"ext\") continue;\n\n if (params.includeRtsp && profile !== \"ext\") {\n const streamName = profile === \"main\" ? \"main\" : \"sub\";\n pushRtsp({\n channel: params.channel,\n profile,\n streamName,\n metadata,\n lens: params.lens,\n });\n }\n\n if (params.includeRtmp) {\n const streamName =\n profile === \"main\" ? \"main\" : profile === \"sub\" ? \"sub\" : \"ext\";\n pushRtmp({\n channel: params.channel,\n profile,\n streamName,\n metadata,\n lens: params.lens,\n });\n }\n\n if (params.includeNative) {\n if (isMultiFocal && profile !== \"main\" && profile !== \"sub\") continue;\n pushNative({\n channel: params.channel,\n profile,\n metadata,\n lens: params.lens,\n // streamKey for RFC4571 server: channel_<ch>_<profile>\n id: `channel_${params.channel}_${profile}`,\n streamName: profile,\n });\n }\n }\n };\n\n if (wantWide) {\n // For TrackMix behind NVR/Hub, do NOT expose RTMP main by default:\n // empirical probes show `channelX_main.bcs` often does not exist, while sub/mobile do.\n const includeRtmpForWide =\n rtmpEnabled && !(isMultiFocal && onNvr && isTrackMix);\n\n buildStandardStreams({\n lens: \"wide\",\n channel: ch,\n metadatas: streams,\n includeRtsp: rtspEnabled,\n includeRtmp: includeRtmpForWide,\n includeNative: true,\n nativeIdPrefix: \"native\",\n });\n\n // Add explicit RTMP sub/mobile for NVR/Hub TrackMix.\n if (rtmpEnabled && isMultiFocal && onNvr && isTrackMix) {\n const subMeta = streams.find((s) => s.profile === \"sub\") ?? streams[0];\n // Wide sub stream\n pushRtmp({\n channel: ch,\n profile: \"sub\",\n streamName: \"sub\",\n ...(subMeta ? { metadata: subMeta } : {}),\n lens: \"wide\",\n });\n }\n }\n\n // Tele-only request on standalone (channel 1): build directly from channel 1 metadata.\n if (isStandaloneTeleRequest) {\n buildStandardStreams({\n lens: \"telephoto\",\n channel: 1,\n metadatas: streams,\n includeRtsp: rtspEnabled,\n includeRtmp: rtmpEnabled,\n includeNative: true,\n nativeIdPrefix: \"native\",\n });\n }\n\n // Add TrackMix tele streams when available (direct camera, channel 1).\n // NOTE: skip if we already handled the tele-only request above.\n if (\n !isStandaloneTeleRequest &&\n wantTele &&\n isMultiFocal &&\n teleStreams.length > 0\n ) {\n buildStandardStreams({\n lens: \"telephoto\",\n channel: 1,\n metadatas: teleStreams,\n includeRtsp: rtspEnabled,\n includeRtmp: rtmpEnabled,\n includeNative: true,\n // Lens-scoped: keep native IDs stable.\n nativeIdPrefix: \"native\",\n });\n }\n\n // Add TrackMix tele stream variant for NVR/Hub.\n // On many NVR/Hub firmwares the tele lens is exposed as RTSP `.../Preview_<NN>_autotrack`.\n if (wantTele && isMultiFocal && onNvr && rtspEnabled) {\n // IMPORTANT: TrackMix behind NVR/Hub often exposes the tele lens as the `autotrack` stream name.\n // Even if the caller conceptually wants \"telephoto\", the actual RTSP path is typically `_autotrack`.\n const rtspVariant: Exclude<NativeVideoStreamVariant, \"default\"> =\n // TrackMix behind NVR/Hub often uses RTSP streamName=autotrack for the tele lens.\n // Keep behavior consistent: requesting telephoto maps to autotrack at the RTSP path level.\n isTrackMix\n ? \"autotrack\"\n : lensVariant === \"telephoto\"\n ? \"telephoto\"\n : \"autotrack\";\n const mainMeta = streams.find((s) => s.profile === \"main\") ?? streams[0];\n const subMeta = streams.find((s) => s.profile === \"sub\") ?? streams[0];\n\n if (mainMeta) {\n pushRtsp({\n channel: ch,\n profile: \"main\",\n streamName: rtspVariant,\n metadata: mainMeta,\n lens: \"telephoto\",\n forceNoEncodingPrefix: true,\n });\n } else {\n pushRtsp({\n channel: ch,\n profile: \"main\",\n streamName: rtspVariant,\n lens: \"telephoto\",\n forceNoEncodingPrefix: true,\n });\n }\n\n // Note: do NOT expose `Preview_<NN>_autotrack_sub` by default.\n // Empirically, Hub/NVR TrackMix RTSP often exposes a single `_autotrack` only.\n }\n\n if (wantTele && isMultiFocal && onNvr && rtmpEnabled && isTrackMix) {\n // Empirical: Hub/NVR TrackMix RTMP tends to expose VOD-style streams like:\n // - channelX_autotrack_sub.bcs\n // - channelX_telephoto_sub.bcs\n // while `*_main` is often missing.\n const subMeta = streams.find((s) => s.profile === \"sub\") ?? streams[0];\n\n // IMPORTANT: do not return multiple RTMP aliases for the same profile.\n // - lens=telephoto -> prefer `telephoto_sub`\n // - lens=autotrack -> prefer `autotrack_sub`\n const teleRtmpName =\n lensVariant === \"telephoto\" ? \"telephoto_sub\" : \"autotrack_sub\";\n pushRtmp({\n channel: ch,\n profile: \"sub\",\n streamName: teleRtmpName,\n ...(subMeta ? { metadata: subMeta } : {}),\n lens: \"telephoto\",\n });\n }\n\n // Add TrackMix native tele stream variant for NVR/Hub.\n // Many firmwares expose the tele lens via a different Baichuan streamType (2/3).\n if (wantTele && isMultiFocal && onNvr) {\n const mainMeta = streams.find((s) => s.profile === \"main\") ?? streams[0];\n const subMeta = streams.find((s) => s.profile === \"sub\") ?? streams[0];\n\n const variantsToExpose: Array<\n Exclude<NativeVideoStreamVariant, \"default\">\n > = [lensVariant === \"telephoto\" ? \"telephoto\" : \"autotrack\"];\n\n for (const nativeVariant of variantsToExpose) {\n pushNative({\n channel: ch,\n profile: \"main\",\n ...(mainMeta ? { metadata: mainMeta } : {}),\n lens: \"telephoto\",\n // streamKey for RFC4571 server: channel_<ch>_<variant>_<profile>\n id: `channel_${ch}_${nativeVariant}_main`,\n streamName: nativeVariant,\n nativeVariant,\n });\n\n pushNative({\n channel: ch,\n profile: \"sub\",\n ...(subMeta ? { metadata: subMeta } : {}),\n lens: \"telephoto\",\n // streamKey for RFC4571 server: channel_<ch>_<variant>_<profile>\n id: `channel_${ch}_${nativeVariant}_sub`,\n streamName: nativeVariant,\n nativeVariant,\n });\n }\n }\n\n const result = {\n nativeStreams,\n rtmpStreams,\n rtspStreams,\n };\n\n return cacheOrFallback(result);\n }\n\n /**\n * Test all available streams for a specific channel.\n * Tests RTSP, RTMP, and native Baichuan streams with all profiles (main, sub, ext).\n *\n * @param channel - Channel number to test (0-based)\n * @param logger - Optional logger for output\n * @returns Test results for all stream types and profiles\n */\n async testChannelStreams(\n channel?: number,\n logger?: import(\"../../debug/DebugConfig\").Logger,\n ): Promise<Record<string, unknown>> {\n const { testChannelStreams } = await import(\"../../debug/DiagnosticsTools\");\n return await testChannelStreams({\n api: this,\n channel: this.normalizeChannel(channel),\n ...(logger !== undefined ? { logger } : {}),\n });\n }\n\n /**\n * Comprehensive diagnostics for multi-focal devices.\n * Tests all channels and all available streams for each channel.\n * Checks if support.channelNum is 2 or 3 and iterates all channels.\n *\n * @param logger - logger for output\n * @returns Complete diagnostics for all channels and streams\n */\n async collectMultifocalDiagnostics(\n logger: import(\"../../debug/DebugConfig\").Logger,\n ): Promise<Record<string, unknown>> {\n const { collectMultifocalDiagnostics } =\n await import(\"../../debug/DiagnosticsTools\");\n return await collectMultifocalDiagnostics({\n api: this,\n logger,\n });\n }\n\n // ====================================================================\n // CGI Passthrough Methods\n // These methods delegate to the internal CGI API (useful for NVR/Hub)\n // ====================================================================\n\n /**\n * Passthrough to ReolinkCgiApi.getAllChannelsEvents.\n * Fetches events/motion/AI state for all channels via CGI and merges results per channel.\n */\n async getAllChannelsEvents(\n options?: Parameters<ReolinkCgiApi[\"getAllChannelsEvents\"]>[0],\n ): ReturnType<ReolinkCgiApi[\"getAllChannelsEvents\"]> {\n await this.cgiApi.login();\n return await this.cgiApi.getAllChannelsEvents(options);\n }\n\n /**\n * Passthrough to ReolinkCgiApi.getAllChannelsBatteryInfo.\n * Fetches battery info for all channels via CGI (merged with channel status sleep flag).\n */\n async getAllChannelsBatteryInfo(\n options?: Parameters<ReolinkCgiApi[\"getAllChannelsBatteryInfo\"]>[0],\n ): ReturnType<ReolinkCgiApi[\"getAllChannelsBatteryInfo\"]> {\n await this.cgiApi.login();\n return await this.cgiApi.getAllChannelsBatteryInfo(options);\n }\n\n // ====================================================================\n // NVR/Hub Baichuan helpers\n // ====================================================================\n\n private getUidFromPushCacheForChannel(channel: number): string | undefined {\n const info = this.getPushCacheEntryForLogicalChannel(channel);\n const uid = typeof info?.uid === \"string\" ? info.uid.trim() : \"\";\n return uid ? uid : undefined;\n }\n\n // ====================================================================\n // VOD (Video On Demand) Passthrough Methods\n // These methods delegate to the internal CGI API for NVR/Hub VOD operations\n // ====================================================================\n\n /**\n\n /**\n * Prepare NVR VOD download by requesting file list for a time range.\n * Passthrough to ReolinkCgiApi.prepareNvrVodDownload for NVR/Hub support.\n *\n * @param channel - Channel number (0-based)\n * @param startTime - Start time object\n * @param endTime - End time object\n * @param streamType - Stream type (default: \"main\")\n * @param options - Optional parameters\n * @returns Filename for the prepared VOD file\n */\n async prepareNvrVodDownload(\n channel: number,\n startTime: {\n year: number;\n mon: number;\n day: number;\n hour: number;\n min: number;\n sec: number;\n },\n endTime: {\n year: number;\n mon: number;\n day: number;\n hour: number;\n min: number;\n sec: number;\n },\n streamType: string = \"main\",\n options?: {\n /** For multifocal cameras: logical channel (0 or 1) */\n iLogicChannel?: number;\n },\n ): Promise<string> {\n await this.cgiApi.login();\n return await this.cgiApi.prepareNvrVodDownload(\n channel,\n startTime,\n endTime,\n streamType,\n options,\n );\n }\n\n /**\n * Get URL for VOD playback, download, or streaming.\n * Passthrough to ReolinkCgiApi.getVodUrl for NVR/Hub support.\n *\n * @param filenameOrVodFile - Filename string or VodFile object from getVideoclips\n * @param channel - Channel number (0-based)\n * @param options - Optional parameters\n * @returns URL string\n */\n async getVodUrl(\n filenameOrVodFile: string | VodFile,\n channel: number,\n options?: GetVodUrlParams,\n ): Promise<string> {\n await this.cgiApi.login();\n return await this.cgiApi.getVodUrl(filenameOrVodFile, channel, options);\n }\n\n /**\n * Download a VOD file.\n * Passthrough to ReolinkCgiApi.downloadVod for NVR/Hub support.\n *\n * @param filename - Filename from getVideoclips or prepareNvrVodDownload\n * @param options - Optional download parameters\n * @returns Buffer containing the video file\n */\n async downloadVod(\n filename: string,\n options?: {\n /** Output filename */\n output?: string;\n /** Start time string */\n start?: string;\n },\n ): Promise<Buffer> {\n await this.cgiApi.login();\n return await this.cgiApi.downloadVod(filename, options);\n }\n\n // ====================================================================\n // CGI Videoclips Passthrough Methods\n // These methods use HTTP/CGI instead of native Baichuan protocol\n // ====================================================================\n\n /**\n * Search video clips using CGI API (HTTP).\n * Alternative to native getVideoclips() that uses HTTP instead of Baichuan protocol.\n * May be more reliable on some devices.\n *\n * @param params - Search parameters\n * @returns Array of RecordingFile objects\n */\n async getVideoclipsCgi(\n params: CgiGetVideoclipsParams,\n ): Promise<RecordingFile[]> {\n await this.cgiApi.login();\n return await this.cgiApi.getVideoclips(params);\n }\n\n /**\n * Extract thumbnail from a video clip using CGI API (HTTP + ffmpeg).\n * Alternative to native getVideoclipThumbnailJpeg() that uses HTTP VOD instead of CoverPreview.\n * May be more reliable on some devices.\n *\n * @param params - Parameters for thumbnail extraction\n * @returns JPEG buffer\n */\n async getVideoclipThumbnailJpegCgi(params: {\n /** Channel number (0-based) */\n channel: number;\n /** Recording filename or VodFile object */\n filename: string | VodFile;\n /** Path to ffmpeg executable */\n ffmpegPath: string;\n /** Timeout in milliseconds (default: 30000) */\n timeoutMs?: number;\n /** Seek position in seconds (default: 0) */\n seekSeconds?: number;\n }): Promise<Buffer> {\n await this.cgiApi.login();\n return await this.cgiApi.getVideoclipThumbnailJpeg(params);\n }\n\n /**\n * Comprehensive NVR/HUB diagnostics.\n * Calls collectNvrDiagnostics directly from DiagnosticsTools.\n * Automatically prints diagnostics after collection using the provided logger.\n *\n * @param options - Configuration object with logger property for progress messages\n * @returns Complete diagnostics data including NVR info, channels, and per-channel details\n */\n async collectNvrDiagnostics(options: {\n logger: Logger;\n }): Promise<Record<string, unknown>> {\n const diagnostics = await collectNvrDiagnostics({\n cgi: this.cgiApi,\n logger: options.logger,\n });\n return diagnostics;\n }\n\n // --------------------\n // PCAP-derived settings pushes\n // --------------------\n\n private parseAndStoreSettingsPush(\n cmdId: number,\n xml: string,\n headerChannelId: number,\n ): void {\n const now = Date.now();\n const configuredChannel = this.client.getConfiguredChannel?.() ?? 0;\n const channelFromHeader =\n headerChannelId >= 250\n ? configuredChannel\n : Math.max(0, headerChannelId - 1);\n\n const normalizePushChannel = (\n candidate: number | undefined,\n ): number | undefined => {\n if (candidate == null) return undefined;\n // Heuristic for NVR/Hub firmwares that emit 1-based <channelId> tags.\n // If the push channel matches the configured channel + 1, normalize to 0-based.\n if (candidate === configuredChannel + 1) return configuredChannel;\n return candidate;\n };\n\n const getEntry = (channel: number): BaichuanSettingsPushCacheEntry => {\n const existing = this.settingsPushCache.get(channel);\n if (existing) return existing;\n const created: BaichuanSettingsPushCacheEntry = { channel };\n this.settingsPushCache.set(channel, created);\n return created;\n };\n\n if (cmdId === BC_CMD_ID_PUSH_VIDEO_INPUT) {\n const value = parseVideoInputPushXml(xml);\n const channel =\n normalizePushChannel(value.channelId) ?? channelFromHeader;\n getEntry(channel).videoInput = { updatedAtMs: now, value };\n return;\n }\n\n if (cmdId === BC_CMD_ID_PUSH_SERIAL) {\n const value = parseSerialPushXml(xml);\n const channel =\n normalizePushChannel(value.channelId) ?? channelFromHeader;\n getEntry(channel).serial = { updatedAtMs: now, value };\n return;\n }\n\n if (cmdId === BC_CMD_ID_PUSH_NET_INFO) {\n const value = parseNetInfoPushXml(xml);\n getEntry(channelFromHeader).netInfo = {\n updatedAtMs: now,\n value,\n };\n return;\n }\n\n if (cmdId === BC_CMD_ID_PUSH_DINGDONG_LIST) {\n const value = parseDingdongListPushXml(xml);\n const channel = normalizePushChannel(value.channel) ?? channelFromHeader;\n getEntry(channel).dingdongList = { updatedAtMs: now, value };\n return;\n }\n\n if (cmdId === BC_CMD_ID_PUSH_SLEEP_STATUS) {\n const value = parseSleepStatusPushXml(xml);\n getEntry(channelFromHeader).sleepStatus = {\n updatedAtMs: now,\n value,\n };\n return;\n }\n\n if (cmdId === BC_CMD_ID_PUSH_COORDINATE_POINT_LIST) {\n const value = parseCoordinatePointListPushXml(xml);\n getEntry(channelFromHeader).coordinatePointList = {\n updatedAtMs: now,\n value,\n };\n return;\n }\n }\n\n /** Read-only snapshot of cached settings pushes (cmd_id 78/79/464/484/623/723). */\n getSettingsPushCacheSnapshot(): Map<number, BaichuanSettingsPushCacheEntry> {\n const out = new Map<number, BaichuanSettingsPushCacheEntry>();\n for (const [channel, entry] of this.settingsPushCache.entries()) {\n out.set(channel, {\n channel: entry.channel,\n ...(entry.videoInput\n ? {\n videoInput: {\n ...entry.videoInput,\n value: { ...entry.videoInput.value },\n },\n }\n : {}),\n ...(entry.serial\n ? { serial: { ...entry.serial, value: { ...entry.serial.value } } }\n : {}),\n ...(entry.netInfo\n ? { netInfo: { ...entry.netInfo, value: { ...entry.netInfo.value } } }\n : {}),\n ...(entry.dingdongList\n ? {\n dingdongList: {\n ...entry.dingdongList,\n value: { ...entry.dingdongList.value },\n },\n }\n : {}),\n ...(entry.sleepStatus\n ? {\n sleepStatus: {\n ...entry.sleepStatus,\n value: { ...entry.sleepStatus.value },\n },\n }\n : {}),\n ...(entry.coordinatePointList\n ? {\n coordinatePointList: {\n ...entry.coordinatePointList,\n value: { ...entry.coordinatePointList.value },\n },\n }\n : {}),\n });\n }\n return out;\n }\n\n getVideoInputFromPushCache(\n channel = 0,\n ): BaichuanCachedPush<BaichuanVideoInputPush> | undefined {\n return this.settingsPushCache.get(channel)?.videoInput;\n }\n getSerialFromPushCache(\n channel = 0,\n ): BaichuanCachedPush<BaichuanSerialPush> | undefined {\n return this.settingsPushCache.get(channel)?.serial;\n }\n getNetInfoFromPushCache(\n channel = 0,\n ): BaichuanCachedPush<BaichuanNetInfoPush> | undefined {\n return this.settingsPushCache.get(channel)?.netInfo;\n }\n getDingdongListFromPushCache(\n channel = -1,\n ): BaichuanCachedPush<BaichuanDingdongListPush> | undefined {\n return this.settingsPushCache.get(channel)?.dingdongList;\n }\n getSleepStatusFromPushCache(\n channel = 0,\n ): BaichuanCachedPush<BaichuanSleepStatusPush> | undefined {\n return this.settingsPushCache.get(channel)?.sleepStatus;\n }\n\n getCoordinatePointListFromPushCache(\n channel = 0,\n ): BaichuanCachedPush<BaichuanCoordinatePointListPush> | undefined {\n return this.settingsPushCache.get(channel)?.coordinatePointList;\n }\n\n // --------------------\n // PCAP-derived settings getters (typed wrappers)\n // --------------------\n\n private isNvrLikeDevice(): boolean {\n const configuredChannel = this.client.getConfiguredChannel?.() ?? 0;\n // If the client is configured with a non-zero channel, we are almost certainly talking to an NVR/Hub.\n if (configuredChannel > 0) return true;\n // NVRs send cmd_id 145 channel info pushes which populate this cache.\n return this.channelPushData.size > 0;\n }\n\n private async sendPcapDerivedSettingsGetXml(params: {\n cmdId: number;\n channel?: number;\n timeoutMs?: number;\n }): Promise<string> {\n const ch =\n params.channel != null\n ? this.normalizeChannel(params.channel)\n : undefined;\n\n const onNvr = this.isNvrLikeDevice();\n\n if (ch == null || !onNvr) {\n return await this.sendXml({\n cmdId: params.cmdId,\n ...(ch != null ? { channel: ch } : {}),\n ...(params.timeoutMs != null ? { timeoutMs: params.timeoutMs } : {}),\n });\n }\n\n const candidates: Array<{\n label: string;\n p: Parameters<ReolinkBaichuanApi[\"sendXml\"]>[0];\n }> = [\n {\n label: \"direct\",\n p: { cmdId: params.cmdId, channel: ch },\n },\n {\n // Common on NVR/Hub: header channelId must be the host (250), while Extension selects the camera.\n label: \"hostHeader ext0\",\n p: { cmdId: params.cmdId, channel: ch, channelIdOverride: 250 },\n },\n {\n label: \"hostHeader ext1\",\n p: {\n cmdId: params.cmdId,\n channel: ch,\n channelIdOverride: 250,\n extensionXml: buildChannelExtensionXml(ch + 1),\n },\n },\n {\n label: \"hostHeader noChannel ext0\",\n p: {\n cmdId: params.cmdId,\n channelIdOverride: 250,\n extensionXml: buildChannelExtensionXml(ch),\n },\n },\n {\n label: \"hostHeader noChannel ext1\",\n p: {\n cmdId: params.cmdId,\n channelIdOverride: 250,\n extensionXml: buildChannelExtensionXml(ch + 1),\n },\n },\n ];\n\n let lastErr: unknown;\n for (const c of candidates) {\n try {\n const xml = await this.sendXml({\n ...c.p,\n ...(params.timeoutMs != null ? { timeoutMs: params.timeoutMs } : {}),\n });\n if (xml !== \"\") return xml;\n // Empty body: try next NVR candidate.\n } catch (e) {\n lastErr = e;\n }\n }\n\n throw lastErr instanceof Error\n ? lastErr\n : new Error(\n `PCAP-derived settings GET failed for cmdId=${params.cmdId}: ${String(lastErr)}`,\n );\n }\n\n async getOsdDatetime(\n channel: number,\n options?: { timeoutMs?: number },\n ): Promise<BaichuanGetOsdDatetimeResult> {\n const rawXml = await this.sendPcapDerivedSettingsGetXml({\n cmdId: BC_CMD_ID_GET_OSD_DATETIME,\n channel,\n ...(options?.timeoutMs != null ? { timeoutMs: options.timeoutMs } : {}),\n });\n\n const osdBlock = getXmlBlocks(rawXml, \"OsdDatetime\")[0];\n const nameBlock = getXmlBlocks(rawXml, \"OsdChannelName\")[0];\n\n const osdDatetime: BaichuanOsdDatetime | undefined = osdBlock\n ? (() => {\n const channelId = parseNumber(getXmlText(osdBlock, \"channelId\"));\n const enable = parseBoolean01(getXmlText(osdBlock, \"enable\"));\n const topLeftX = parseNumber(getXmlText(osdBlock, \"topLeftX\"));\n const topLeftY = parseNumber(getXmlText(osdBlock, \"topLeftY\"));\n const width = parseNumber(getXmlText(osdBlock, \"width\"));\n const height = parseNumber(getXmlText(osdBlock, \"height\"));\n const language = getXmlText(osdBlock, \"language\")?.trim();\n\n return {\n ...(channelId != null ? { channelId } : {}),\n ...(enable != null ? { enable } : {}),\n ...(topLeftX != null ? { topLeftX } : {}),\n ...(topLeftY != null ? { topLeftY } : {}),\n ...(width != null ? { width } : {}),\n ...(height != null ? { height } : {}),\n ...(language ? { language } : {}),\n };\n })()\n : undefined;\n\n const osdChannelName: BaichuanOsdChannelName | undefined = nameBlock\n ? (() => {\n const channelId = parseNumber(getXmlText(nameBlock, \"channelId\"));\n const name = getXmlText(nameBlock, \"name\")?.trim();\n const enable = parseBoolean01(getXmlText(nameBlock, \"enable\"));\n const topLeftX = parseNumber(getXmlText(nameBlock, \"topLeftX\"));\n const topLeftY = parseNumber(getXmlText(nameBlock, \"topLeftY\"));\n const enWatermark = parseBoolean01(\n getXmlText(nameBlock, \"enWatermark\"),\n );\n const enBgcolor = parseBoolean01(getXmlText(nameBlock, \"enBgcolor\"));\n\n return {\n ...(channelId != null ? { channelId } : {}),\n ...(name ? { name } : {}),\n ...(enable != null ? { enable } : {}),\n ...(topLeftX != null ? { topLeftX } : {}),\n ...(topLeftY != null ? { topLeftY } : {}),\n ...(enWatermark != null ? { enWatermark } : {}),\n ...(enBgcolor != null ? { enBgcolor } : {}),\n };\n })()\n : undefined;\n\n return {\n ...(osdDatetime ? { osdDatetime } : {}),\n ...(osdChannelName ? { osdChannelName } : {}),\n };\n }\n\n async getRecordCfg(\n channel: number,\n options?: { timeoutMs?: number },\n ): Promise<BaichuanRecordCfg> {\n const rawXml = await this.sendPcapDerivedSettingsGetXml({\n cmdId: BC_CMD_ID_GET_RECORD_CFG,\n channel,\n ...(options?.timeoutMs != null ? { timeoutMs: options.timeoutMs } : {}),\n });\n\n const block = getXmlBlocks(rawXml, \"RecordCfg\")[0];\n const cycleListBlock = block\n ? getXmlBlocks(block, \"cyclelist\")[0]\n : undefined;\n const cycleList = cycleListBlock\n ? getXmlBlocks(cycleListBlock, \"item\")\n .map((t) => parseNumber(t.trim()))\n .filter((n): n is number => n != null)\n : undefined;\n\n const value: BaichuanRecordCfg = block\n ? (() => {\n const channelId = parseNumber(getXmlText(block, \"channelId\"));\n const cycle = parseNumber(getXmlText(block, \"cycle\"));\n const recordAbility = parseNumber(getXmlText(block, \"recordAbility\"));\n const smartRecord = parseNumber(getXmlText(block, \"smartRecord\"));\n const recordDelayTime = parseNumber(\n getXmlText(block, \"recordDelayTime\"),\n );\n const preRecordTime = parseNumber(getXmlText(block, \"preRecordTime\"));\n const packageTime = parseNumber(getXmlText(block, \"packageTime\"));\n\n return {\n ...(channelId != null ? { channelId } : {}),\n ...(cycle != null ? { cycle } : {}),\n ...(recordAbility != null ? { recordAbility } : {}),\n ...(smartRecord != null ? { smartRecord } : {}),\n ...(recordDelayTime != null ? { recordDelayTime } : {}),\n ...(preRecordTime != null ? { preRecordTime } : {}),\n ...(packageTime != null ? { packageTime } : {}),\n ...(cycleList && cycleList.length ? { cycleList } : {}),\n };\n })()\n : {};\n\n return value;\n }\n\n async getRecordSchedule(\n channel: number,\n options?: { timeoutMs?: number },\n ): Promise<BaichuanRecordSchedule> {\n const rawXml = await this.sendPcapDerivedSettingsGetXml({\n cmdId: BC_CMD_ID_GET_RECORD,\n channel,\n ...(options?.timeoutMs != null ? { timeoutMs: options.timeoutMs } : {}),\n });\n\n const block = getXmlBlocks(rawXml, \"Record\")[0];\n const listBlock = block\n ? getXmlBlocks(block, \"typeScheduleList\")[0]\n : undefined;\n const items = listBlock\n ? getXmlBlocks(listBlock, \"item\")\n .map((b) => {\n const type = (getXmlText(b, \"type\") ?? \"\").trim();\n const valueTable = (getXmlText(b, \"valueTable\") ?? \"\").trim();\n if (!type || !valueTable) return undefined;\n return { type, valueTable };\n })\n .filter((i): i is { type: string; valueTable: string } => i != null)\n : undefined;\n\n const value: BaichuanRecordSchedule = block\n ? (() => {\n const channelId = parseNumber(getXmlText(block, \"channelId\"));\n const enable = parseBoolean01(getXmlText(block, \"enable\"));\n return {\n ...(channelId != null ? { channelId } : {}),\n ...(enable != null ? { enable } : {}),\n ...(items && items.length ? { typeScheduleList: items } : {}),\n };\n })()\n : {};\n\n return value;\n }\n\n async getWifiSignal(\n channel: number,\n options?: { timeoutMs?: number },\n ): Promise<BaichuanWifiSignal> {\n const rawXml = await this.sendPcapDerivedSettingsGetXml({\n cmdId: BC_CMD_ID_GET_WIFI_SIGNAL,\n channel,\n ...(options?.timeoutMs != null ? { timeoutMs: options.timeoutMs } : {}),\n });\n const signal = parseNumber(getXmlText(rawXml, \"signal\"));\n return {\n ...(signal != null ? { signal } : {}),\n };\n }\n\n async getWifi(\n channel: number,\n options?: { timeoutMs?: number },\n ): Promise<BaichuanWifi> {\n const rawXml = await this.sendPcapDerivedSettingsGetXml({\n cmdId: BC_CMD_ID_GET_WIFI,\n channel,\n ...(options?.timeoutMs != null ? { timeoutMs: options.timeoutMs } : {}),\n });\n const protocol = parseNumber(getXmlText(rawXml, \"protocol\"));\n const mode = getXmlText(rawXml, \"mode\")?.trim();\n const ssid = getXmlText(rawXml, \"ssid\")?.trim();\n const key = getXmlText(rawXml, \"key\")?.trim();\n const wifiChannel = parseNumber(getXmlText(rawXml, \"channel\"));\n const isNVRSsid = parseNumber(getXmlText(rawXml, \"isNVRSsid\"));\n\n return {\n ...(protocol != null ? { protocol } : {}),\n ...(mode ? { mode } : {}),\n ...(ssid ? { ssid } : {}),\n ...(key ? { key } : {}),\n ...(wifiChannel != null ? { channel: wifiChannel } : {}),\n ...(isNVRSsid != null ? { isNVRSsid } : {}),\n };\n }\n\n async getStreamInfoList(\n channel: number,\n options?: { timeoutMs?: number },\n ): Promise<BaichuanStreamInfoList> {\n const rawXml = await this.sendPcapDerivedSettingsGetXml({\n cmdId: BC_CMD_ID_GET_STREAM_INFO_LIST,\n channel,\n ...(options?.timeoutMs != null ? { timeoutMs: options.timeoutMs } : {}),\n });\n\n const listBlock = getXmlBlocks(rawXml, \"StreamInfoList\")[0] ?? rawXml;\n const streamBlocks = getXmlBlocks(listBlock, \"StreamInfo\");\n\n const parseCsvNums = (s: string | undefined): number[] | undefined => {\n const raw = (s ?? \"\").trim();\n if (!raw) return undefined;\n const nums = raw\n .split(\",\")\n .map((x) => Number(x.trim()))\n .filter((n) => Number.isFinite(n));\n return nums.length ? nums : undefined;\n };\n\n const streams = streamBlocks.map((sb) => {\n const channelBits = parseNumber(getXmlText(sb, \"channelBits\"));\n const encBlocks = getXmlBlocks(sb, \"encodeTable\");\n const encodeTables = encBlocks.map((eb) => {\n const res = getXmlBlocks(eb, \"resolution\")[0];\n const width = res ? parseNumber(getXmlText(res, \"width\")) : undefined;\n const height = res ? parseNumber(getXmlText(res, \"height\")) : undefined;\n\n const encTypeListBlock = getXmlBlocks(eb, \"videoEncTypeList\")[0];\n const videoEncTypeList = encTypeListBlock\n ? getXmlBlocks(encTypeListBlock, \"videoEncType\")\n .map((t) => parseNumber(t.trim()))\n .filter((n): n is number => n != null)\n : undefined;\n\n const fr = parseCsvNums(getXmlText(eb, \"framerateTable\"));\n const br = parseCsvNums(getXmlText(eb, \"bitrateTable\"));\n\n const type = getXmlText(eb, \"type\")?.trim();\n const videoEncType = parseNumber(getXmlText(eb, \"videoEncType\"));\n const defaultFramerate = parseNumber(\n getXmlText(eb, \"defaultFramerate\"),\n );\n const defaultBitrate = parseNumber(getXmlText(eb, \"defaultBitrate\"));\n const defaultGop = parseNumber(getXmlText(eb, \"defaultGop\"));\n\n return {\n ...(type ? { type } : {}),\n ...(width != null ? { width } : {}),\n ...(height != null ? { height } : {}),\n ...(videoEncType != null ? { videoEncType } : {}),\n ...(videoEncTypeList && videoEncTypeList.length\n ? { videoEncTypeList }\n : {}),\n ...(defaultFramerate != null ? { defaultFramerate } : {}),\n ...(defaultBitrate != null ? { defaultBitrate } : {}),\n ...(fr ? { framerateTable: fr } : {}),\n ...(br ? { bitrateTable: br } : {}),\n ...(defaultGop != null ? { defaultGop } : {}),\n };\n });\n\n return {\n ...(channelBits != null ? { channelBits } : {}),\n encodeTables,\n };\n });\n\n return { streams };\n }\n\n async getLedState(\n channel: number,\n options?: { timeoutMs?: number },\n ): Promise<BaichuanLedState> {\n const rawXml = await this.sendPcapDerivedSettingsGetXml({\n cmdId: BC_CMD_ID_GET_LED_STATE,\n channel,\n ...(options?.timeoutMs != null ? { timeoutMs: options.timeoutMs } : {}),\n });\n const block = getXmlBlocks(rawXml, \"LedState\")[0];\n const value: BaichuanLedState = block\n ? (() => {\n const channelId = parseNumber(getXmlText(block, \"channelId\"));\n const ledVersion = parseNumber(getXmlText(block, \"ledVersion\"));\n const state = getXmlText(block, \"state\")?.trim();\n const lightState = getXmlText(block, \"lightState\")?.trim();\n return {\n ...(channelId != null ? { channelId } : {}),\n ...(ledVersion != null ? { ledVersion } : {}),\n ...(state ? { state } : {}),\n ...(lightState ? { lightState } : {}),\n };\n })()\n : {};\n return value;\n }\n\n async getSleepState(\n channel: number,\n options?: { timeoutMs?: number },\n ): Promise<BaichuanSleepState> {\n const rawXml = await this.sendPcapDerivedSettingsGetXml({\n cmdId: BC_CMD_ID_GET_SLEEP_STATE,\n channel,\n ...(options?.timeoutMs != null ? { timeoutMs: options.timeoutMs } : {}),\n });\n const block = getXmlBlocks(rawXml, \"sleepState\")[0];\n const value: BaichuanSleepState = block\n ? (() => {\n const sleep = parseNumber(getXmlText(block, \"sleep\"));\n const mode = parseNumber(getXmlText(block, \"mode\"));\n const panPos = parseNumber(getXmlText(block, \"panPos\"));\n const tiltPos = parseNumber(getXmlText(block, \"tiltPos\"));\n const imageName = getXmlText(block, \"imageName\")?.trim();\n return {\n ...(sleep != null ? { sleep } : {}),\n ...(mode != null ? { mode } : {}),\n ...(panPos != null ? { panPos } : {}),\n ...(tiltPos != null ? { tiltPos } : {}),\n ...(imageName ? { imageName } : {}),\n };\n })()\n : {};\n return value;\n }\n\n // Remaining PCAP-derived cmdIds: expose as JSON (XML parsed client-side).\n async getAbilitySupport(\n channel?: number,\n options?: { timeoutMs?: number },\n ): Promise<XmlJsonValue> {\n const xml = await this.sendPcapDerivedSettingsGetXml({\n cmdId: BC_CMD_ID_GET_ABILITY_SUPPORT,\n ...(channel != null ? { channel } : {}),\n ...(options?.timeoutMs != null ? { timeoutMs: options.timeoutMs } : {}),\n });\n return parseXmlFragmentToJson(xml);\n }\n\n async getFtpTask(\n channel?: number,\n options?: { timeoutMs?: number },\n ): Promise<FtpTaskConfig> {\n const xml = await this.sendPcapDerivedSettingsGetXml({\n cmdId: BC_CMD_ID_GET_FTP_TASK,\n ...(channel != null ? { channel } : {}),\n ...(options?.timeoutMs != null ? { timeoutMs: options.timeoutMs } : {}),\n });\n return parseXmlFragmentToJson<FtpTaskConfig>(xml);\n }\n\n async getHddInfoList(options?: {\n timeoutMs?: number;\n }): Promise<HddInfoListConfig> {\n const xml = await this.sendXml({\n cmdId: BC_CMD_ID_GET_HDD_INFO_LIST,\n ...(options?.timeoutMs != null ? { timeoutMs: options.timeoutMs } : {}),\n });\n return parseXmlFragmentToJson<HddInfoListConfig>(xml);\n }\n\n async getDayRecords(\n channel?: number,\n options?: { timeoutMs?: number },\n ): Promise<XmlJsonValue> {\n const xml = await this.sendPcapDerivedSettingsGetXml({\n cmdId: BC_CMD_ID_GET_DAY_RECORDS,\n ...(channel != null ? { channel } : {}),\n ...(options?.timeoutMs != null ? { timeoutMs: options.timeoutMs } : {}),\n });\n return parseXmlFragmentToJson(xml);\n }\n\n async getEmailTask(\n channel?: number,\n options?: { timeoutMs?: number },\n ): Promise<EmailTaskConfig> {\n const xml = await this.sendPcapDerivedSettingsGetXml({\n cmdId: BC_CMD_ID_GET_EMAIL_TASK,\n ...(channel != null ? { channel } : {}),\n ...(options?.timeoutMs != null ? { timeoutMs: options.timeoutMs } : {}),\n });\n return parseXmlFragmentToJson<EmailTaskConfig>(xml);\n }\n\n /**\n * Get siren-on-motion state via AudioTask (cmdId=232).\n *\n * This is the command the Reolink app uses to get motion alarm/siren enable state.\n * Returns AudioTask with enable=1 (siren on motion enabled) or enable=0 (disabled).\n *\n * Note: This is different from getMotionAlarm (cmdId=46) which controls motion detection recording.\n * This controls whether the siren/notification sounds when motion is detected.\n *\n * @example\n * ```ts\n * const result = await api.getSirenOnMotion(0);\n * // Returns: AudioTask with enable, typeScheduleList\n * ```\n */\n async getSirenOnMotion(\n channel?: number,\n options?: { timeoutMs?: number },\n ): Promise<AudioTaskConfig> {\n const xml = await this.sendPcapDerivedSettingsGetXml({\n cmdId: BC_CMD_ID_GET_AUDIO_TASK,\n ...(channel != null ? { channel } : {}),\n ...(options?.timeoutMs != null ? { timeoutMs: options.timeoutMs } : {}),\n });\n return parseXmlFragmentToJson<AudioTaskConfig>(xml);\n }\n\n async getAudioCfg(\n channel?: number,\n options?: { timeoutMs?: number },\n ): Promise<AudioCfgConfig> {\n const xml = await this.sendPcapDerivedSettingsGetXml({\n cmdId: BC_CMD_ID_GET_AUDIO_CFG,\n ...(channel != null ? { channel } : {}),\n ...(options?.timeoutMs != null ? { timeoutMs: options.timeoutMs } : {}),\n });\n return parseXmlFragmentToJson<AudioCfgConfig>(xml);\n }\n\n async getDayNightThreshold(\n channel?: number,\n options?: { timeoutMs?: number },\n ): Promise<DayNightThresholdConfig> {\n const xml = await this.sendPcapDerivedSettingsGetXml({\n cmdId: BC_CMD_ID_GET_DAY_NIGHT_THRESHOLD,\n ...(channel != null ? { channel } : {}),\n ...(options?.timeoutMs != null ? { timeoutMs: options.timeoutMs } : {}),\n });\n return parseXmlFragmentToJson<DayNightThresholdConfig>(xml);\n }\n\n async getTimelapseCfg(\n channel?: number,\n options?: { timeoutMs?: number },\n ): Promise<TimelapseCfgConfig> {\n const xml = await this.sendPcapDerivedSettingsGetXml({\n cmdId: BC_CMD_ID_GET_TIMELAPSE_CFG,\n ...(channel != null ? { channel } : {}),\n ...(options?.timeoutMs != null ? { timeoutMs: options.timeoutMs } : {}),\n });\n return parseXmlFragmentToJson<TimelapseCfgConfig>(xml);\n }\n\n async getAiDenoise(\n channel?: number,\n options?: { timeoutMs?: number },\n ): Promise<AiDenoiseConfig> {\n const xml = await this.sendPcapDerivedSettingsGetXml({\n cmdId: BC_CMD_ID_GET_AI_DENOISE,\n ...(channel != null ? { channel } : {}),\n ...(options?.timeoutMs != null ? { timeoutMs: options.timeoutMs } : {}),\n });\n return parseXmlFragmentToJson<AiDenoiseConfig>(xml);\n }\n\n async getKitApCfg(options?: { timeoutMs?: number }): Promise<XmlJsonValue> {\n const xml = await this.sendXml({\n cmdId: BC_CMD_ID_GET_KIT_AP_CFG,\n ...(options?.timeoutMs != null ? { timeoutMs: options.timeoutMs } : {}),\n });\n return parseXmlFragmentToJson(xml);\n }\n\n async getRecEncCfg(\n channel?: number,\n options?: { timeoutMs?: number },\n ): Promise<RecEncConfig> {\n const xml = await this.sendPcapDerivedSettingsGetXml({\n cmdId: BC_CMD_ID_GET_REC_ENC_CFG,\n ...(channel != null ? { channel } : {}),\n ...(options?.timeoutMs != null ? { timeoutMs: options.timeoutMs } : {}),\n });\n return parseXmlFragmentToJson<RecEncConfig>(xml);\n }\n\n async getAccessUserList(options?: {\n timeoutMs?: number;\n }): Promise<AccessUserListConfig> {\n const xml = await this.sendXml({\n cmdId: BC_CMD_ID_GET_ACCESS_USER_LIST,\n ...(options?.timeoutMs != null ? { timeoutMs: options.timeoutMs } : {}),\n });\n return parseXmlFragmentToJson<AccessUserListConfig>(xml);\n }\n\n /**\n * Get list of active/online user sessions (cmdId=120).\n * Returns information about currently connected users/sessions on the device.\n */\n async getOnlineUserList(options?: {\n timeoutMs?: number;\n }): Promise<OnlineUserListConfig> {\n const xml = await this.sendXml({\n cmdId: BC_CMD_ID_GET_ONLINE_USER_LIST,\n ...(options?.timeoutMs != null ? { timeoutMs: options.timeoutMs } : {}),\n });\n return parseXmlFragmentToJson<OnlineUserListConfig>(xml);\n }\n\n // Placeholder cmdIds seen in PCAPs but without XML samples yet.\n // Expose as JSON (parsed from XML) for easy inspection.\n async getCmd123(\n channel?: number,\n options?: { timeoutMs?: number },\n ): Promise<XmlJsonValue> {\n const xml = await this.sendPcapDerivedSettingsGetXml({\n cmdId: BC_CMD_ID_CMD_123,\n ...(channel != null ? { channel } : {}),\n ...(options?.timeoutMs != null ? { timeoutMs: options.timeoutMs } : {}),\n });\n return parseXmlFragmentToJson(xml);\n }\n\n async getCmd209(\n channel?: number,\n options?: { timeoutMs?: number },\n ): Promise<XmlJsonValue> {\n const xml = await this.sendPcapDerivedSettingsGetXml({\n cmdId: BC_CMD_ID_CMD_209,\n ...(channel != null ? { channel } : {}),\n ...(options?.timeoutMs != null ? { timeoutMs: options.timeoutMs } : {}),\n });\n return parseXmlFragmentToJson(xml);\n }\n\n /**\n * Get video input settings and advanced configuration.\n *\n * @returns VideoInput + InputAdvanceCfg - brightness, contrast, exposure, etc.\n * @example\n * ```ts\n * const result = await api.getVideoInput(0);\n * // Returns: VideoInput with bright, contrast, etc.\n * ```\n */\n async getVideoInput(\n channel?: number,\n options?: { timeoutMs?: number },\n ): Promise<VideoInputConfig> {\n const xml = await this.sendPcapDerivedSettingsGetXml({\n cmdId: BC_CMD_ID_GET_VIDEO_INPUT,\n ...(channel != null ? { channel } : {}),\n ...(options?.timeoutMs != null ? { timeoutMs: options.timeoutMs } : {}),\n });\n return parseXmlFragmentToJson<VideoInputConfig>(xml);\n }\n\n /**\n * Get system general settings (time, name, language).\n *\n * @returns SystemGeneral + Norm - timezone, deviceName, language, etc.\n * @example\n * ```ts\n * const result = await api.getSystemGeneral();\n * // Returns: SystemGeneral with timeZone, deviceName, etc.\n * ```\n */\n async getSystemGeneral(options?: {\n timeoutMs?: number;\n }): Promise<SystemGeneralConfig> {\n const xml = await this.sendPcapDerivedSettingsGetXml({\n cmdId: BC_CMD_ID_GET_SYSTEM_GENERAL,\n ...(options?.timeoutMs != null ? { timeoutMs: options.timeoutMs } : {}),\n });\n return parseXmlFragmentToJson<SystemGeneralConfig>(xml);\n }\n\n /**\n * Get device support/capability flags.\n *\n * @returns Support - ptzMode, channelNum, wifi flags, rtsp, rtmp, etc.\n * @example\n * ```ts\n * const result = await api.getSupport();\n * // Returns: Support with ptzMode, channelNum, wifi, rtsp, etc.\n * ```\n */\n async getSupport(options?: { timeoutMs?: number }): Promise<SupportConfig> {\n const xml = await this.sendPcapDerivedSettingsGetXml({\n cmdId: BC_CMD_ID_GET_SUPPORT,\n ...(options?.timeoutMs != null ? { timeoutMs: options.timeoutMs } : {}),\n });\n return parseXmlFragmentToJson<SupportConfig>(xml);\n }\n\n /**\n * Get AI configuration (smart tracking, detection types).\n *\n * @returns AiCfg - smartTrack, detectType, trackPriorities, etc.\n * @example\n * ```ts\n * const result = await api.getAiCfg(0);\n * // Returns: AiCfg with smartTrack, detectType, etc.\n * ```\n */\n async getAiCfg(\n channel?: number,\n options?: { timeoutMs?: number },\n ): Promise<AiConfig> {\n const xml = await this.sendPcapDerivedSettingsGetXml({\n cmdId: BC_CMD_ID_GET_AI_CFG,\n ...(channel != null ? { channel } : {}),\n ...(options?.timeoutMs != null ? { timeoutMs: options.timeoutMs } : {}),\n });\n return parseXmlFragmentToJson<AiConfig>(xml);\n }\n\n /**\n * Get autotracking (smart track) state.\n *\n * Uses cmdId=299 (AiCfg) to retrieve the autotracking configuration.\n * The smartTrack field (0=off, 1=on) controls whether the camera automatically\n * pans/tilts to follow detected objects.\n *\n * @param channel - Optional channel ID (default: 0)\n * @returns Object with enabled (boolean) and raw AiCfg data\n * @example\n * ```ts\n * const result = await api.getAutotracking(0);\n * console.log(\"Autotracking enabled:\", result.enabled);\n * console.log(\"Tracking mode:\", result.smartTrackMode);\n * ```\n */\n async getAutotracking(\n channel?: number,\n options?: { timeoutMs?: number },\n ): Promise<{\n enabled: boolean;\n smartTrack: number;\n smartTrackMode?: number | undefined;\n smartTrackType?: string | undefined;\n detectType?: string | undefined;\n raw: AiConfig;\n }> {\n const aiCfg = await this.getAiCfg(channel, options);\n const cfg = (aiCfg as any)?.body?.AiCfg ?? (aiCfg as any)?.AiCfg ?? aiCfg;\n const smartTrack = Number(cfg?.smartTrack ?? 0);\n return {\n enabled: smartTrack === 1,\n smartTrack,\n smartTrackMode:\n cfg?.smartTrackMode != null ? Number(cfg.smartTrackMode) : undefined,\n smartTrackType: cfg?.smartTrackType ?? undefined,\n detectType: cfg?.detectType ?? undefined,\n raw: aiCfg,\n };\n }\n\n /**\n * Set autotracking (smart track) state via AiCfg (cmdId=300).\n *\n * This controls the auto pan-tilt tracking feature on PTZ cameras.\n * When enabled, the camera will automatically track detected objects (people, pets, vehicles).\n *\n * @param enabled - Whether to enable (true/1) or disable (false/0) autotracking\n * @param channel - Optional channel ID (default: 0)\n * @example\n * ```ts\n * // Enable autotracking\n * await api.setAutotracking(true, 0);\n * // Disable autotracking\n * await api.setAutotracking(false, 0);\n * ```\n */\n async setAutotracking(\n enabled: boolean | 0 | 1,\n channel?: number,\n options?: { timeoutMs?: number },\n ): Promise<XmlJsonValue> {\n const ch = channel ?? 0;\n const smartTrack = enabled ? 1 : 0;\n\n // First get the current AiCfg to preserve other settings\n const currentCfg = await this.getAiCfg(ch, options);\n const cfg =\n (currentCfg as any)?.body?.AiCfg ?? (currentCfg as any)?.AiCfg ?? {};\n\n // Build the XML payload with the updated smartTrack value\n const payloadXml = `<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<body>\n<AiCfg version=\"1.1\">\n<channelId>${ch}</channelId>\n<smartTrack>${smartTrack}</smartTrack>\n${cfg.smartTrackMode != null ? `<smartTrackMode>${cfg.smartTrackMode}</smartTrackMode>` : \"\"}\n${cfg.detectType ? `<detectType>${cfg.detectType}</detectType>` : \"\"}\n${cfg.smartTrackType ? `<smartTrackType>${cfg.smartTrackType}</smartTrackType>` : \"\"}\n</AiCfg>\n</body>`;\n\n const xml = await this.sendXml({\n cmdId: BC_CMD_ID_SET_AI_CFG,\n channel: ch,\n payloadXml,\n ...(options?.timeoutMs != null ? { timeoutMs: options.timeoutMs } : {}),\n });\n return parseXmlFragmentToJson(xml);\n }\n\n /**\n * Set autotracking settings (smartTrackType, delays, etc.) via AiCfg (cmdId=300).\n *\n * This allows configuring autotracking parameters without changing the enable state.\n *\n * @param channel - Channel ID (default: 0)\n * @param settings - Autotracking settings to apply\n * @example\n * ```ts\n * await api.setAutotrackingSettings(0, {\n * smartTrackType: 'people,vehicle',\n * smartTrackObjectStopDelay: 30,\n * smartTrackObjectDisappearDelay: 15,\n * });\n * ```\n */\n async setAutotrackingSettings(\n channel: number | undefined,\n settings: {\n smartTrackType?: string;\n smartTrackObjectStopDelay?: number;\n smartTrackObjectDisappearDelay?: number;\n },\n options?: { timeoutMs?: number },\n ): Promise<XmlJsonValue> {\n const ch = channel ?? 0;\n\n // First get the current AiCfg to preserve other settings\n const currentCfg = await this.getAiCfg(ch, options);\n const cfg =\n (currentCfg as any)?.body?.AiCfg ?? (currentCfg as any)?.AiCfg ?? {};\n\n // Merge with current values\n const smartTrackType = settings.smartTrackType ?? cfg.smartTrackType;\n const stopDelay =\n settings.smartTrackObjectStopDelay ?? cfg.smartTrackObjectStopDelay ?? 20;\n const disappearDelay =\n settings.smartTrackObjectDisappearDelay ??\n cfg.smartTrackObjectDisappearDelay ??\n 10;\n\n // Build the XML payload with updated settings\n const payloadXml = `<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<body>\n<AiCfg version=\"1.1\">\n<channelId>${ch}</channelId>\n<smartTrack>${cfg.smartTrack ?? 0}</smartTrack>\n${cfg.smartTrackMode != null ? `<smartTrackMode>${cfg.smartTrackMode}</smartTrackMode>` : \"\"}\n${cfg.detectType ? `<detectType>${cfg.detectType}</detectType>` : \"\"}\n${smartTrackType ? `<smartTrackType>${smartTrackType}</smartTrackType>` : \"\"}\n<smartTrackObjectStopDelay>${stopDelay}</smartTrackObjectStopDelay>\n<smartTrackObjectDisappearDelay>${disappearDelay}</smartTrackObjectDisappearDelay>\n</AiCfg>\n</body>`;\n\n const xml = await this.sendXml({\n cmdId: BC_CMD_ID_SET_AI_CFG,\n channel: ch,\n payloadXml,\n ...(options?.timeoutMs != null ? { timeoutMs: options.timeoutMs } : {}),\n });\n return parseXmlFragmentToJson(xml);\n }\n\n /**\n * Get siren status (for cameras with siren capability).\n *\n * @returns SirenStatusList - status of siren alarm\n */\n async getSirenStatus(options?: {\n timeoutMs?: number;\n }): Promise<SirenStatusConfig> {\n const xml = await this.sendPcapDerivedSettingsGetXml({\n cmdId: BC_CMD_ID_GET_SIREN_STATUS,\n ...(options?.timeoutMs != null ? { timeoutMs: options.timeoutMs } : {}),\n });\n return parseXmlFragmentToJson<SirenStatusConfig>(xml);\n }\n\n /**\n * Set siren-on-motion state via AudioTask (cmdId=231).\n *\n * This is the command the Reolink app uses to toggle siren/alarm on motion on/off.\n * cmdId=231 sends the AudioTask configuration with enable=0/1.\n *\n * Note: This is different from setMotionDetection (cmdId=47) which controls motion detection recording.\n * This controls whether the siren/notification sounds when motion is detected.\n *\n * @param audioTask - The siren-on-motion configuration to set (enable: 0 or 1)\n * @param channel - Optional channel ID (default: 0)\n * @example\n * ```ts\n * // Disable siren on motion\n * await api.setSirenOnMotion({ enable: 0 }, 0);\n * // Enable siren on motion\n * await api.setSirenOnMotion({ enable: 1 }, 0);\n * ```\n */\n async setSirenOnMotion(\n audioTask: {\n enable: 0 | 1;\n typeScheduleList?: Array<{\n type: string;\n valueTable: string;\n }>;\n },\n channel?: number,\n options?: { timeoutMs?: number },\n ): Promise<XmlJsonValue> {\n const ch = channel ?? 0;\n const scheduleList = audioTask.typeScheduleList || [\n { type: \"MD\", valueTable: \"1\".repeat(168) },\n { type: \"people\", valueTable: \"1\".repeat(168) },\n { type: \"dog_cat\", valueTable: \"1\".repeat(168) },\n ];\n const scheduleItems = scheduleList\n .map(\n (item) =>\n `<item><type>${item.type}</type><valueTable>${item.valueTable}</valueTable></item>`,\n )\n .join(\"\");\n\n const payloadXml = `<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<body>\n<AudioTask version=\"1.1\">\n<channelId>${ch}</channelId>\n<enable>${audioTask.enable}</enable>\n<typeScheduleList>\n${scheduleItems}\n</typeScheduleList>\n</AudioTask>\n</body>`;\n\n const xml = await this.sendXml({\n cmdId: BC_CMD_ID_SET_AUDIO_TASK,\n channel: ch,\n payloadXml,\n ...(options?.timeoutMs != null ? { timeoutMs: options.timeoutMs } : {}),\n });\n return parseXmlFragmentToJson(xml);\n }\n\n async getCmd265(\n channel?: number,\n options?: { timeoutMs?: number },\n ): Promise<XmlJsonValue> {\n const xml = await this.sendPcapDerivedSettingsGetXml({\n cmdId: BC_CMD_ID_CMD_265,\n ...(channel != null ? { channel } : {}),\n ...(options?.timeoutMs != null ? { timeoutMs: options.timeoutMs } : {}),\n });\n return parseXmlFragmentToJson(xml);\n }\n\n async getCmd440(\n channel?: number,\n options?: { timeoutMs?: number },\n ): Promise<XmlJsonValue> {\n const xml = await this.sendPcapDerivedSettingsGetXml({\n cmdId: BC_CMD_ID_CMD_440,\n ...(channel != null ? { channel } : {}),\n ...(options?.timeoutMs != null ? { timeoutMs: options.timeoutMs } : {}),\n });\n return parseXmlFragmentToJson(xml);\n }\n\n /**\n * Convenience helper: convert CoverPreview (cmdId=298) snapshot to a JPEG.\n *\n * Implementation detail: uses `ffmpeg` from PATH (same dependency already used by endpoints-server VOD streaming).\n */\n async getVideoclipThumbnailJpegRaw(params: {\n channel?: number;\n time: Date;\n snapType?: \"main\" | \"sub\";\n timeoutMs?: number;\n /** 2..31 (lower = better quality). Default: 2 */\n jpegQuality?: number;\n }): Promise<{\n jpeg: Buffer;\n snapshot: VideoclipThumbnailResult;\n }> {\n const snapshot = await this.getVideoclipThumbnail(params);\n const enc = String(snapshot.encoding || \"\").toUpperCase();\n const demux = enc.includes(\"265\") || enc.includes(\"HEVC\") ? \"hevc\" : \"h264\";\n const q = params.jpegQuality ?? 2;\n\n const args = [\n \"-hide_banner\",\n \"-loglevel\",\n \"error\",\n \"-fflags\",\n \"+genpts\",\n \"-f\",\n demux,\n \"-i\",\n \"pipe:0\",\n \"-frames:v\",\n \"1\",\n \"-q:v\",\n String(q),\n \"-f\",\n \"image2pipe\",\n \"-vcodec\",\n \"mjpeg\",\n \"pipe:1\",\n ];\n\n const ff = spawn(\"ffmpeg\", args, { stdio: [\"pipe\", \"pipe\", \"pipe\"] });\n const chunks: Buffer[] = [];\n let stderr = \"\";\n ff.stdout.on(\"data\", (d) => chunks.push(Buffer.from(d)));\n ff.stderr.on(\"data\", (d) => (stderr += String(d)));\n\n ff.stdin.write(snapshot.frame);\n ff.stdin.end();\n\n const exitCode: number = await new Promise((resolve, reject) => {\n ff.on(\"error\", reject);\n ff.on(\"close\", (code) => resolve(code ?? 0));\n });\n\n if (exitCode !== 0) {\n throw new Error(\n `ffmpeg failed converting CoverPreview I-frame to JPEG (exit=${exitCode}): ${stderr}`,\n );\n }\n\n const jpeg = Buffer.concat(chunks);\n if (jpeg.length < 16) {\n throw new Error(\n `ffmpeg produced an empty JPEG buffer (${jpeg.length} bytes)`,\n );\n }\n\n return { jpeg, snapshot };\n }\n\n /**\n * Stream a recording replay (cmdId=5) as a fragmented MP4 (streamable over HTTP).\n *\n * The duration is automatically extracted from the filename timestamps.\n * Falls back to 300 seconds if parsing fails.\n *\n * Notes:\n * - This is video-only (audio is currently not muxed).\n * - Uses `ffmpeg` from PATH.\n * - Operations are serialized via the replay queue.\n */\n async createRecordingReplayMp4Stream(params: {\n /** Channel number (0-based). Required. */\n channel: number;\n /** Full path to the recording file. Required. Duration is extracted from filename. */\n fileName: string;\n /**\n * Force NVR mode (uses id-based XML with UID) or standalone mode (name-based XML).\n * If not specified, the library will detect based on device channel count.\n */\n isNvr?: boolean;\n /** Optional logger override. If not provided, uses the API's logger. */\n logger?: Logger;\n /**\n * External identifier for the dedicated socket session.\n * When provided, a dedicated BaichuanClient is created/reused for this deviceId.\n * This allows switching between clips without interfering with other sessions.\n * Recommended: pass a unique identifier per logical device/player instance.\n */\n deviceId?: string;\n /**\n * Transcode H.265/HEVC to H.264/AVC for compatibility with clients that don't support H.265.\n * When true and the source is H.265, ffmpeg will transcode to H.264 using libx264.\n * This increases CPU usage but ensures playback on iOS Safari, older browsers, etc.\n * Default: false (passthrough/copy).\n */\n transcodeH265ToH264?: boolean;\n }): Promise<{\n mp4: Readable;\n stop: () => Promise<void>;\n }> {\n const logger = params.logger ?? this.logger;\n\n // Extract duration from filename timestamps\n const parsed = parseRecordingFileName(params.fileName);\n const durationMs = parsed?.durationMs ?? 300_000; // Fallback: 5 minutes\n // Add 10% buffer to ensure we get the complete clip\n const seconds = Math.ceil((durationMs / 1000) * 1.1);\n\n logger?.debug?.(\n `[createRecordingReplayMp4Stream] Starting: channel=${params.channel}, fileName=${params.fileName}, durationMs=${durationMs}, timeoutSec=${seconds}, deviceId=${params.deviceId ?? \"auto\"}`,\n );\n\n const startParams: Parameters<\n ReolinkBaichuanApi[\"startRecordingReplayStream\"]\n >[0] = {\n channel: params.channel,\n fileName: params.fileName,\n logger,\n ...(params.isNvr != null ? { isNvr: params.isNvr } : {}),\n ...(params.deviceId != null ? { deviceId: params.deviceId } : {}),\n };\n\n // Use streaming queue - holds the slot until release() is called\n const { result: replayResult, release: releaseQueueSlot } =\n await this.enqueueStreamingReplayOperation(() =>\n this.startRecordingReplayStream(startParams),\n );\n\n const { stream, stop: stopReplay } = replayResult;\n\n const input = new PassThrough();\n const output = new PassThrough();\n\n // Use MPEG-TS muxer to preserve frame timestamps (PTS)\n // This ensures correct playback speed regardless of variable framerate\n let tsMuxer: MpegTsMuxer | null = null;\n\n let ff: ReturnType<typeof spawn> | null = null;\n let ended = false;\n let frameCount = 0;\n\n const startFfmpeg = (videoType: \"H264\" | \"H265\") => {\n if (ff) return;\n\n // Check if we need to transcode H.265 to H.264\n const needsTranscode =\n videoType === \"H265\" && params.transcodeH265ToH264 === true;\n\n logger?.debug?.(\n `[createRecordingReplayMp4Stream] Starting ffmpeg with videoType=${videoType}, transcode=${needsTranscode}`,\n );\n\n // Initialize MPEG-TS muxer for this video type\n MpegTsMuxer.resetCounters();\n tsMuxer = new MpegTsMuxer({ videoType });\n\n // ffmpeg reads MPEG-TS input (which has PTS) and outputs fMP4\n // No -r needed because timestamps come from the TS stream\n const args = [\n \"-hide_banner\",\n \"-loglevel\",\n \"error\",\n \"-f\",\n \"mpegts\",\n \"-i\",\n \"pipe:0\",\n // Video codec: transcode H.265→H.264 if requested, otherwise copy\n ...(needsTranscode\n ? [\"-c:v\", \"libx264\", \"-preset\", \"ultrafast\", \"-crf\", \"23\"]\n : [\"-c\", \"copy\"]),\n \"-movflags\",\n \"frag_keyframe+empty_moov\",\n \"-f\",\n \"mp4\",\n \"pipe:1\",\n ];\n\n ff = spawn(\"ffmpeg\", args, { stdio: [\"pipe\", \"pipe\", \"pipe\"] });\n if (!ff.stdin || !ff.stdout || !ff.stderr) {\n throw new Error(\"ffmpeg stdio streams not available\");\n }\n input.pipe(ff.stdin);\n ff.stdout.pipe(output);\n\n // Prevent uncaught ECONNRESET/EPIPE errors on streams\n ff.stdin.on(\"error\", () => {});\n ff.stdout.on(\"error\", () => {});\n input.on(\"error\", () => {});\n output.on(\"error\", () => {});\n\n let stderr = \"\";\n ff.stderr.on(\"data\", (d) => (stderr += String(d)));\n ff.on(\"close\", (code) => {\n if (ended) return;\n ended = true;\n // Best-effort: surface ffmpeg errors to consumers.\n if ((code ?? 0) !== 0 && stderr.trim()) {\n logger?.error?.(\n `[createRecordingReplayMp4Stream] ffmpeg exited with code ${code}: ${stderr}`,\n );\n output.destroy(\n new Error(`ffmpeg exited with code ${code ?? 0}: ${stderr}`),\n );\n } else {\n logger?.debug?.(\n `[createRecordingReplayMp4Stream] ffmpeg closed normally, frames=${frameCount}`,\n );\n output.end();\n }\n });\n };\n\n const stopAll = async (): Promise<void> => {\n if (ended) return;\n ended = true;\n logger?.debug?.(\n `[createRecordingReplayMp4Stream] Stopping stream, frames=${frameCount}`,\n );\n try {\n await stopReplay();\n } catch {\n // ignore\n }\n try {\n await stream.stop();\n } catch {\n // ignore\n }\n try {\n input.end();\n } catch {\n // ignore\n }\n try {\n ff?.kill(\"SIGKILL\");\n } catch {\n // ignore\n }\n try {\n output.end();\n } catch {\n // ignore\n }\n // Release queue slot when stream ends\n releaseQueueSlot();\n };\n\n const timer = setTimeout(\n () => {\n logger?.debug?.(\n `[createRecordingReplayMp4Stream] Timeout reached (${seconds}s), stopping`,\n );\n void stopAll();\n },\n Math.max(1, seconds) * 1000,\n );\n\n output.on(\"close\", () => {\n clearTimeout(timer);\n void stopAll();\n });\n\n stream.on(\"error\", (e) => {\n logger?.error?.(\n `[createRecordingReplayMp4Stream] Stream error: ${e.message}`,\n );\n output.destroy(e);\n void stopAll();\n });\n\n stream.on(\n \"videoAccessUnit\",\n ({ data, videoType, isKeyframe, microseconds }) => {\n if (ended) return;\n startFfmpeg(videoType);\n frameCount++;\n // Mux frame into MPEG-TS with correct PTS timestamp\n if (tsMuxer) {\n const tsData = tsMuxer.mux(data, microseconds, isKeyframe);\n input.write(tsData);\n }\n },\n );\n\n return {\n mp4: output,\n stop: stopAll,\n };\n }\n\n /**\n * Download a recording (cmdId=13) and convert it to a fragmented MP4 stream.\n *\n * This is an alternative to `createRecordingReplayMp4Stream()` for cameras that\n * don't support streaming replay (cmdId=5). It downloads the entire recording first,\n * demuxes it to Annex-B format, then pipes through ffmpeg to create a streamable MP4.\n *\n * Advantages over replay streaming:\n * - Works on all cameras that support download (cmdId=13)\n * - More reliable for standalone cameras like E1 Outdoor PoE\n *\n * Disadvantages:\n * - Downloads the entire file before streaming starts (not truly real-time)\n * - Higher memory usage for large recordings\n *\n * Notes:\n * - This is video-only (audio is currently not muxed).\n * - Uses `ffmpeg` from PATH.\n *\n * @example\n * ```ts\n * const { mp4, stop } = await api.createRecordingDownloadMp4Stream({\n * channel: 0,\n * fileName: \"/mnt/sda/Mp4Record/2026-01-25/RecS03_20260125_120000_120100_ABC.mp4\",\n * });\n * mp4.pipe(response); // Pipe to HTTP response\n * ```\n */\n async createRecordingDownloadMp4Stream(params: {\n /** Channel number (0-based). Required. */\n channel: number;\n /** Full path to the recording file. Required. */\n fileName: string;\n /** Download timeout in ms. Default: 120000 (2 minutes). */\n timeoutMs?: number;\n }): Promise<{\n mp4: Readable;\n stop: () => Promise<void>;\n }> {\n const timeoutMs = params.timeoutMs ?? 120_000;\n\n // Extract framerate from filename hex flags, fallback to 15 fps (common for recordings)\n const parsed = parseRecordingFileName(params.fileName);\n const fps =\n parsed?.framerate && parsed.framerate > 0 ? parsed.framerate : 15;\n\n // Get UID for the channel (required for download)\n const channel = this.normalizeChannel(params.channel);\n const uid = await this.ensureUidForRecordings(channel);\n\n // Download and demux the recording\n const { annexB, videoType } = await this.downloadRecordingDemuxed({\n channel,\n uid,\n fileName: params.fileName,\n timeoutMs,\n });\n\n if (annexB.length === 0) {\n throw new Error(\"Downloaded recording is empty\");\n }\n\n const input = new PassThrough();\n const output = new PassThrough();\n\n let ff: ReturnType<typeof spawn> | null = null;\n let ended = false;\n\n const stopAll = async (): Promise<void> => {\n if (ended) return;\n ended = true;\n try {\n input.end();\n } catch {\n // ignore\n }\n try {\n ff?.kill(\"SIGKILL\");\n } catch {\n // ignore\n }\n try {\n output.end();\n } catch {\n // ignore\n }\n };\n\n // Determine demuxer based on video type\n const demux = videoType === \"H265\" ? \"hevc\" : \"h264\";\n const args = [\n \"-hide_banner\",\n \"-loglevel\",\n \"error\",\n \"-fflags\",\n \"+genpts\",\n \"-r\",\n String(fps),\n \"-f\",\n demux,\n \"-i\",\n \"pipe:0\",\n \"-c\",\n \"copy\",\n \"-movflags\",\n \"frag_keyframe+empty_moov\",\n \"-f\",\n \"mp4\",\n \"pipe:1\",\n ];\n\n ff = spawn(\"ffmpeg\", args, { stdio: [\"pipe\", \"pipe\", \"pipe\"] });\n if (!ff.stdin || !ff.stdout || !ff.stderr) {\n throw new Error(\"ffmpeg stdio streams not available\");\n }\n\n input.pipe(ff.stdin);\n ff.stdout.pipe(output);\n\n let stderr = \"\";\n ff.stderr.on(\"data\", (d) => (stderr += String(d)));\n ff.on(\"close\", (code) => {\n if (ended) return;\n ended = true;\n if ((code ?? 0) !== 0 && stderr.trim()) {\n output.destroy(\n new Error(`ffmpeg exited with code ${code ?? 0}: ${stderr}`),\n );\n } else {\n output.end();\n }\n });\n\n // Add AUD NAL units for H.264 to help ffmpeg with frame boundaries\n const H264_AUD = Buffer.from([0x00, 0x00, 0x00, 0x01, 0x09, 0xf0]);\n\n // Write the entire Annex-B data to ffmpeg\n // For better streaming, we could split by NAL units, but for simplicity we write all at once\n if (videoType === \"H264\") {\n input.write(H264_AUD);\n }\n input.write(annexB);\n input.end();\n\n return {\n mp4: output,\n stop: stopAll,\n };\n }\n\n // ============================================================\n // STANDALONE CAMERA METHODS\n // ============================================================\n // These methods are specifically designed for standalone cameras\n // (non-NVR) connected via TCP. They provide a simplified interface\n // for common operations like listing recordings, streaming playback,\n // downloading clips, and getting thumbnails.\n // ============================================================\n\n /**\n * List recordings from a standalone camera.\n *\n * This method is optimized for standalone cameras (non-NVR) and uses\n * the native Baichuan protocol to list recorded files.\n *\n * @example\n * ```ts\n * const api = new ReolinkBaichuanApi({ host: '192.168.1.100', ... });\n * await api.login();\n *\n * const recordings = await api.standaloneListRecordings({\n * start: new Date('2024-01-20T00:00:00'),\n * end: new Date('2024-01-21T23:59:59'),\n * });\n *\n * for (const file of recordings) {\n * console.log(file.fileName, file.startTime, file.endTime);\n * }\n * ```\n */\n async standaloneListRecordings(params: {\n /** Start time for the search range */\n start: Date;\n /** End time for the search range */\n end: Date;\n /** Stream type to search for (default: mainStream) */\n streamType?: \"mainStream\" | \"subStream\";\n /** Maximum number of files to return (default: 100) */\n count?: number;\n /** Timeout in ms (default: 15000) */\n timeoutMs?: number;\n }): Promise<RecordingFile[]> {\n const channel = 0; // Standalone cameras always use channel 0\n const streamType =\n params.streamType === \"mainStream\" ? \"mainStream\" : \"subStream\";\n const timeoutMs = params.timeoutMs ?? 15_000;\n\n return await this.getVideoclips({\n channel,\n start: params.start,\n end: params.end,\n streamType,\n timeoutMs,\n });\n }\n\n /**\n * Start a streaming replay of a recorded file from a standalone camera.\n *\n * Returns a video stream that emits frames in real-time. The stream can be\n * used for live playback or forwarded to media players.\n *\n * @example\n * ```ts\n * const api = new ReolinkBaichuanApi({ host: '192.168.1.100', ... });\n * await api.login();\n *\n * const recordings = await api.standaloneListRecordings({ ... });\n * const { stream, stop } = await api.standaloneStartReplayStream({\n * fileName: recordings[0].fileName,\n * });\n *\n * stream.on('videoFrame', (data) => {\n * console.log('Video frame:', data.length, 'bytes');\n * });\n *\n * stream.on('audioFrame', (data) => {\n * console.log('Audio frame:', data.length, 'bytes');\n * });\n *\n * // Stop after 10 seconds\n * setTimeout(() => stop(), 10000);\n * ```\n */\n async standaloneStartReplayStream(params: {\n /** Full path to the recording file (from listRecordings) */\n fileName: string;\n /** Stream type (default: mainStream) */\n streamType?: \"mainStream\" | \"subStream\";\n /** Timeout in ms for starting the stream (default: 20000) */\n timeoutMs?: number;\n }): Promise<{\n /** Message number for this stream */\n msgNum: number;\n /** The video stream that emits frames */\n stream: BaichuanVideoStream;\n /** Stop the replay stream */\n stop: () => Promise<void>;\n }> {\n const channel = 0; // Standalone cameras always use channel 0\n const streamType = params.streamType ?? \"mainStream\";\n const timeoutMs = params.timeoutMs ?? 20_000;\n\n await this.client.login();\n\n // IMPORTANT: Always use standalone method for standalone cameras,\n // regardless of whether fileName contains \"/\" (full path).\n // The NVR vs Standalone distinction in startRecordingReplayStream\n // is based on path format, but standalone cameras can have full paths too.\n return await this.startRecordingReplayStreamStandalone({\n channel,\n fileName: params.fileName,\n streamType,\n timeoutMs,\n });\n }\n\n /**\n * Download a recorded file from a standalone camera as MP4.\n *\n * Returns a readable stream of MP4 data that can be piped to a file\n * or sent over HTTP.\n *\n * @example\n * ```ts\n * import { createWriteStream } from 'fs';\n *\n * const api = new ReolinkBaichuanApi({ host: '192.168.1.100', ... });\n * await api.login();\n *\n * const recordings = await api.standaloneListRecordings({ ... });\n * const { mp4, stop } = await api.standaloneDownloadRecording({\n * fileName: recordings[0].fileName,\n * });\n *\n * // Save to file\n * const file = createWriteStream('recording.mp4');\n * mp4.pipe(file);\n *\n * mp4.on('end', () => {\n * console.log('Download complete');\n * });\n * ```\n */\n async standaloneDownloadRecording(params: {\n /** Full path to the recording file (from listRecordings) */\n fileName: string;\n /** Timeout in ms (default: 120000) */\n timeoutMs?: number;\n }): Promise<{\n /** Readable stream of MP4 data */\n mp4: Readable;\n /** Stop the download */\n stop: () => Promise<void>;\n }> {\n const channel = 0; // Standalone cameras always use channel 0\n const timeoutMs = params.timeoutMs ?? 120_000;\n\n return await this.createRecordingDownloadMp4Stream({\n channel,\n fileName: params.fileName,\n timeoutMs,\n });\n }\n\n /**\n * Get a thumbnail (I-frame) from a recorded file on a standalone camera.\n *\n * Returns a raw H.264/H.265 I-frame that can be converted to JPEG using ffmpeg.\n *\n * @example\n * ```ts\n * const api = new ReolinkBaichuanApi({ host: '192.168.1.100', ... });\n * await api.login();\n *\n * const recordings = await api.standaloneListRecordings({ ... });\n * const thumbnail = await api.standaloneGetThumbnail({\n * time: recordings[0].startTime,\n * });\n *\n * console.log('Encoding:', thumbnail.encoding); // 'H264' or 'H265'\n * console.log('Resolution:', thumbnail.streamInfo.width, 'x', thumbnail.streamInfo.height);\n * console.log('Frame size:', thumbnail.frame.length, 'bytes');\n *\n * // Save raw frame (can be converted to JPEG with ffmpeg)\n * fs.writeFileSync('thumbnail.h264', thumbnail.frame);\n * ```\n */\n async standaloneGetThumbnail(params: {\n /** Timestamp to capture (use startTime from a recording file) */\n time: Date;\n /** Snapshot quality: 'main' for full res, 'sub' for smaller (default: sub) */\n snapType?: \"main\" | \"sub\";\n /** Timeout in ms (default: 30000) */\n timeoutMs?: number;\n }): Promise<VideoclipThumbnailResult> {\n const channel = 0; // Standalone cameras always use channel 0\n const snapType = params.snapType ?? \"sub\";\n const timeoutMs = params.timeoutMs ?? 30_000;\n\n return await this.getVideoclipThumbnail({\n channel,\n time: params.time,\n snapType,\n timeoutMs,\n });\n }\n\n /**\n * Subscribe to real-time events from a standalone camera.\n *\n * Events include motion detection, AI detection (person, vehicle, pet, etc.),\n * and other sensor triggers.\n *\n * @example\n * ```ts\n * const api = new ReolinkBaichuanApi({ host: '192.168.1.100', ... });\n * await api.login();\n *\n * const { unsubscribe } = await api.standaloneSubscribeEvents({\n * onEvent: (event) => {\n * console.log('Event:', event.type, event.channel);\n * if (event.type === 'ai') {\n * console.log('AI detection:', event.ai);\n * }\n * },\n * });\n *\n * // Later, stop receiving events\n * await unsubscribe();\n * ```\n */\n async standaloneSubscribeEvents(params: {\n /** Callback for each event received */\n onEvent: (event: ReolinkEvent) => void;\n }): Promise<{\n /** Unsubscribe from events */\n unsubscribe: () => Promise<void>;\n }> {\n // Register the event handler\n this.client.on(\"event\", params.onEvent);\n\n // Subscribe to events\n await this.subscribeEvents();\n\n return {\n unsubscribe: async () => {\n await this.unsubscribeEvents();\n this.client.off(\"event\", params.onEvent);\n },\n };\n }\n\n /**\n * Start a live video stream from a standalone camera.\n *\n * Returns a video stream that emits frames in real-time from the camera's\n * current view (not a recording).\n *\n * @example\n * ```ts\n * const api = new ReolinkBaichuanApi({ host: '192.168.1.100', ... });\n * await api.login();\n *\n * const { stream, stop } = await api.standaloneStartLiveStream({\n * profile: 'main', // or 'sub' for lower quality\n * });\n *\n * stream.on('videoFrame', (data) => {\n * console.log('Live frame:', data.length, 'bytes');\n * });\n *\n * // Stop after 30 seconds\n * setTimeout(() => stop(), 30000);\n * ```\n */\n async standaloneStartLiveStream(params?: {\n /** Stream profile: 'main' for high quality, 'sub' for low quality (default: main) */\n profile?: \"main\" | \"sub\";\n /** Timeout in ms for starting the stream (default: 20000) */\n timeoutMs?: number;\n }): Promise<{\n /** Stop the live stream */\n stop: () => Promise<void>;\n }> {\n const channel = 0; // Standalone cameras always use channel 0\n const profile = params?.profile ?? \"main\";\n\n // Start the video stream (this subscribes to frames)\n await this.startVideoStream(channel, profile);\n\n return {\n stop: async () => {\n await this.stopVideoStream(channel, profile);\n },\n };\n }\n\n /**\n * Get a live snapshot (JPEG) from a standalone camera.\n *\n * @example\n * ```ts\n * import { writeFileSync } from 'fs';\n *\n * const api = new ReolinkBaichuanApi({ host: '192.168.1.100', ... });\n * await api.login();\n *\n * const jpeg = await api.standaloneGetSnapshot();\n * writeFileSync('snapshot.jpg', jpeg);\n * ```\n */\n async standaloneGetSnapshot(): Promise<Buffer> {\n const channel = 0; // Standalone cameras always use channel 0\n return await this.getSnapshot(channel);\n }\n}\n","/**\n * Simple MPEG-TS Muxer for H.264/H.265 video with PTS timestamps.\n *\n * This muxer creates MPEG-TS packets from raw Annex-B video frames,\n * preserving the original PTS timestamps. This allows ffmpeg to read\n * the stream with correct timing without needing -r (framerate) hints.\n *\n * MPEG-TS format:\n * - 188-byte packets\n * - PAT (Program Association Table) - PID 0x0000\n * - PMT (Program Map Table) - PID 0x1000\n * - Video PES - PID 0x0100\n *\n * PTS timestamps are in 90kHz units (standard for MPEG-TS).\n */\n\nconst TS_PACKET_SIZE = 188;\nconst TS_SYNC_BYTE = 0x47;\n\n// PIDs\nconst PAT_PID = 0x0000;\nconst PMT_PID = 0x1000;\nconst VIDEO_PID = 0x0100;\n\n// Stream types\nconst STREAM_TYPE_H264 = 0x1b;\nconst STREAM_TYPE_H265 = 0x24;\n\n// Continuity counters (0-15, wrap around)\nlet patCc = 0;\nlet pmtCc = 0;\nlet videoCc = 0;\n\n/**\n * Create a PAT (Program Association Table) packet.\n */\nfunction createPat(): Buffer {\n const packet = Buffer.alloc(TS_PACKET_SIZE, 0xff);\n\n // TS header (4 bytes)\n packet[0] = TS_SYNC_BYTE;\n packet[1] = 0x40 | ((PAT_PID >> 8) & 0x1f); // payload_unit_start=1, PID high\n packet[2] = PAT_PID & 0xff; // PID low\n packet[3] = 0x10 | (patCc & 0x0f); // no adaptation, payload only, CC\n patCc = (patCc + 1) & 0x0f;\n\n // Pointer field (1 byte) - 0 means table starts immediately\n packet[4] = 0x00;\n\n // PAT section\n let idx = 5;\n packet[idx++] = 0x00; // table_id = 0 (PAT)\n packet[idx++] = 0xb0; // section_syntax_indicator=1, private=0, reserved=11\n packet[idx++] = 13; // section_length (includes CRC)\n packet[idx++] = 0x00; // transport_stream_id high\n packet[idx++] = 0x01; // transport_stream_id low\n packet[idx++] = 0xc1; // reserved, version=0, current_next=1\n packet[idx++] = 0x00; // section_number\n packet[idx++] = 0x00; // last_section_number\n\n // Program 1 -> PMT PID\n packet[idx++] = 0x00; // program_number high\n packet[idx++] = 0x01; // program_number low\n packet[idx++] = 0xe0 | ((PMT_PID >> 8) & 0x1f); // reserved + PMT PID high\n packet[idx++] = PMT_PID & 0xff; // PMT PID low\n\n // CRC32 (calculated over section from table_id to last byte before CRC)\n const crc = crc32Mpeg(packet.subarray(5, idx));\n packet.writeUInt32BE(crc, idx);\n\n return packet;\n}\n\n/**\n * Create a PMT (Program Map Table) packet.\n */\nfunction createPmt(streamType: number): Buffer {\n const packet = Buffer.alloc(TS_PACKET_SIZE, 0xff);\n\n // TS header\n packet[0] = TS_SYNC_BYTE;\n packet[1] = 0x40 | ((PMT_PID >> 8) & 0x1f);\n packet[2] = PMT_PID & 0xff;\n packet[3] = 0x10 | (pmtCc & 0x0f);\n pmtCc = (pmtCc + 1) & 0x0f;\n\n // Pointer field\n packet[4] = 0x00;\n\n // PMT section\n let idx = 5;\n packet[idx++] = 0x02; // table_id = 2 (PMT)\n packet[idx++] = 0xb0; // section_syntax_indicator=1\n packet[idx++] = 18; // section_length\n packet[idx++] = 0x00; // program_number high\n packet[idx++] = 0x01; // program_number low\n packet[idx++] = 0xc1; // reserved, version=0, current_next=1\n packet[idx++] = 0x00; // section_number\n packet[idx++] = 0x00; // last_section_number\n packet[idx++] = 0xe0 | ((VIDEO_PID >> 8) & 0x1f); // reserved + PCR_PID high\n packet[idx++] = VIDEO_PID & 0xff; // PCR_PID low\n packet[idx++] = 0xf0; // reserved + program_info_length high\n packet[idx++] = 0x00; // program_info_length low\n\n // Stream info (video)\n packet[idx++] = streamType; // stream_type (H.264 or H.265)\n packet[idx++] = 0xe0 | ((VIDEO_PID >> 8) & 0x1f); // reserved + elementary_PID high\n packet[idx++] = VIDEO_PID & 0xff; // elementary_PID low\n packet[idx++] = 0xf0; // reserved + ES_info_length high\n packet[idx++] = 0x00; // ES_info_length low\n\n // CRC32\n const crc = crc32Mpeg(packet.subarray(5, idx));\n packet.writeUInt32BE(crc, idx);\n\n return packet;\n}\n\n/**\n * Create TS packets for a video PES (Packetized Elementary Stream).\n *\n * @param data - Annex-B video data (with start codes)\n * @param pts - Presentation timestamp in microseconds\n * @param isKeyframe - Whether this is a keyframe (for random access indicator)\n */\nfunction createVideoPes(\n data: Buffer,\n pts: number,\n isKeyframe: boolean,\n): Buffer[] {\n const packets: Buffer[] = [];\n\n // Convert microseconds to 90kHz PTS\n const pts90k = Math.floor((pts * 90000) / 1_000_000);\n\n // Create PES header\n // PES header: 6 bytes base + 3 bytes PTS header + 5 bytes PTS\n const pesHeaderLen = 14;\n const pesHeader = Buffer.alloc(pesHeaderLen);\n\n let idx = 0;\n // Start code prefix (3 bytes)\n pesHeader[idx++] = 0x00;\n pesHeader[idx++] = 0x00;\n pesHeader[idx++] = 0x01;\n // Stream ID (0xe0 = video)\n pesHeader[idx++] = 0xe0;\n // PES packet length (0 = unbounded for video)\n pesHeader[idx++] = 0x00;\n pesHeader[idx++] = 0x00;\n // PES header flags\n pesHeader[idx++] = 0x80; // '10' marker bits, no scrambling, no priority, no alignment, no copyright, no original\n pesHeader[idx++] = 0x80; // PTS only, no DTS, no ESCR, no ES_rate, no DSM_trick_mode, no additional_copy_info, no CRC, no extension\n // PES header data length\n pesHeader[idx++] = 0x05; // 5 bytes for PTS\n\n // PTS (5 bytes)\n // Format: '0010' + PTS[32..30] + '1' + PTS[29..15] + '1' + PTS[14..0] + '1'\n pesHeader[idx++] = 0x21 | ((pts90k >> 29) & 0x0e); // '0010' + PTS[32:30] + '1'\n pesHeader[idx++] = (pts90k >> 22) & 0xff; // PTS[29:22]\n pesHeader[idx++] = 0x01 | ((pts90k >> 14) & 0xfe); // PTS[21:15] + '1'\n pesHeader[idx++] = (pts90k >> 7) & 0xff; // PTS[14:7]\n pesHeader[idx++] = 0x01 | ((pts90k << 1) & 0xfe); // PTS[6:0] + '1'\n\n // Combine PES header with payload\n const pesData = Buffer.concat([pesHeader, data]);\n let pesOffset = 0;\n\n // Fragment into TS packets\n let isFirst = true;\n while (pesOffset < pesData.length) {\n const packet = Buffer.alloc(TS_PACKET_SIZE, 0xff);\n let pktIdx = 0;\n\n // TS header\n packet[pktIdx++] = TS_SYNC_BYTE;\n packet[pktIdx++] = (isFirst ? 0x40 : 0x00) | ((VIDEO_PID >> 8) & 0x1f);\n packet[pktIdx++] = VIDEO_PID & 0xff;\n\n const remaining = pesData.length - pesOffset;\n const maxPayload = TS_PACKET_SIZE - 4; // 184 bytes\n\n if (remaining >= maxPayload) {\n // Full payload, no adaptation field needed\n packet[pktIdx++] = 0x10 | (videoCc & 0x0f);\n videoCc = (videoCc + 1) & 0x0f;\n pesData.copy(packet, pktIdx, pesOffset, pesOffset + maxPayload);\n pesOffset += maxPayload;\n } else {\n // Need adaptation field for padding\n const adaptLen = maxPayload - remaining - 1; // -1 for adaptation_field_length byte\n\n if (adaptLen < 0) {\n // Edge case: very small remaining data, use stuffing\n packet[pktIdx++] = 0x30 | (videoCc & 0x0f); // adaptation + payload\n videoCc = (videoCc + 1) & 0x0f;\n packet[pktIdx++] = TS_PACKET_SIZE - 4 - 1 - remaining; // adaptation_field_length\n if (isFirst && isKeyframe) {\n packet[pktIdx++] = 0x40; // random_access_indicator\n // Fill rest with stuffing\n for (let i = pktIdx; i < TS_PACKET_SIZE - remaining; i++) {\n packet[i] = 0xff;\n }\n } else {\n packet[pktIdx++] = 0x00; // no flags\n for (let i = pktIdx; i < TS_PACKET_SIZE - remaining; i++) {\n packet[i] = 0xff;\n }\n }\n pesData.copy(packet, TS_PACKET_SIZE - remaining, pesOffset);\n pesOffset += remaining;\n } else {\n // Normal case with adaptation field\n packet[pktIdx++] = 0x30 | (videoCc & 0x0f); // adaptation + payload\n videoCc = (videoCc + 1) & 0x0f;\n\n if (adaptLen === 0) {\n packet[pktIdx++] = 0x00; // adaptation_field_length = 0\n } else {\n packet[pktIdx++] = adaptLen; // adaptation_field_length\n if (isFirst && isKeyframe) {\n packet[pktIdx++] = 0x40; // random_access_indicator\n } else {\n packet[pktIdx++] = 0x00; // no flags\n }\n // Stuffing bytes\n for (let i = 0; i < adaptLen - 1; i++) {\n packet[pktIdx++] = 0xff;\n }\n }\n\n pesData.copy(packet, pktIdx, pesOffset, pesOffset + remaining);\n pesOffset += remaining;\n }\n }\n\n packets.push(packet);\n isFirst = false;\n }\n\n return packets;\n}\n\n/**\n * CRC32 for MPEG-TS (polynomial 0x04c11db7).\n */\nfunction crc32Mpeg(data: Buffer): number {\n let crc = 0xffffffff;\n for (let i = 0; i < data.length; i++) {\n crc ^= data[i]! << 24;\n for (let j = 0; j < 8; j++) {\n if (crc & 0x80000000) {\n crc = ((crc << 1) ^ 0x04c11db7) >>> 0;\n } else {\n crc = (crc << 1) >>> 0;\n }\n }\n }\n return crc >>> 0;\n}\n\nexport interface MpegTsMuxerOptions {\n /** Video codec type */\n videoType: \"H264\" | \"H265\";\n}\n\n/**\n * MPEG-TS Muxer class for streaming video with timestamps.\n */\nexport class MpegTsMuxer {\n private readonly streamType: number;\n private patSent = false;\n private pmtSent = false;\n private patPmtInterval = 0;\n private readonly patPmtIntervalMax = 40; // Send PAT/PMT every ~40 frames\n\n constructor(options: MpegTsMuxerOptions) {\n this.streamType =\n options.videoType === \"H265\" ? STREAM_TYPE_H265 : STREAM_TYPE_H264;\n }\n\n /**\n * Reset continuity counters (call when starting a new stream).\n */\n static resetCounters(): void {\n patCc = 0;\n pmtCc = 0;\n videoCc = 0;\n }\n\n /**\n * Mux a video frame into MPEG-TS packets.\n *\n * @param data - Annex-B video data (with start codes)\n * @param microseconds - Frame timestamp in microseconds\n * @param isKeyframe - Whether this is a keyframe\n * @returns Buffer containing all TS packets for this frame\n */\n mux(data: Buffer, microseconds: number, isKeyframe: boolean): Buffer {\n const packets: Buffer[] = [];\n\n // Send PAT/PMT at start and periodically (especially before keyframes)\n if (\n !this.patSent ||\n !this.pmtSent ||\n isKeyframe ||\n this.patPmtInterval >= this.patPmtIntervalMax\n ) {\n packets.push(createPat());\n packets.push(createPmt(this.streamType));\n this.patSent = true;\n this.pmtSent = true;\n this.patPmtInterval = 0;\n }\n this.patPmtInterval++;\n\n // Create video PES packets\n const videoPackets = createVideoPes(data, microseconds, isKeyframe);\n packets.push(...videoPackets);\n\n return Buffer.concat(packets);\n }\n}\n","import { XMLParser } from \"fast-xml-parser\";\n\nexport type XmlJsonPrimitive = string | number | boolean | null;\nexport type XmlJsonValue = XmlJsonPrimitive | XmlJsonObject | XmlJsonValue[];\nexport type XmlJsonObject = { [key: string]: XmlJsonValue };\n\nconst parser = new XMLParser({\n ignoreAttributes: false,\n attributeNamePrefix: \"@_\",\n textNodeName: \"#text\",\n trimValues: true,\n parseTagValue: true,\n parseAttributeValue: true,\n allowBooleanAttributes: true,\n});\n\nexport function parseXmlToJson<T extends XmlJsonValue = XmlJsonValue>(\n xml: string,\n): T {\n // fast-xml-parser returns `any`; we expose a JSON-friendly type.\n return parser.parse(xml) as T;\n}\n\nfunction stripXmlDeclaration(xml: string): string {\n return String(xml ?? \"\")\n .replace(/^\\s*<\\?xml[^>]*\\?>\\s*/i, \"\")\n .trim();\n}\n\n/**\n * Baichuan often returns XML fragments (multiple top-level tags).\n * This wraps the payload into a synthetic root so it can be parsed reliably.\n */\nexport function parseXmlFragmentToJson<T = XmlJsonValue>(xml: string): T {\n const payload = stripXmlDeclaration(xml);\n if (!payload) return {} as T;\n\n const wrapped = `<root>${payload}</root>`;\n const parsed = parser.parse(wrapped) as { root: T };\n return parsed.root;\n}\n\nexport function safeParseXmlToJson<T extends XmlJsonValue = XmlJsonValue>(\n xml: string,\n): { ok: true; value: T } | { ok: false; error: string } {\n try {\n return { ok: true, value: parseXmlToJson<T>(xml) };\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n return { ok: false, error: msg };\n }\n}\n","import { getXmlText } from \"../../../protocol/xml\";\n\nexport type AbilityInfoResult = Partial<Record<number | \"Host\", Record<string, number | string | undefined>>>;\n\nexport const parseAbilityInfoXml = (xml: string): AbilityInfoResult => {\n const abilities: AbilityInfoResult = {};\n\n const tokenSections = [\n \"system\",\n \"streaming\",\n \"PTZ\",\n \"IO\",\n \"security\",\n \"replay\",\n \"disk\",\n \"network\",\n \"alarm\",\n \"record\",\n \"video\",\n \"image\",\n ];\n\n for (const tokenSection of tokenSections) {\n const sectionRegex = new RegExp(`<${tokenSection}[^>]*>([\\\\s\\\\S]*?)<\\\\/${tokenSection}>`, \"i\");\n const sectionMatch = xml.match(sectionRegex);\n\n if (!sectionMatch) continue;\n\n const sectionXml = sectionMatch[1] ?? \"\";\n const subModuleMatches = sectionXml.matchAll(/<subModule[^>]*>([\\s\\S]*?)<\\/subModule>/g);\n\n for (const match of subModuleMatches) {\n const subModuleXml = match[1] ?? \"\";\n const channelIdText = getXmlText(subModuleXml, \"channelId\") || getXmlText(subModuleXml, \"chnID\");\n const abilityValue = getXmlText(subModuleXml, \"abilityValue\");\n\n if (!abilityValue) continue;\n\n const channelKey: number | \"Host\" = channelIdText ? Number(channelIdText) : \"Host\";\n if (!abilities[channelKey]) abilities[channelKey] = {};\n\n const capabilities = abilityValue\n .split(\",\")\n .map((c) => c.trim())\n .filter(Boolean);\n\n for (const capability of capabilities) {\n abilities[channelKey]![capability] = 1;\n }\n }\n }\n\n const abilityInfoMatch = xml.match(/<AbilityInfo[^>]*>([\\s\\S]*?)<\\/AbilityInfo>/);\n if (abilityInfoMatch) {\n const abilityInfoXml = abilityInfoMatch[1] ?? \"\";\n const directChildren = abilityInfoXml.matchAll(/<([A-Za-z]+)[^>]*>([^<]*)<\\/\\1>/g);\n\n if (!abilities.Host) abilities.Host = {};\n\n for (const childMatch of directChildren) {\n const tagName = childMatch[1];\n const textValue = childMatch[2];\n\n if (!tagName || textValue === undefined) continue;\n if ([\"image\", \"video\", \"subModule\"].includes(tagName)) continue;\n if (tagName === \"channelId\" || tagName === \"abilityValue\") continue;\n\n const numValue = Number(textValue);\n abilities.Host![tagName] = Number.isNaN(numValue) ? textValue : numValue;\n }\n }\n\n if (abilities.Host && Object.keys(abilities.Host).length === 0) {\n delete abilities.Host;\n }\n\n return abilities;\n};\n","import type { BaichuanClient } from \"../../../client/BaichuanClient\";\n\nexport const formatErrorForLog = (e: unknown): string => {\n if (e instanceof Error) {\n const codeValue = Reflect.get(e, \"code\");\n const code = typeof codeValue === \"string\" || typeof codeValue === \"number\" ? ` code=${String(codeValue)}` : \"\";\n return `${e.name}: ${e.message}${code}`;\n }\n if (typeof e === \"string\") return e;\n if (e && typeof e === \"object\") {\n // Many runtimes/plugins serialize errors (losing prototype/stack) into plain objects.\n // Extract the common fields explicitly before falling back to JSON.\n try {\n const msgValue = Reflect.get(e as object, \"message\");\n const nameValue = Reflect.get(e as object, \"name\");\n const codeValue = Reflect.get(e as object, \"code\");\n\n const msg = typeof msgValue === \"string\" ? msgValue : undefined;\n const name = typeof nameValue === \"string\" ? nameValue : undefined;\n const code = typeof codeValue === \"string\" || typeof codeValue === \"number\" ? String(codeValue) : undefined;\n\n if (msg || name || code) {\n const head = name ? `${name}: ` : \"\";\n const tail = code ? ` code=${code}` : \"\";\n return `${head}${msg ?? \"(no message)\"}${tail}`;\n }\n } catch {\n // ignore\n }\n\n try {\n const keys = Object.keys(e);\n const json = JSON.stringify(e);\n return keys.length ? `object keys=[${keys.join(\",\")}]: ${json}` : `object: ${json}`;\n } catch {\n return \"[unserializable object]\";\n }\n }\n return String(e);\n};\n\nexport const formatClientIoForLog = (api: { client?: BaichuanClient }): string => {\n try {\n const c = api.client;\n if (!c) return \"\";\n\n const transport = c.getTransport?.() ?? \"unknown\";\n const connected = c.isSocketConnected?.() ?? false;\n const loggedIn = c.loggedIn === true;\n const lastTx = c.getLastTxInfo?.();\n const lastRx = c.getLastRxInfo?.();\n\n const parts: string[] = [\n `transport=${transport}`,\n `connected=${connected}`,\n `loggedIn=${loggedIn}`,\n lastTx?.cmdId != null ? `lastTxCmdId=${lastTx.cmdId}` : \"\",\n lastTx?.responseCode != null ? `lastTxCode=${lastTx.responseCode}` : \"\",\n lastRx?.cmdId != null ? `lastRxCmdId=${lastRx.cmdId}` : \"\",\n lastRx?.responseCode != null ? `lastRxCode=${lastRx.responseCode}` : \"\",\n ].filter(Boolean);\n\n return parts.length ? ` (${parts.join(\" \")})` : \"\";\n } catch {\n return \"\";\n }\n};\n","import { BC_CMD_ID_GET_AI_ALARM } from \"../../../protocol/constants\";\nimport { getXmlText, xmlEscape } from \"../../../protocol/xml\";\nimport type { AIState } from \"../types\";\nimport { formatErrorForLog } from \"./logging\";\n\nexport type SendXmlLike = (\n params: {\n cmdId: number;\n channel?: number;\n payloadXml?: string;\n channelIdOverride?: number;\n },\n retry?: number,\n) => Promise<string>;\n\nconst looksLikeConnectionDrop = (e: unknown): boolean => {\n const msg = formatErrorForLog(e);\n return (\n msg.includes(\"ECONNRESET\") ||\n msg.includes(\"EPIPE\") ||\n msg.includes(\"socket hang up\") ||\n msg.includes(\"Baichuan socket closed\") ||\n msg.includes(\"timeout\")\n );\n};\n\nexport const getAiStateViaGetAiAlarm = async (params: {\n sendXml: SendXmlLike;\n channel: number;\n candidateTypes?: string[];\n}): Promise<AIState> => {\n const cmdId = BC_CMD_ID_GET_AI_ALARM;\n const ch = params.channel;\n\n const defaultCandidateTypes = [\n \"people\",\n \"vehicle\",\n \"dog_cat\",\n \"face\",\n \"package\",\n ] as const;\n const candidateTypes =\n params.candidateTypes && params.candidateTypes.length > 0\n ? params.candidateTypes\n : [...defaultCandidateTypes];\n let lastErr: unknown;\n\n const tryOnce = async (\n type: string,\n channelIdOverride?: number,\n ): Promise<string> => {\n const payloadXml =\n `<?xml version=\"1.0\" encoding=\"UTF-8\" ?>` +\n `<body>` +\n `<AiDetectCfg version=\"1.1\">` +\n `<chn>${ch}</chn>` +\n `<type>${xmlEscape(type)}</type>` +\n `</AiDetectCfg>` +\n `</body>`;\n\n return await params.sendXml(\n {\n cmdId,\n channel: ch,\n payloadXml,\n ...(channelIdOverride != null ? { channelIdOverride } : {}),\n },\n 0,\n );\n };\n\n for (const type of candidateTypes) {\n try {\n const xml = await tryOnce(type, ch);\n if (xml) {\n return {\n channel: ch,\n alarm_state: Number(getXmlText(xml, \"alarm_state\") ?? \"0\"),\n support: Number(getXmlText(xml, \"support\") ?? \"0\"),\n };\n }\n } catch (e) {\n if (looksLikeConnectionDrop(e)) throw e;\n lastErr = e;\n }\n }\n\n for (const type of candidateTypes) {\n try {\n const xml = await tryOnce(type, undefined);\n if (xml) {\n return {\n channel: ch,\n alarm_state: Number(getXmlText(xml, \"alarm_state\") ?? \"0\"),\n support: Number(getXmlText(xml, \"support\") ?? \"0\"),\n };\n }\n } catch (e) {\n if (looksLikeConnectionDrop(e)) throw e;\n lastErr = e;\n }\n }\n\n throw lastErr instanceof Error\n ? lastErr\n : new Error(String(lastErr ?? \"getAiState failed\"));\n};\n","import { getXmlText } from \"../../../protocol/xml\";\n\nexport type ChannelInfoPushEntry = {\n channel: number;\n name: string;\n uid: string;\n state: string;\n stateLower: string;\n index?: number;\n streamSupport?: string[];\n wifiState?: string;\n networkSegment?: string;\n changed?: boolean;\n abilityChanged?: boolean;\n loginState?: string;\n inferredOnline?: boolean;\n inferredSleep?: boolean;\n isNonePlaceholder: boolean;\n};\n\nconst parseOptionalInt = (value: string | undefined): number | undefined => {\n const v = (value ?? \"\").trim();\n if (!v) return undefined;\n const n = Number.parseInt(v, 10);\n return Number.isFinite(n) ? n : undefined;\n};\n\nconst parseOptionalBoolish = (value: string | undefined): boolean | undefined => {\n const v = (value ?? \"\").trim();\n if (v === \"\") return undefined;\n const n = Number(v);\n if (Number.isFinite(n)) return n > 0;\n return v !== \"0\";\n};\n\nexport const parseChannelInfoPushBlocks = (blocks: string[]): ChannelInfoPushEntry[] => {\n const out: ChannelInfoPushEntry[] = [];\n\n for (const block of blocks) {\n const channelText = getXmlText(block, \"channelId\") ?? getXmlText(block, \"channel\") ?? getXmlText(block, \"chnID\");\n if (!channelText) continue;\n\n const channel = Number.parseInt(channelText, 10);\n if (!Number.isFinite(channel)) continue;\n\n const name = (getXmlText(block, \"devName\") ?? \"\").trim();\n const uid = (getXmlText(block, \"uid\") ?? \"\").trim();\n const state = (getXmlText(block, \"state\") ?? \"\").trim();\n const stateLower = state.trim().toLowerCase();\n\n const index = parseOptionalInt(getXmlText(block, \"index\"));\n const wifiState = (getXmlText(block, \"wifiState\") ?? \"\").trim() || undefined;\n const networkSegment = (getXmlText(block, \"networkSegment\") ?? \"\").trim() || undefined;\n const changed = parseOptionalBoolish(getXmlText(block, \"changed\"));\n const abilityChanged = parseOptionalBoolish(getXmlText(block, \"abilityChanged\"));\n\n const loginState = (getXmlText(block, \"loginState\") ?? \"\").trim().toLowerCase() || undefined;\n const streamSupportText = (getXmlText(block, \"streamSupport\") ?? \"\").trim().toLowerCase();\n const streamSupport = streamSupportText\n ? streamSupportText\n .split(\",\")\n .map((s) => s.trim())\n .filter(Boolean)\n : undefined;\n\n const indexText = (getXmlText(block, \"index\") ?? \"\").trim();\n const isNonePlaceholder =\n stateLower === \"none\" &&\n !name &&\n !uid &&\n !loginState &&\n (streamSupportText === \"none\" || streamSupportText === \"\") &&\n (indexText === \"0\" || indexText === \"\");\n\n const inferredOnline: boolean | undefined =\n stateLower === \"connect\" ? true : stateLower === \"none\" || stateLower === \"disconnect\" ? false : undefined;\n\n const inferredSleep: boolean | undefined =\n loginState\n ? loginState === \"standby\"\n : stateLower === \"connect\"\n ? false\n : stateLower === \"none\" || stateLower === \"disconnect\"\n ? true\n : undefined;\n\n out.push({\n channel,\n name,\n uid,\n state,\n stateLower,\n ...(index !== undefined ? { index } : {}),\n ...(streamSupport?.length ? { streamSupport } : {}),\n ...(wifiState ? { wifiState } : {}),\n ...(networkSegment ? { networkSegment } : {}),\n ...(typeof changed === \"boolean\" ? { changed } : {}),\n ...(typeof abilityChanged === \"boolean\" ? { abilityChanged } : {}),\n ...(loginState ? { loginState } : {}),\n ...(inferredOnline !== undefined ? { inferredOnline } : {}),\n ...(inferredSleep !== undefined ? { inferredSleep } : {}),\n isNonePlaceholder,\n });\n }\n\n return out;\n};\n","import type { ReolinkSimpleEvent } from \"../types\";\nimport type { ChannelInfoPushEntry } from \"./channelInfoPush\";\n\nexport type ChannelPushData = {\n name: string;\n uid: string;\n state: string;\n index?: number;\n streamSupport?: string[];\n wifiState?: string;\n networkSegment?: string;\n changed?: boolean;\n abilityChanged?: boolean;\n stateLower?: string;\n online?: boolean;\n sleeping?: boolean;\n loginState?: string;\n updatedAtMs: number;\n};\n\nconst pickBoolean = (next: boolean | undefined, prev: boolean | undefined): boolean | undefined => {\n if (typeof next === \"boolean\") return next;\n if (typeof prev === \"boolean\") return prev;\n return undefined;\n};\n\nconst pickOptionalString = (next: string | undefined, prev: string | undefined): string | undefined => {\n const n = (next ?? \"\").trim();\n if (n) return next;\n const p = (prev ?? \"\").trim();\n if (p) return prev;\n return undefined;\n};\n\nconst pickOptionalArray = <T>(next: T[] | undefined, prev: T[] | undefined): T[] | undefined => {\n if (Array.isArray(next) && next.length > 0) return next;\n if (Array.isArray(prev) && prev.length > 0) return prev;\n return undefined;\n};\n\nexport const computeChannelPushUpdateFromEntry = (params: {\n entry: ChannelInfoPushEntry;\n existing: ChannelPushData | undefined;\n nowMs: number;\n}): { shouldSkip: boolean; next?: ChannelPushData; events: ReolinkSimpleEvent[] } => {\n const { entry, existing, nowMs } = params;\n const events: ReolinkSimpleEvent[] = [];\n\n if (entry.isNonePlaceholder) {\n const existingStateLower = (existing?.stateLower ?? existing?.state ?? \"\").toLowerCase();\n if (existingStateLower === \"connect\") return { shouldSkip: true, events };\n if (!existing) return { shouldSkip: true, events };\n }\n\n const nextOnline = pickBoolean(entry.inferredOnline, existing?.online);\n if (typeof entry.inferredOnline === \"boolean\" && existing?.online !== entry.inferredOnline) {\n events.push({\n type: entry.inferredOnline ? \"online\" : \"offline\",\n channel: entry.channel,\n timestamp: nowMs,\n });\n }\n\n const nextSleeping = pickBoolean(entry.inferredSleep, existing?.sleeping);\n if (typeof entry.inferredSleep === \"boolean\" && existing?.sleeping !== entry.inferredSleep) {\n events.push({\n type: entry.inferredSleep ? \"sleeping\" : \"awake\",\n channel: entry.channel,\n timestamp: nowMs,\n });\n }\n\n const name = entry.name || existing?.name || \"\";\n const uid = entry.uid || existing?.uid || \"\";\n const state = entry.state || existing?.state || \"\";\n\n const streamSupport = pickOptionalArray(entry.streamSupport, existing?.streamSupport);\n const wifiState = pickOptionalString(entry.wifiState, existing?.wifiState);\n const networkSegment = pickOptionalString(entry.networkSegment, existing?.networkSegment);\n const stateLower = pickOptionalString(entry.stateLower, existing?.stateLower);\n const loginState = pickOptionalString(entry.loginState, existing?.loginState);\n\n const next: ChannelPushData = {\n name,\n uid,\n state,\n updatedAtMs: nowMs,\n ...(typeof entry.index === \"number\"\n ? { index: entry.index }\n : existing?.index !== undefined\n ? { index: existing.index }\n : {}),\n ...(streamSupport != null ? { streamSupport } : {}),\n ...(wifiState != null ? { wifiState } : {}),\n ...(networkSegment != null ? { networkSegment } : {}),\n ...(typeof entry.changed === \"boolean\"\n ? { changed: entry.changed }\n : existing?.changed !== undefined\n ? { changed: existing.changed }\n : {}),\n ...(typeof entry.abilityChanged === \"boolean\"\n ? { abilityChanged: entry.abilityChanged }\n : existing?.abilityChanged !== undefined\n ? { abilityChanged: existing.abilityChanged }\n : {}),\n ...(stateLower != null ? { stateLower } : {}),\n ...(nextOnline !== undefined ? { online: nextOnline } : {}),\n ...(nextSleeping !== undefined ? { sleeping: nextSleeping } : {}),\n ...(loginState != null ? { loginState } : {}),\n };\n\n return { shouldSkip: false, next, events };\n};\n\nexport const buildChannelPushDataLogSnapshot = (channelPushData: Map<number, ChannelPushData>): {\n result: Record<number, { name: string; uid: string; state: string }>;\n storedChannels: string[];\n} => {\n const resultObj: Record<number, { name: string; uid: string; state: string }> = {};\n for (const [channel, info] of channelPushData.entries()) {\n if ((info.stateLower ?? info.state).toLowerCase() === \"none\") continue;\n resultObj[channel] = { name: info.name, uid: info.uid, state: info.state };\n }\n return { result: resultObj, storedChannels: Object.keys(resultObj) };\n};\n","import type { AIEvent, ReolinkEvent, ReolinkSimpleEvent, ReolinkSimpleEventType } from \"../types\";\n\nexport const mapToSimpleEvent = (event: ReolinkEvent): ReolinkSimpleEvent | null => {\n const timestamp = event.timestamp ?? Date.now();\n\n if (event.type === \"motion\") {\n return { type: \"motion\", channel: event.channel, timestamp };\n }\n\n if (event.type === \"visitor\") {\n return { type: \"doorbell\", channel: event.channel, timestamp };\n }\n\n if (event.type === \"daynight\") {\n return { type: \"daynight\", channel: event.channel, timestamp };\n }\n\n if (event.type === \"ai\") {\n const aiType = event.ai?.type;\n\n const map: Record<NonNullable<AIEvent[\"type\"]>, ReolinkSimpleEventType> = {\n people: \"people\",\n vehicle: \"vehicle\",\n dog_cat: \"animal\",\n face: \"face\",\n package: \"package\",\n other: \"other\",\n };\n\n return {\n type: aiType ? map[aiType] : \"other\",\n channel: event.channel,\n timestamp,\n };\n }\n\n return null;\n};\n","export function parseNumber(v: string | undefined): number | undefined {\n if (v == null) return undefined;\n const n = Number(v);\n return Number.isFinite(n) ? n : undefined;\n}\n\nexport function parseBoolean01(v: string | undefined): boolean | undefined {\n if (v == null) return undefined;\n const s = v.trim();\n if (s === \"1\" || s.toLowerCase() === \"true\") return true;\n if (s === \"0\" || s.toLowerCase() === \"false\") return false;\n return undefined;\n}\n","import type { PipPosition } from \"../../../multifocal/compositeStream\";\n\nexport const resolvePipMarginPx = (\n mainWidth: number,\n mainHeight: number,\n rawMargin: unknown,\n defaultPx = 10,\n): number => {\n const v = Number(rawMargin);\n if (rawMargin === undefined || rawMargin === null) return defaultPx;\n if (!Number.isFinite(v) || v < 0) return defaultPx;\n\n // Legacy: values > 1 are treated as pixels.\n if (v > 1) return Math.floor(v);\n\n // New: treat as fraction (0..1) of output size.\n const base = Math.min(Math.max(1, Math.floor(mainWidth)), Math.max(1, Math.floor(mainHeight)));\n return Math.max(0, Math.floor(base * v));\n};\n\nexport const calculatePipOverlayPosition = (params: {\n position: PipPosition;\n mainWidth: number;\n mainHeight: number;\n pipWidth: number;\n pipHeight: number;\n margin: number;\n}): { left: number; top: number } => {\n const pipW = Math.max(1, Math.floor(params.pipWidth));\n const pipH = Math.max(1, Math.floor(params.pipHeight));\n const margin = Math.max(0, Math.floor(params.margin));\n const mainW = Math.max(1, Math.floor(params.mainWidth));\n const mainH = Math.max(1, Math.floor(params.mainHeight));\n\n const clamp = (x: number, min: number, max: number) => Math.min(Math.max(x, min), max);\n const maxX = Math.max(0, mainW - pipW);\n const maxY = Math.max(0, mainH - pipH);\n\n let left = margin;\n let top = margin;\n\n switch (params.position) {\n case \"top-left\":\n left = margin;\n top = margin;\n break;\n case \"top-right\":\n left = mainW - pipW - margin;\n top = margin;\n break;\n case \"bottom-left\":\n left = margin;\n top = mainH - pipH - margin;\n break;\n case \"bottom-right\":\n left = mainW - pipW - margin;\n top = mainH - pipH - margin;\n break;\n case \"center\":\n left = Math.floor((mainW - pipW) / 2);\n top = Math.floor((mainH - pipH) / 2);\n break;\n case \"top-center\":\n left = Math.floor((mainW - pipW) / 2);\n top = margin;\n break;\n case \"bottom-center\":\n left = Math.floor((mainW - pipW) / 2);\n top = mainH - pipH - margin;\n break;\n case \"left-center\":\n left = margin;\n top = Math.floor((mainH - pipH) / 2);\n break;\n case \"right-center\":\n left = mainW - pipW - margin;\n top = Math.floor((mainH - pipH) / 2);\n break;\n }\n\n return {\n left: clamp(left, 0, maxX),\n top: clamp(top, 0, maxY),\n };\n};\n","import type { BaichuanClient } from \"../../../client/BaichuanClient\";\nimport { buildPtzPresetXmlV2 } from \"../../../protocol/xml\";\nimport type { PtzCommand } from \"../types\";\n\nexport type PtzDirection = \"up\" | \"down\" | \"left\" | \"right\" | \"stop\";\n\nexport const resolvePtzDirection = (command: PtzCommand): PtzDirection => {\n if (command.action === \"stop\") return \"stop\";\n\n const cmdMap: Record<string, Exclude<PtzDirection, \"stop\">> = {\n Left: \"left\",\n Right: \"right\",\n Up: \"up\",\n Down: \"down\",\n };\n\n const mapped = cmdMap[command.command];\n if (!mapped) {\n throw new Error(`Unsupported PTZ command for MSG_ID_PTZ_CONTROL: ${command.command}`);\n }\n\n return mapped;\n};\n\nexport const resolvePtzSpeed = (direction: PtzDirection, rawSpeed: number | undefined): number => {\n if (direction === \"stop\") return 0;\n\n if (rawSpeed === undefined) return 32;\n\n if (rawSpeed > 0 && rawSpeed <= 1) {\n return Math.max(1, rawSpeed * 64);\n }\n\n return rawSpeed;\n};\n\nexport const extractFrameErrorDetails = (params: {\n client: BaichuanClient;\n frame: { body: Buffer; header: { channelId: number; responseCode: number } };\n maxChars?: number;\n}): string => {\n if (params.frame.body.length === 0) return \"\";\n\n try {\n const tryDecryptXml = (params.client as unknown as { tryDecryptXml?: (...args: any[]) => string | undefined }).tryDecryptXml;\n if (!tryDecryptXml) return \"\";\n\n const errorXml = tryDecryptXml.call(params.client, params.frame.body, params.frame.header.channelId, (params.client as any).enc);\n if (!errorXml) return \"\";\n\n const maxChars = params.maxChars ?? 200;\n return ` - Error details: ${errorXml.substring(0, maxChars)}`;\n } catch {\n return \"\";\n }\n};\n\nexport const buildDeletePtzPresetAttempts = (params: {\n channelId: number;\n presetId: number;\n currentName?: string;\n}): Array<{ payloadXml: string; label: string }> => {\n return [\n {\n label: \"command=delPos\",\n payloadXml: buildPtzPresetXmlV2(params.channelId, params.presetId, \"delPos\"),\n },\n {\n label: \"enable=0 (no name)\",\n payloadXml: buildPtzPresetXmlV2(params.channelId, params.presetId, \"setPos\", { enable: 0 }),\n },\n {\n label: \"name='' (no enable)\",\n payloadXml: buildPtzPresetXmlV2(params.channelId, params.presetId, \"setPos\", { name: \"\" }),\n },\n {\n label: \"enable=0 name=''\",\n payloadXml: buildPtzPresetXmlV2(params.channelId, params.presetId, \"setPos\", { name: \"\", enable: 0 }),\n },\n ...(params.currentName\n ? [\n {\n label: \"enable=0 name=current\",\n payloadXml: buildPtzPresetXmlV2(params.channelId, params.presetId, \"setPos\", {\n name: params.currentName,\n enable: 0,\n }),\n },\n ]\n : []),\n {\n label: \"enable=0 name='Preset N'\",\n payloadXml: buildPtzPresetXmlV2(params.channelId, params.presetId, \"setPos\", {\n name: `Preset ${params.presetId}`,\n enable: 0,\n }),\n },\n ];\n};\n\nexport const isPresetEffectivelyDeleted = (preset: { name?: unknown; enable?: unknown } | undefined): boolean => {\n if (!preset) return true;\n\n const nameEmpty = preset.name !== undefined && String(preset.name).trim() === \"\";\n const disabled = preset.enable !== undefined && String(preset.enable).trim() === \"0\";\n\n return nameEmpty || disabled;\n};\n\nexport const runDeletePtzPresetAttempts = async (params: {\n attempts: Array<{ payloadXml: string; label: string }>;\n send: (payloadXml: string, label: string) => Promise<{ responseCode: number }>;\n verify?: () => Promise<boolean>;\n}): Promise<{ any200: boolean; verified: boolean; lastError: unknown }> => {\n let lastError: unknown;\n let any200 = false;\n let verified = false;\n\n for (const a of params.attempts) {\n try {\n const res = await params.send(a.payloadXml, a.label);\n\n if (res.responseCode !== 200) {\n throw new Error(`PTZ preset delete rejected (response_code ${res.responseCode})`);\n }\n\n any200 = true;\n\n if (params.verify) {\n try {\n if (await params.verify()) {\n verified = true;\n return { any200, verified, lastError };\n }\n } catch (e) {\n // Soft failure: continue with the next attempt.\n lastError = e;\n }\n }\n } catch (e) {\n lastError = e;\n }\n }\n\n return { any200, verified, lastError };\n};\n","import { getXmlText } from \"../../protocol/xml\";\nimport type { RecordingDetectionClass, RecordingFile } from \"./types\";\nimport { parseRecordingFileName } from \"./recordingFileName\";\n\ntype TalkAbility = import(\"./types\").TalkAbility;\ntype TalkAudioConfig = import(\"./types\").TalkAudioConfig;\n\n/**\n * Build detectionClasses array from parsedFileName flags and/or recordType string.\n */\nconst buildDetectionClasses = (\n parsed: ReturnType<typeof parseRecordingFileName> | undefined,\n recordType: string | undefined,\n): RecordingDetectionClass[] => {\n const classes: RecordingDetectionClass[] = [];\n const flags = parsed?.flags;\n\n // From hex-parsed flags\n if (flags) {\n if (flags.aiPerson) classes.push(\"person\");\n if (flags.aiVehicle) classes.push(\"vehicle\");\n if (flags.aiAnimal) classes.push(\"animal\");\n if (flags.aiFace) classes.push(\"face\");\n if (flags.motion) classes.push(\"motion\");\n if (flags.schedule) classes.push(\"schedule\");\n if (flags.doorbell) classes.push(\"doorbell\");\n if (flags.package) classes.push(\"package\");\n if (flags.rf) classes.push(\"rf\");\n if (flags.aiOther) classes.push(\"other\");\n }\n\n // From recordType string (if flags didn't provide anything)\n if (classes.length === 0 && recordType) {\n const rt = recordType.toLowerCase();\n if (rt.includes(\"people\") || rt.includes(\"person\")) classes.push(\"person\");\n if (rt.includes(\"vehicle\")) classes.push(\"vehicle\");\n if (rt.includes(\"dog_cat\") || rt.includes(\"animal\")) classes.push(\"animal\");\n if (rt.includes(\"face\")) classes.push(\"face\");\n if (rt.includes(\"md\") || rt.includes(\"motion\")) classes.push(\"motion\");\n if (rt.includes(\"sched\")) classes.push(\"schedule\");\n if (rt.includes(\"visitor\") || rt.includes(\"doorbell\"))\n classes.push(\"doorbell\");\n if (rt.includes(\"package\")) classes.push(\"package\");\n if (rt.includes(\"rf\")) classes.push(\"rf\");\n }\n\n // Default to motion\n if (classes.length === 0) {\n classes.push(\"motion\");\n }\n\n return classes;\n};\n\nexport const getXmlTexts = <T extends string>(\n xml: string,\n tags: readonly T[],\n): Partial<Record<T, string>> => {\n const out: Partial<Record<T, string>> = {};\n for (const tag of tags) {\n const v = getXmlText(xml, tag);\n if (v !== undefined) out[tag] = v;\n }\n return out;\n};\n\nexport const getXmlBlocks = (xml: string, tagName: string): string[] => {\n const re = new RegExp(`<${tagName}\\\\b[^>]*>([\\\\s\\\\S]*?)<\\\\/${tagName}>`, \"g\");\n const out: string[] = [];\n let match: RegExpExecArray | null;\n // eslint-disable-next-line no-cond-assign\n while ((match = re.exec(xml))) {\n out.push(match[1] ?? \"\");\n }\n return out;\n};\n\nexport const parseXmlDateTimeBlock = (block: string): Date | undefined => {\n const year = Number.parseInt(getXmlText(block, \"year\") ?? \"\", 10);\n const month = Number.parseInt(getXmlText(block, \"month\") ?? \"\", 10);\n const day = Number.parseInt(getXmlText(block, \"day\") ?? \"\", 10);\n const hour = Number.parseInt(getXmlText(block, \"hour\") ?? \"\", 10);\n const minute = Number.parseInt(getXmlText(block, \"minute\") ?? \"\", 10);\n const second = Number.parseInt(getXmlText(block, \"second\") ?? \"\", 10);\n\n if ([year, month, day, hour, minute, second].every(Number.isFinite)) {\n // Parse as LOCAL TIME because camera timestamps represent local time on the camera.\n // When the camera says \"08:30:00\", it means 8:30 AM in the camera's configured timezone.\n // By parsing as local time (assuming server and camera share the same timezone),\n // the Date object will correctly represent that moment in time.\n return new Date(year, month - 1, day, hour, minute, second);\n }\n\n // Some firmwares encode the timestamp as plain text instead of nested tags.\n const text = block.replace(/<[^>]*>/g, \"\").trim();\n const m = text.match(\n /(\\d{4})[-/](\\d{1,2})[-/](\\d{1,2})(?:[ T](\\d{1,2}):(\\d{1,2})(?::(\\d{1,2}))?)?/,\n );\n if (!m) return undefined;\n const y = Number.parseInt(m[1] ?? \"\", 10);\n const mo = Number.parseInt(m[2] ?? \"\", 10);\n const da = Number.parseInt(m[3] ?? \"\", 10);\n const ho = Number.parseInt(m[4] ?? \"0\", 10);\n const mi = Number.parseInt(m[5] ?? \"0\", 10);\n const se = Number.parseInt(m[6] ?? \"0\", 10);\n if (![y, mo, da, ho, mi, se].every(Number.isFinite)) return undefined;\n return new Date(y, mo - 1, da, ho, mi, se);\n};\n\nexport const parseRecordingFilesFromXml = (xml: string): RecordingFile[] => {\n const out: RecordingFile[] = [];\n\n // FileInfoList commonly returns <FileInfo> blocks with <name> and/or <Id>.\n const fileInfoBlocks = getXmlBlocks(xml, \"FileInfo\");\n for (const b of fileInfoBlocks) {\n const id =\n getXmlText(b, \"Id\") ?? getXmlText(b, \"ID\") ?? getXmlText(b, \"id\");\n const name = getXmlText(b, \"name\") ?? getXmlText(b, \"fileName\");\n const chosen = (id ?? name)?.trim();\n if (!chosen) continue;\n\n const item: RecordingFile = { fileName: chosen };\n if (name != null && name.trim()) item.name = name.trim();\n if (id != null && id.trim()) item.id = id.trim();\n\n const recordType =\n getXmlText(b, \"type\") ??\n getXmlText(b, \"recordType\") ??\n getXmlText(b, \"alarmType\");\n if (recordType != null) item.recordType = recordType;\n\n const sizeText = getXmlText(b, \"size\") ?? getXmlText(b, \"fileSize\");\n const sizeBytes = sizeText ? Number.parseInt(sizeText, 10) : undefined;\n if (sizeBytes != null && Number.isFinite(sizeBytes))\n item.sizeBytes = sizeBytes;\n\n const start = getXmlBlocks(b, \"startTime\")[0];\n const end = getXmlBlocks(b, \"endTime\")[0];\n const startDt = start ? parseXmlDateTimeBlock(start) : undefined;\n const endDt = end ? parseXmlDateTimeBlock(end) : undefined;\n if (startDt) item.startTime = startDt;\n if (endDt) item.endTime = endDt;\n\n const parsed = parseRecordingFileName(item.name ?? item.fileName);\n if (parsed) {\n item.parsedFileName = parsed;\n if (!item.startTime) item.startTime = parsed.start;\n if (!item.endTime) item.endTime = parsed.end;\n }\n\n item.detectionClasses = buildDetectionClasses(parsed, item.recordType);\n\n out.push(item);\n }\n\n // Preferred: parse <File> blocks.\n const fileBlocks = getXmlBlocks(xml, \"File\");\n for (const b of fileBlocks) {\n const fileName = (\n getXmlText(b, \"fileName\") ?? getXmlText(b, \"name\")\n )?.trim();\n if (!fileName) continue;\n\n const sizeText = getXmlText(b, \"size\") ?? getXmlText(b, \"fileSize\");\n const sizeBytes = sizeText ? Number.parseInt(sizeText, 10) : undefined;\n const recordType =\n getXmlText(b, \"type\") ??\n getXmlText(b, \"recordType\") ??\n getXmlText(b, \"alarmType\");\n\n const start = getXmlBlocks(b, \"startTime\")[0];\n const end = getXmlBlocks(b, \"endTime\")[0];\n\n const item: RecordingFile = { fileName };\n if (sizeBytes != null && Number.isFinite(sizeBytes))\n item.sizeBytes = sizeBytes;\n if (recordType != null) item.recordType = recordType;\n\n const startDt = start ? parseXmlDateTimeBlock(start) : undefined;\n const endDt = end ? parseXmlDateTimeBlock(end) : undefined;\n if (startDt) item.startTime = startDt;\n if (endDt) item.endTime = endDt;\n\n const parsed = parseRecordingFileName(item.fileName);\n if (parsed) {\n item.parsedFileName = parsed;\n if (!item.startTime) item.startTime = parsed.start;\n if (!item.endTime) item.endTime = parsed.end;\n }\n\n item.detectionClasses = buildDetectionClasses(parsed, item.recordType);\n\n out.push(item);\n }\n\n // Fallback: any <fileName> tags.\n if (out.length === 0) {\n const re = /<fileName>([\\s\\S]*?)<\\/fileName>/g;\n const seenNames = new Set<string>();\n let match: RegExpExecArray | null;\n // eslint-disable-next-line no-cond-assign\n while ((match = re.exec(xml))) {\n const fileName = (match[1] ?? \"\").trim();\n if (!fileName) continue;\n if (seenNames.has(fileName)) continue;\n seenNames.add(fileName);\n\n const item: RecordingFile = { fileName };\n const parsed = parseRecordingFileName(fileName);\n if (parsed) {\n item.parsedFileName = parsed;\n item.startTime = parsed.start;\n item.endTime = parsed.end;\n }\n item.detectionClasses = buildDetectionClasses(parsed, undefined);\n out.push(item);\n }\n }\n\n // Alarm video list: <alarmVideo><fileName>...</fileName><alarmType>...</alarmType>...</alarmVideo>\n const alarmBlocks = getXmlBlocks(xml, \"alarmVideo\");\n if (alarmBlocks.length > 0) {\n const byName = new Map<string, RecordingFile>();\n for (const existing of out) {\n const key = existing.fileName.trim();\n if (!key) continue;\n if (!byName.has(key)) byName.set(key, existing);\n }\n\n for (const b of alarmBlocks) {\n const fileNameRaw = getXmlText(b, \"fileName\") ?? getXmlText(b, \"name\");\n const fileName = fileNameRaw?.trim();\n if (!fileName) continue;\n\n const candidateId =\n getXmlText(b, \"fileId\") ??\n getXmlText(b, \"recFileId\") ??\n getXmlText(b, \"recFileName\") ??\n getXmlText(b, \"recFile\") ??\n getXmlText(b, \"recordFile\") ??\n getXmlText(b, \"Id\") ??\n getXmlText(b, \"ID\") ??\n getXmlText(b, \"id\");\n const alarmId = (candidateId ?? \"\").trim();\n\n const alarmType = getXmlText(b, \"alarmType\")?.trim();\n const start = getXmlBlocks(b, \"startTime\")[0];\n const end = getXmlBlocks(b, \"endTime\")[0];\n const startDt = start ? parseXmlDateTimeBlock(start) : undefined;\n const endDt = end ? parseXmlDateTimeBlock(end) : undefined;\n\n const target = byName.get(fileName) ?? { fileName };\n if (alarmId && !target.id) target.id = alarmId;\n if (alarmType) target.recordType = alarmType;\n if (startDt) target.startTime = startDt;\n if (endDt) target.endTime = endDt;\n\n if (!target.parsedFileName) {\n const parsed = parseRecordingFileName(target.fileName);\n if (parsed) {\n target.parsedFileName = parsed;\n if (!target.startTime) target.startTime = parsed.start;\n if (!target.endTime) target.endTime = parsed.end;\n }\n }\n\n if (!byName.has(fileName)) {\n out.push(target);\n byName.set(fileName, target);\n }\n }\n }\n\n // De-dup by fileName.\n const seen = new Set<string>();\n return out.filter((f) => {\n const key = f.fileName.trim();\n if (seen.has(key)) return false;\n seen.add(key);\n return true;\n });\n};\n\nfunction parseTalkAudioConfig(block: string): TalkAudioConfig | null {\n const audioType = getXmlText(block, \"audioType\");\n const sampleRate = Number.parseInt(getXmlText(block, \"sampleRate\") ?? \"\", 10);\n const samplePrecision = Number.parseInt(\n getXmlText(block, \"samplePrecision\") ?? \"\",\n 10,\n );\n const lengthPerEncoder = Number.parseInt(\n getXmlText(block, \"lengthPerEncoder\") ?? \"\",\n 10,\n );\n const soundTrack = getXmlText(block, \"soundTrack\");\n const priorityText = getXmlText(block, \"priority\");\n\n if (\n !audioType ||\n !Number.isFinite(sampleRate) ||\n !Number.isFinite(samplePrecision) ||\n !Number.isFinite(lengthPerEncoder) ||\n !soundTrack\n ) {\n return null;\n }\n\n const config: TalkAudioConfig = {\n audioType,\n sampleRate,\n samplePrecision,\n lengthPerEncoder,\n soundTrack,\n };\n\n if (priorityText !== undefined) {\n const pr = Number.parseInt(priorityText, 10);\n if (Number.isFinite(pr)) config.priority = pr;\n }\n\n return config;\n}\n\nexport const parseTalkAbilityXml = (xml: string): TalkAbility => {\n const talkAbilityBlock = getXmlBlocks(xml, \"TalkAbility\")[0];\n if (!talkAbilityBlock) {\n throw new Error(\"TalkAbility XML not found in response\");\n }\n\n const duplexListBlocks = getXmlBlocks(talkAbilityBlock, \"duplexList\");\n const duplexList = duplexListBlocks\n .map((b) => getXmlText(b, \"duplex\"))\n .filter((v): v is string => Boolean(v));\n\n const audioStreamModeListBlocks = getXmlBlocks(\n talkAbilityBlock,\n \"audioStreamModeList\",\n );\n const audioStreamModeList = audioStreamModeListBlocks\n .map((b) => getXmlText(b, \"audioStreamMode\"))\n .filter((v): v is string => Boolean(v));\n\n const audioConfigBlocks = getXmlBlocks(talkAbilityBlock, \"audioConfig\");\n const audioConfigList = audioConfigBlocks\n .map(parseTalkAudioConfig)\n .filter((v): v is TalkAudioConfig => Boolean(v));\n\n return {\n duplexList,\n audioStreamModeList,\n audioConfigList,\n };\n};\n","import { getXmlText } from \"../../../protocol/xml\";\nimport { getXmlBlocks } from \"../xmlUtils\";\nimport { parseBoolean01, parseNumber } from \"./parsing\";\nimport { parseXmlToJson } from \"./xml\";\nimport type {\n BaichuanCoordinatePointListPush,\n BaichuanDingdongListPush,\n BaichuanNetInfoPush,\n BaichuanSerialPush,\n BaichuanSleepStatusPush,\n BaichuanVideoInputPush,\n} from \"../types\";\n\nexport const parseVideoInputPushXml = (xml: string): BaichuanVideoInputPush => {\n const channelId = parseNumber(getXmlText(xml, \"channelId\"));\n const bright = parseNumber(getXmlText(xml, \"bright\"));\n const contrast = parseNumber(getXmlText(xml, \"contrast\"));\n const saturation = parseNumber(getXmlText(xml, \"saturation\"));\n const hue = parseNumber(getXmlText(xml, \"hue\"));\n const sharpen = parseNumber(getXmlText(xml, \"sharpen\"));\n const corridorAbility = parseNumber(getXmlText(xml, \"corridorAbility\"));\n const corridorMode = getXmlText(xml, \"corridorMode\")?.trim();\n\n return {\n ...(channelId != null ? { channelId } : {}),\n ...(bright != null ? { bright } : {}),\n ...(contrast != null ? { contrast } : {}),\n ...(saturation != null ? { saturation } : {}),\n ...(hue != null ? { hue } : {}),\n ...(sharpen != null ? { sharpen } : {}),\n ...(corridorAbility != null ? { corridorAbility } : {}),\n ...(corridorMode ? { corridorMode } : {}),\n };\n};\n\nexport const parseSerialPushXml = (xml: string): BaichuanSerialPush => {\n const channelId = parseNumber(getXmlText(xml, \"channelId\"));\n const baudRate = parseNumber(getXmlText(xml, \"baudRate\"));\n const dataBit = getXmlText(xml, \"dataBit\")?.trim();\n const stopBit = getXmlText(xml, \"stopBit\")?.trim();\n const parity = getXmlText(xml, \"parity\")?.trim();\n const flowControl = getXmlText(xml, \"flowControl\")?.trim();\n const controlProtocol = getXmlText(xml, \"controlProtocol\")?.trim();\n const controlAddress = parseNumber(getXmlText(xml, \"controlAddress\"));\n\n return {\n ...(channelId != null ? { channelId } : {}),\n ...(baudRate != null ? { baudRate } : {}),\n ...(dataBit ? { dataBit } : {}),\n ...(stopBit ? { stopBit } : {}),\n ...(parity ? { parity } : {}),\n ...(flowControl ? { flowControl } : {}),\n ...(controlProtocol ? { controlProtocol } : {}),\n ...(controlAddress != null ? { controlAddress } : {}),\n };\n};\n\nexport const parseNetInfoPushXml = (xml: string): BaichuanNetInfoPush => {\n const netType = getXmlText(xml, \"net_type\")?.trim();\n const signal = parseNumber(getXmlText(xml, \"signal\"));\n return {\n ...(netType ? { netType } : {}),\n ...(signal != null ? { signal } : {}),\n };\n};\n\nexport const parseDingdongListPushXml = (\n xml: string,\n): BaichuanDingdongListPush => {\n const pairedListBlock = getXmlBlocks(xml, \"pairedList\")[0];\n\n // Best-effort count: count direct child \"item\" blocks when present.\n const pairedCount = pairedListBlock\n ? getXmlBlocks(pairedListBlock, \"item\").length\n : 0;\n\n const maxPairNumber = parseNumber(getXmlText(xml, \"maxPairNumber\"));\n const channel = parseNumber(getXmlText(xml, \"channel\"));\n const pairedList = pairedListBlock\n ? parseXmlToJson(pairedListBlock)\n : undefined;\n\n return {\n ...(maxPairNumber != null ? { maxPairNumber } : {}),\n ...(channel != null ? { channel } : {}),\n ...(Number.isFinite(pairedCount) ? { pairedCount } : {}),\n ...(pairedList != null ? { pairedList } : {}),\n };\n};\n\nexport const parseSleepStatusPushXml = (\n xml: string,\n): BaichuanSleepStatusPush => {\n const rawSleep = parseNumber(getXmlText(xml, \"sleep\"));\n const statusListBlock = getXmlBlocks(xml, \"statusList\")[0];\n\n const sleep =\n rawSleep != null ? rawSleep > 0 : parseBoolean01(getXmlText(xml, \"sleep\"));\n\n return {\n ...(rawSleep != null ? { rawSleep } : {}),\n ...(sleep != null ? { sleep } : {}),\n statusListPresent: statusListBlock != null,\n };\n};\n\nexport const parseCoordinatePointListPushXml = (\n xml: string,\n): BaichuanCoordinatePointListPush => {\n // No rich samples yet in our corpus; parse any <coordinatePoint> blocks if present.\n const pointBlocks = getXmlBlocks(xml, \"coordinatePoint\");\n const points = pointBlocks\n .map((b) => {\n const x = parseNumber(getXmlText(b, \"x\"));\n const y = parseNumber(getXmlText(b, \"y\"));\n if (x == null || y == null) return undefined;\n return { x, y };\n })\n .filter((p): p is { x: number; y: number } => p != null);\n\n return {\n count: points.length,\n ...(points.length ? { points } : {}),\n };\n};\n","import {\n BC_CLASS_FILE_DOWNLOAD,\n BC_CMD_ID_FILE_INFO_LIST_DOWNLOAD,\n} from \"../../../protocol/constants\";\nimport { buildBinaryExtensionXml, xmlEscape } from \"../../../protocol/xml\";\n\nexport const getRecordingNameFromFileName = (fileName: string): string => {\n if (!fileName.includes(\"/\")) return fileName;\n return fileName.split(\"/\").filter(Boolean).at(-1) ?? fileName;\n};\n\nexport const buildFileInfoListDownloadXml = (params: {\n channel: number;\n uid: string;\n fileName: string;\n xmlChannelId?: number;\n}): string => {\n const name = getRecordingNameFromFileName(params.fileName);\n const xmlCh = params.xmlChannelId ?? params.channel;\n\n return `<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<body>\n<FileInfoList version=\"1.1\">\n<FileInfo>\n<channelId>${xmlCh}</channelId>\n<uid>${xmlEscape(params.uid)}</uid>\n<fileName>${xmlEscape(params.fileName)}</fileName>\n<name>${xmlEscape(name)}</name>\n<Id>${xmlEscape(params.fileName)}</Id>\n</FileInfo>\n</FileInfoList>\n</body>`;\n};\n\nexport const sanitizeDownloadFilename = (fileName: string): string => {\n return fileName.replaceAll(\"/\", \"_\").replaceAll(\"\\\\\", \"_\");\n};\n\nexport const buildHttpVodSourceCandidates = (fileName: string): string[] => {\n const out: string[] = [];\n const pushUnique = (s: string | undefined) => {\n const v = s?.trim();\n if (!v) return;\n if (!out.includes(v)) out.push(v);\n };\n\n pushUnique(fileName);\n pushUnique(fileName.replace(/^\\/mnt\\/[a-zA-Z0-9]+\\//, \"\"));\n pushUnique(fileName.replace(/^\\//, \"\"));\n\n return out;\n};\n\nexport const parseRecStartParamIfPresent = (\n fileName: string,\n): string | undefined => {\n const m = /Rec(\\w{3})(?:_|_DST)(\\d{8})_(\\d{6})_.*/.exec(fileName);\n if (!m) return undefined;\n return `${m[2]}${m[3]}`;\n};\n\nexport type SendBinaryLike = (params: {\n cmdId: number;\n channel?: number;\n messageClass?: number;\n extensionXml?: string;\n payloadXml?: string;\n timeoutMs?: number;\n}) => Promise<Buffer>;\n\nexport const downloadRecordingViaFileInfoList = async (params: {\n sendBinary: SendBinaryLike;\n channel: number;\n uid: string;\n fileName: string;\n timeoutMs?: number;\n}): Promise<Buffer> => {\n const payloadXml = buildFileInfoListDownloadXml({\n channel: params.channel,\n uid: params.uid,\n fileName: params.fileName,\n });\n\n return await params.sendBinary({\n cmdId: BC_CMD_ID_FILE_INFO_LIST_DOWNLOAD,\n channel: params.channel,\n messageClass: BC_CLASS_FILE_DOWNLOAD,\n extensionXml: buildBinaryExtensionXml(params.channel),\n payloadXml,\n timeoutMs: params.timeoutMs ?? 120_000,\n });\n};\n","import { xmlEscape } from \"../../../protocol/xml\";\n\nexport type RecordingReplayStreamType = \"mainStream\" | \"subStream\";\nexport type RecordingReplayIFrameMode = \"b\" | \"i\" | \"both\" | true | false;\n\nexport const parseRecStartParamIfPresent = (\n fileName: string,\n): string | undefined => {\n const m = /Rec(\\w{3})(?:_|_DST)(\\d{8})_(\\d{6})_.*/.exec(fileName);\n if (!m) return undefined;\n return `${m[2]}${m[3]}`;\n};\n\n/**\n * Some firmwares want a stop <name> like: 01YYYYMMDDHHMMSS (derived from Rec*_YYYYMMDD_HHMMSS).\n * If the caller already has a 01xxxxxxxxxxxxxx name, keep it.\n */\nexport const buildReplayStopNameFromFileName = (\n fileName: string,\n): string | undefined => {\n const trimmed = (fileName ?? \"\").trim();\n if (/^01\\d{14}$/.test(trimmed)) return trimmed;\n const start = parseRecStartParamIfPresent(fileName);\n if (!start) return undefined;\n return `01${start}`;\n};\n\nexport const buildFileInfoListReplayByIdXml = (params: {\n channel: number;\n /** Optional override for the <channelId> value inside the XML payload. */\n xmlChannelId?: number;\n id: string;\n uid?: string;\n streamType?: RecordingReplayStreamType;\n iframeReplay?: RecordingReplayIFrameMode;\n}): string => {\n const st = params.streamType ?? \"mainStream\";\n const supportSub = st === \"subStream\" ? 1 : 0;\n const xmlCh = params.xmlChannelId ?? params.channel;\n const iframe = params.iframeReplay;\n const iframeXml =\n iframe === true || iframe === \"both\"\n ? \"<bIframeReplay>1</bIframeReplay><iIframeReplay>1</iIframeReplay>\"\n : iframe === \"b\"\n ? \"<bIframeReplay>1</bIframeReplay>\"\n : iframe === \"i\"\n ? \"<iIframeReplay>1</iIframeReplay>\"\n : \"\";\n\n // Build the XML without empty lines when optional fields are absent.\n // PCAP analysis shows the app does NOT include <uid> for standalone cameras.\n const lines = [\n '<?xml version=\"1.0\" encoding=\"UTF-8\" ?>',\n \"<body>\",\n '<FileInfoList version=\"1.1\">',\n \"<FileInfo>\",\n `<channelId>${xmlCh}</channelId>`,\n `<Id>${xmlEscape(params.id)}</Id>`,\n ];\n if (params.uid) {\n lines.push(`<uid>${xmlEscape(params.uid)}</uid>`);\n }\n lines.push(\n `<supportSub>${supportSub}</supportSub>`,\n \"<playSpeed>1</playSpeed>\",\n `<streamType>${xmlEscape(st)}</streamType>`,\n );\n if (iframeXml) {\n lines.push(iframeXml);\n }\n lines.push(\"</FileInfo>\", \"</FileInfoList>\", \"</body>\");\n return lines.join(\"\\n\");\n};\n\nexport const buildFileInfoListReplayByNameXml = (params: {\n channel: number;\n /** Optional override for the <channelId> value inside the XML payload. */\n xmlChannelId?: number;\n name: string;\n uid?: string;\n streamType?: RecordingReplayStreamType;\n iframeReplay?: RecordingReplayIFrameMode;\n}): string => {\n const st = params.streamType ?? \"mainStream\";\n const supportSub = st === \"subStream\" ? 1 : 0;\n const xmlCh = params.xmlChannelId ?? params.channel;\n const iframe = params.iframeReplay;\n const iframeXml =\n iframe === true || iframe === \"both\"\n ? \"<bIframeReplay>1</bIframeReplay><iIframeReplay>1</iIframeReplay>\"\n : iframe === \"b\"\n ? \"<bIframeReplay>1</bIframeReplay>\"\n : iframe === \"i\"\n ? \"<iIframeReplay>1</iIframeReplay>\"\n : \"\";\n\n // Build the XML without empty lines when optional fields are absent.\n const lines = [\n '<?xml version=\"1.0\" encoding=\"UTF-8\" ?>',\n \"<body>\",\n '<FileInfoList version=\"1.1\">',\n \"<FileInfo>\",\n `<channelId>${xmlCh}</channelId>`,\n `<Id>${xmlEscape(params.name)}</Id>`,\n ];\n if (params.uid) {\n lines.push(`<uid>${xmlEscape(params.uid)}</uid>`);\n }\n lines.push(\n `<supportSub>${supportSub}</supportSub>`,\n \"<playSpeed>1</playSpeed>\",\n `<streamType>${xmlEscape(st)}</streamType>`,\n );\n if (iframeXml) {\n lines.push(iframeXml);\n }\n lines.push(\"</FileInfo>\", \"</FileInfoList>\", \"</body>\");\n return lines.join(\"\\n\");\n};\n\nexport const buildFileInfoListStopXml = (params: {\n channel: number;\n name: string;\n streamType?: RecordingReplayStreamType;\n}): string => {\n const st = params.streamType ?? \"mainStream\";\n\n return `<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<body>\n<FileInfoList version=\"1.1\">\n<FileInfo>\n<channelId>${params.channel}</channelId>\n<name>${xmlEscape(params.name)}</name>\n<streamType>${xmlEscape(st)}</streamType>\n</FileInfo>\n</FileInfoList>\n</body>`;\n};\n","export const sleepMs = (ms: number): Promise<void> => new Promise((resolve) => setTimeout(resolve, ms));\n\nexport const xmlDateTimePayload = (tag: \"startTime\" | \"endTime\", d: Date): string => {\n // Use local time methods - the camera expects dates in local time format.\n return `<${tag}><year>${d.getFullYear()}</year><month>${d.getMonth() + 1}</month><day>${d.getDate()}</day><hour>${d.getHours()}</hour><minute>${d.getMinutes()}</minute><second>${d.getSeconds()}</second></${tag}>`;\n};\n","import {\n BC_CMD_ID_FILE_INFO_LIST_CLOSE,\n BC_CMD_ID_FILE_INFO_LIST_GET,\n BC_CMD_ID_FILE_INFO_LIST_OPEN,\n} from \"../../../protocol/constants\";\nimport { getXmlText, xmlEscape } from \"../../../protocol/xml\";\nimport type { RecordingFile, RecordingStreamType } from \"../types\";\nimport { parseRecordingFilesFromXml } from \"../xmlUtils\";\nimport { xmlDateTimePayload } from \"./recordings\";\n\nexport type SendXmlLike = (params: {\n cmdId: number;\n channel?: number;\n payloadXml?: string;\n timeoutMs?: number;\n}) => Promise<string>;\n\nexport const buildFileInfoListOpenXml = (params: {\n uid: string;\n channel: number;\n streamType: RecordingStreamType;\n recordType: string;\n start: Date;\n end: Date;\n}): string => {\n return `<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<body>\n<FileInfoList version=\"1.1\">\n<FileInfo>\n<uid>${xmlEscape(params.uid)}</uid>\n<searchAITrack>1</searchAITrack>\n<channelId>${params.channel}</channelId>\n<logicChnBitmap>255</logicChnBitmap>\n<streamType>${xmlEscape(params.streamType)}</streamType>\n<recordType>${xmlEscape(params.recordType)}</recordType>\n${xmlDateTimePayload(\"startTime\", params.start)}\n${xmlDateTimePayload(\"endTime\", params.end)}\n</FileInfo>\n</FileInfoList>\n</body>`;\n};\n\nexport const buildFileInfoListPageXml = (params: {\n channel: number;\n uid: string;\n handle: number;\n}): string => {\n return `<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<body>\n<FileInfoList version=\"1.1\">\n<FileInfo>\n<channelId>${params.channel}</channelId>\n<uid>${xmlEscape(params.uid)}</uid>\n<searchAITrack>1</searchAITrack>\n<handle>${params.handle}</handle>\n</FileInfo>\n</FileInfoList>\n</body>`;\n};\n\nexport const parseFileInfoListHandle = (openRespXml: string): number => {\n const handleText = getXmlText(openRespXml, \"handle\");\n if (!handleText) throw new Error(\"FileInfoList open did not return <handle>\");\n\n const handle = Number.parseInt(handleText, 10);\n if (!Number.isFinite(handle)) {\n throw new Error(`FileInfoList open returned invalid handle: ${handleText}`);\n }\n\n return handle;\n};\n\nexport const dedupeRecordingFiles = (\n files: RecordingFile[],\n): RecordingFile[] => {\n const seen = new Set<string>();\n return files.filter((f) => {\n if (seen.has(f.fileName)) return false;\n seen.add(f.fileName);\n return true;\n });\n};\n\nexport const listRecordingsViaFileInfoList = async (params: {\n sendXml: SendXmlLike;\n channel: number;\n uid: string;\n streamType: RecordingStreamType;\n recordType: string;\n start: Date;\n end: Date;\n maxIterations: number;\n timeoutMs?: number;\n}): Promise<RecordingFile[]> => {\n const timeoutMs = params.timeoutMs ?? 15_000;\n\n const openXml = buildFileInfoListOpenXml({\n uid: params.uid,\n channel: params.channel,\n streamType: params.streamType,\n recordType: params.recordType,\n start: params.start,\n end: params.end,\n });\n\n // NOTE: For FileInfoList, we do NOT pass channel to sendXml for header calculation.\n // The channel is only passed inside the XML payload (<channelId>).\n // Passing channel causes channelId=channel+1 in the Baichuan header, which NVRs reject (400).\n // Without channel, sendXml uses hostChannelId (250) which is correct.\n const openResp = await params.sendXml({\n cmdId: BC_CMD_ID_FILE_INFO_LIST_OPEN,\n // channel is NOT passed here - only in XML payload\n payloadXml: openXml,\n timeoutMs,\n });\n\n const handle = parseFileInfoListHandle(openResp);\n\n const pageXml = buildFileInfoListPageXml({\n channel: params.channel,\n uid: params.uid,\n handle,\n });\n\n const files: RecordingFile[] = [];\n const TYPICAL_PAGE_SIZE = 40;\n\n try {\n for (let i = 0; i < params.maxIterations; i++) {\n let resp: string;\n try {\n resp = await params.sendXml({\n cmdId: BC_CMD_ID_FILE_INFO_LIST_GET,\n // channel is NOT passed here - only in XML payload\n payloadXml: pageXml,\n timeoutMs,\n });\n } catch (e) {\n const errorMsg = e instanceof Error ? e.message : String(e);\n if (errorMsg.includes(\"responseCode 400, empty body\")) break;\n throw e;\n }\n\n const pageFiles = parseRecordingFilesFromXml(resp);\n files.push(...pageFiles);\n\n const bFinishedText =\n getXmlText(resp, \"bFinished\") ?? getXmlText(resp, \"finished\");\n if (bFinishedText != null) {\n if (bFinishedText.trim() === \"1\") break;\n } else if (\n pageFiles.length === 0 ||\n pageFiles.length < TYPICAL_PAGE_SIZE\n ) {\n break;\n }\n }\n } finally {\n try {\n await params.sendXml({\n cmdId: BC_CMD_ID_FILE_INFO_LIST_CLOSE,\n // channel is NOT passed here - only in XML payload\n payloadXml: pageXml,\n timeoutMs: Math.min(timeoutMs, 5_000),\n });\n } catch {\n // ignore\n }\n }\n\n return files;\n};\n\n/**\n * Build XML for FileInfoList OPEN for file download (cmdId=14).\n * This is different from listing - it opens a file for binary data retrieval.\n */\nexport const buildFileInfoListDownloadOpenXml = (params: {\n uid: string;\n channel: number;\n fileName: string;\n}): string => {\n const name =\n params.fileName.split(\"/\").filter(Boolean).at(-1) ?? params.fileName;\n return `<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<body>\n<FileInfoList version=\"1.1\">\n<FileInfo>\n<uid>${xmlEscape(params.uid)}</uid>\n<channelId>${params.channel}</channelId>\n<fileName>${xmlEscape(params.fileName)}</fileName>\n<name>${xmlEscape(name)}</name>\n<Id>${xmlEscape(params.fileName)}</Id>\n</FileInfo>\n</FileInfoList>\n</body>`;\n};\n\n/**\n * Build XML for FileInfoList GET/CLOSE for file download (cmdId=15/16).\n */\nexport const buildFileInfoListDownloadPageXml = (params: {\n uid: string;\n channel: number;\n handle: number;\n}): string => {\n return `<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<body>\n<FileInfoList version=\"1.1\">\n<FileInfo>\n<channelId>${params.channel}</channelId>\n<uid>${xmlEscape(params.uid)}</uid>\n<handle>${params.handle}</handle>\n</FileInfo>\n</FileInfoList>\n</body>`;\n};\n\nexport type SendBinaryLike = (params: {\n cmdId: number;\n channel?: number;\n payloadXml?: string;\n timeoutMs?: number;\n}) => Promise<Buffer>;\n\n/**\n * Download a recording using the paged FileInfoList method (cmdId=14 OPEN, 15 GET, 16 CLOSE).\n * This is observed in PCAP for TrackMix PoE cameras where cmdId=5/13 return empty.\n */\nexport const downloadRecordingViaFileInfoListPaged = async (params: {\n sendXml: SendXmlLike;\n sendBinary: SendBinaryLike;\n channel: number;\n uid: string;\n fileName: string;\n maxIterations?: number;\n timeoutMs?: number;\n}): Promise<Buffer> => {\n const timeoutMs = params.timeoutMs ?? 120_000;\n const maxIterations = params.maxIterations ?? 1000;\n\n const openXml = buildFileInfoListDownloadOpenXml({\n uid: params.uid,\n channel: params.channel,\n fileName: params.fileName,\n });\n\n // OPEN the file (cmdId=14)\n const openResp = await params.sendXml({\n cmdId: BC_CMD_ID_FILE_INFO_LIST_OPEN,\n payloadXml: openXml,\n timeoutMs,\n });\n\n const handle = parseFileInfoListHandle(openResp);\n\n const pageXml = buildFileInfoListDownloadPageXml({\n channel: params.channel,\n uid: params.uid,\n handle,\n });\n\n const chunks: Buffer[] = [];\n\n try {\n for (let i = 0; i < maxIterations; i++) {\n let resp: Buffer;\n try {\n // GET data chunk (cmdId=15) - returns binary data\n resp = await params.sendBinary({\n cmdId: BC_CMD_ID_FILE_INFO_LIST_GET,\n payloadXml: pageXml,\n timeoutMs,\n });\n } catch (e) {\n const errorMsg = e instanceof Error ? e.message : String(e);\n // 400 or empty response = end of data\n if (errorMsg.includes(\"responseCode 400\") || errorMsg.includes(\"empty\"))\n break;\n throw e;\n }\n\n if (resp.length === 0) break;\n chunks.push(resp);\n\n // Check if this is the last chunk (smaller than expected)\n // Typical chunks are 26KB+, a smaller chunk indicates end\n if (resp.length < 10000) break;\n }\n } finally {\n try {\n // CLOSE the file (cmdId=16)\n await params.sendXml({\n cmdId: BC_CMD_ID_FILE_INFO_LIST_CLOSE,\n payloadXml: pageXml,\n timeoutMs: Math.min(timeoutMs, 5_000),\n });\n } catch {\n // ignore close errors\n }\n }\n\n return Buffer.concat(chunks);\n};\n","import { getXmlText } from \"../../../protocol/xml\";\nimport type { Logger } from \"../../../debug/DebugConfig\";\nimport type { ChannelStreamMetadata, StreamMetadata, VideoCodec } from \"../types\";\n\nconst videoCodecMap: Record<number, VideoCodec> = {\n 0: \"H.264\",\n 1: \"H.265\",\n 2: \"MJPEG\",\n 3: \"MPEG4\",\n};\n\nconst isEnabledFromText = (enableText: string | undefined): boolean => {\n return enableText === undefined || enableText === \"1\" || enableText === \"true\";\n};\n\nconst isPlausibleStream = (s: { width: number; height: number; frameRate: number; bitRate: number }): boolean => {\n return s.width > 0 && s.height > 0 && (s.frameRate > 0 || s.bitRate > 0);\n};\n\nconst logDebugStreamBlock = (params: {\n logger: Logger;\n traceNativeStream: boolean;\n channel: number;\n tag: string;\n blockXml: string | undefined;\n}): void => {\n const { logger, traceNativeStream, channel, tag, blockXml } = params;\n if (!traceNativeStream) return;\n\n if (!blockXml) {\n (logger.warn ?? logger.log).call(logger, `[ReolinkBaichuanApi] getStreamMetadata(traceNativeStream): channel=${channel} tag=<${tag}> missing`);\n return;\n }\n\n const raw = blockXml;\n const widthText = getXmlText(raw, \"width\") ?? getXmlText(raw, \"Width\");\n const heightText = getXmlText(raw, \"height\") ?? getXmlText(raw, \"Height\");\n const frameText = getXmlText(raw, \"frame\") ?? getXmlText(raw, \"Frame\");\n const bitRateText = getXmlText(raw, \"bitRate\") ?? getXmlText(raw, \"BitRate\");\n const videoEncTypeText = getXmlText(raw, \"videoEncType\") ?? getXmlText(raw, \"VideoEncType\");\n const audioText = getXmlText(raw, \"audio\") ?? getXmlText(raw, \"Audio\");\n const enableText = getXmlText(raw, \"enable\") ?? getXmlText(raw, \"Enable\");\n\n const width = Number(widthText ?? \"0\");\n const height = Number(heightText ?? \"0\");\n const frameRate = Number(frameText ?? \"0\");\n const bitRate = Number(bitRateText ?? \"0\");\n const audio = Number(audioText ?? \"0\");\n const isEnabled = isEnabledFromText(enableText);\n const plausible = isEnabled && isPlausibleStream({ width, height, frameRate, bitRate });\n\n const previewMax = 1400;\n const xmlPreview = raw.length <= previewMax ? raw : raw.slice(0, previewMax) + `\\n...truncated (+${raw.length - previewMax} chars)`;\n\n (logger.warn ?? logger.log).call(\n logger,\n `[ReolinkBaichuanApi] getStreamMetadata(traceNativeStream): channel=${channel} tag=<${tag}> ` +\n `enabled=${isEnabled} plausible=${plausible} ` +\n `width=${width} height=${height} frame=${frameRate} bitRate=${bitRate} audio=${audio} videoEncType=${videoEncTypeText ?? \"?\"} ` +\n `rawFields=${JSON.stringify({ widthText, heightText, frameText, bitRateText, videoEncTypeText, audioText, enableText })} ` +\n `blockXml=${JSON.stringify(xmlPreview)}`,\n );\n};\n\nconst buildStream = (params: {\n profile: StreamMetadata[\"profile\"];\n streamXml: string;\n}): StreamMetadata | undefined => {\n const { profile, streamXml } = params;\n\n const width = Number(getXmlText(streamXml, \"width\") ?? \"0\");\n const height = Number(getXmlText(streamXml, \"height\") ?? \"0\");\n const videoEncTypeInt = Number(getXmlText(streamXml, \"videoEncType\") ?? \"0\");\n const frameRate = Number(getXmlText(streamXml, \"frame\") ?? \"0\");\n const bitRate = Number(getXmlText(streamXml, \"bitRate\") ?? \"0\");\n const audio = Number(getXmlText(streamXml, \"audio\") ?? \"0\");\n\n const enabled = getXmlText(streamXml, \"enable\");\n const isEnabled = isEnabledFromText(enabled);\n\n if (!isEnabled || !isPlausibleStream({ width, height, frameRate, bitRate })) return undefined;\n\n return {\n profile,\n audio,\n width,\n height,\n videoEncType: videoCodecMap[videoEncTypeInt] ?? `Unknown(${videoEncTypeInt})`,\n videoEncTypeInt,\n frameRate,\n bitRate,\n audioCodec: \"aac\",\n };\n};\n\nexport const parseChannelStreamMetadataFromGetEncXml = (params: {\n channel: number;\n xml: string;\n logger: Logger;\n traceNativeStream?: boolean;\n}): ChannelStreamMetadata => {\n const { channel, xml, logger } = params;\n const traceNativeStream = params.traceNativeStream === true;\n\n const streams: StreamMetadata[] = [];\n let audioEnabled = true;\n\n if (traceNativeStream) {\n const headMax = 1600;\n const xmlHead = xml.length <= headMax ? xml : xml.slice(0, headMax) + `\\n...truncated (+${xml.length - headMax} chars)`;\n const tagsPresent = {\n mainStream: /<mainStream\\b/.test(xml),\n subStream: /<subStream\\b/.test(xml),\n extStream: /<extStream\\b/.test(xml),\n thirdStream: /<thirdStream\\b/.test(xml),\n externStream: /<externStream\\b/.test(xml),\n extraStream: /<extraStream\\b/.test(xml),\n };\n (logger.warn ?? logger.log).call(\n logger,\n `[ReolinkBaichuanApi] getStreamMetadata(traceNativeStream): channel=${channel} xmlLen=${xml.length} tagsPresent=${JSON.stringify(tagsPresent)} xmlHead=${JSON.stringify(xmlHead)}`,\n );\n }\n\n const mainMatch = xml.match(/<mainStream[^>]*>([\\s\\S]*?)<\\/mainStream>/);\n if (mainMatch) {\n const mainXml = mainMatch[1] ?? \"\";\n logDebugStreamBlock({ logger, traceNativeStream, channel, tag: \"mainStream\", blockXml: mainXml });\n const s = buildStream({ profile: \"main\", streamXml: mainXml });\n if (s) {\n streams.push(s);\n audioEnabled = audioEnabled && s.audio === 1;\n }\n }\n\n const subMatch = xml.match(/<subStream[^>]*>([\\s\\S]*?)<\\/subStream>/);\n if (subMatch) {\n const subXml = subMatch[1] ?? \"\";\n logDebugStreamBlock({ logger, traceNativeStream, channel, tag: \"subStream\", blockXml: subXml });\n const s = buildStream({ profile: \"sub\", streamXml: subXml });\n if (s) {\n streams.push(s);\n audioEnabled = audioEnabled && s.audio === 1;\n }\n }\n\n const extLikeTags = [\"extStream\", \"thirdStream\", \"externStream\", \"extraStream\"];\n for (const tag of extLikeTags) {\n const extMatch = xml.match(new RegExp(`<${tag}[^>]*>([\\\\s\\\\S]*?)<\\\\/${tag}>`));\n if (!extMatch) continue;\n\n const extXml = extMatch[1] ?? \"\";\n logDebugStreamBlock({ logger, traceNativeStream, channel, tag, blockXml: extXml });\n const s = buildStream({ profile: \"ext\", streamXml: extXml });\n if (s) {\n streams.push(s);\n audioEnabled = audioEnabled && s.audio === 1;\n }\n\n break;\n }\n\n return {\n channel,\n streams,\n audioEnabled,\n };\n};\n","import { xmlEscape } from \"../../../protocol/xml\";\nimport type { TalkConfig } from \"../types\";\n\nexport const buildTalkConfigPayloadXml = (config: TalkConfig): string => {\n const audio = config.audioConfig;\n return `<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<body>\n<TalkConfig version=\"1.1\">\n<channelId>${config.channel}</channelId>\n<duplex>${xmlEscape(config.duplex)}</duplex>\n<audioStreamMode>${xmlEscape(config.audioStreamMode)}</audioStreamMode>\n<audioConfig>\n<audioType>${xmlEscape(audio.audioType)}</audioType>\n<sampleRate>${audio.sampleRate}</sampleRate>\n<samplePrecision>${audio.samplePrecision}</samplePrecision>\n<lengthPerEncoder>${audio.lengthPerEncoder}</lengthPerEncoder>\n<soundTrack>${xmlEscape(audio.soundTrack)}</soundTrack>\n</audioConfig>\n</TalkConfig>\n</body>`;\n};\n\nexport const encodeBcMediaAdpcmBlock = (block: Buffer, halfBlockSize: number): Buffer => {\n // Matches parseAdpcm in src/baichuan/stream/BcMediaParser.ts\n // magic(4) + payload_size(u16) + payload_size_b(u16) + magic_data(u16=0x0100) + half_block_size(u16) + data + padding\n const magic = 0x62773130; // \"bw10\"\n const subHeaderSize = 4;\n const payloadSize = subHeaderSize + block.length;\n const headerLen = 12;\n const padSize = payloadSize % 8 === 0 ? 0 : 8 - (payloadSize % 8);\n const totalLen = headerLen + block.length + padSize;\n const buf = Buffer.alloc(totalLen);\n\n buf.writeUInt32LE(magic, 0);\n buf.writeUInt16LE(payloadSize, 4);\n buf.writeUInt16LE(payloadSize, 6);\n buf.writeUInt16LE(0x0100, 8);\n buf.writeUInt16LE(halfBlockSize, 10);\n block.copy(buf, 12);\n\n return buf;\n};\n","import type { BaichuanClient } from \"../../../client/BaichuanClient\";\nimport {\n BC_CLASS_MODERN_24,\n BC_CMD_ID_TALK_CONFIG,\n BC_CMD_ID_TALK_RESET,\n} from \"../../../protocol/constants\";\nimport type { TalkAbility, TalkConfig, TalkSessionInfo } from \"../types\";\nimport { buildTalkConfigPayloadXml } from \"./talk\";\n\nexport const buildTalkSessionInfoFromAbility = (params: {\n channel: number;\n ability: TalkAbility;\n}): { payloadXml: string; info: TalkSessionInfo } => {\n const audioConfig =\n params.ability.audioConfigList.find((c) => c.audioType.toLowerCase() === \"adpcm\") ??\n params.ability.audioConfigList[0];\n\n if (!audioConfig) {\n throw new Error(`Talk not supported on channel ${params.channel} (no audioConfig in TalkAbility)`);\n }\n\n const talkConfig: TalkConfig = {\n channel: params.channel,\n duplex: params.ability.duplexList[0] ?? \"FDX\",\n audioStreamMode: params.ability.audioStreamModeList[0] ?? \"followVideoStream\",\n audioConfig,\n };\n\n const blockSize = Math.floor(audioConfig.lengthPerEncoder / 2);\n const fullBlockSize = blockSize + 4;\n if (blockSize <= 0 || fullBlockSize <= 4) {\n throw new Error(`Invalid talk audio config: lengthPerEncoder=${audioConfig.lengthPerEncoder}`);\n }\n\n return {\n payloadXml: buildTalkConfigPayloadXml(talkConfig),\n info: {\n channel: params.channel,\n audioConfig,\n blockSize,\n fullBlockSize,\n },\n };\n};\n\nexport const sendTalkConfigWithReset = async (params: {\n client: Pick<BaichuanClient, \"sendFrame\">;\n channel: number;\n payloadXml: string;\n channelIdOverride?: number;\n}): Promise<void> => {\n const trySend = async (): Promise<number> => {\n const frame = await params.client.sendFrame({\n cmdId: BC_CMD_ID_TALK_CONFIG,\n channel: params.channel,\n ...(params.channelIdOverride != null ? { channelIdOverride: params.channelIdOverride } : {}),\n payloadXml: params.payloadXml,\n messageClass: BC_CLASS_MODERN_24,\n });\n return frame.header.responseCode;\n };\n\n const code = await trySend();\n if (code === 422) {\n await params.client.sendFrame({\n cmdId: BC_CMD_ID_TALK_RESET,\n channel: params.channel,\n ...(params.channelIdOverride != null ? { channelIdOverride: params.channelIdOverride } : {}),\n payloadXml: \"\",\n messageClass: BC_CLASS_MODERN_24,\n });\n\n const retryCode = await trySend();\n if (retryCode !== 200) {\n throw new Error(`TalkConfig rejected after reset (responseCode ${retryCode})`);\n }\n return;\n }\n\n if (code !== 200) {\n throw new Error(`TalkConfig rejected (responseCode ${code})`);\n }\n};\n","import type { BaichuanClient } from \"../../../client/BaichuanClient\";\nimport { BC_CLASS_MODERN_24, BC_CMD_ID_TALK, BC_CMD_ID_TALK_RESET } from \"../../../protocol/constants\";\nimport { buildBinaryExtensionXml } from \"../../../protocol/xml\";\nimport type { TalkSession, TalkSessionInfo } from \"../types\";\nimport { sleepMs } from \"./recordings\";\nimport { encodeBcMediaAdpcmBlock } from \"./talk\";\n\nexport const createBufferedTalkSession = (params: {\n client: BaichuanClient;\n channel: number;\n channelIdOverride?: number;\n info: TalkSessionInfo;\n blocksPerPayload?: number;\n /** If true, close the underlying socket when stop() completes (for dedicated sessions). */\n closeSocketOnStop?: boolean;\n}): TalkSession => {\n const { client, channel, channelIdOverride, info } = params;\n\n const chunks: Buffer[] = [];\n let bufferedBytes = 0;\n let closed = false;\n let pumping = false;\n let expectedStreamEndMs = Date.now();\n\n const enqueue = (b: Buffer): void => {\n if (b.length === 0) return;\n chunks.push(b);\n bufferedBytes += b.length;\n };\n\n const consume = (n: number): Buffer => {\n if (n <= 0) return Buffer.alloc(0);\n if (bufferedBytes < n) {\n throw new Error(`Internal error: consume(${n}) with bufferedBytes=${bufferedBytes}`);\n }\n\n if (chunks.length === 1) {\n const only = chunks[0]!;\n if (only.length === n) {\n chunks.length = 0;\n bufferedBytes = 0;\n return only;\n }\n if (only.length > n) {\n const out = only.subarray(0, n);\n chunks[0] = only.subarray(n);\n bufferedBytes -= n;\n return out;\n }\n }\n\n const out = Buffer.allocUnsafe(n);\n let off = 0;\n while (off < n) {\n const head = chunks[0];\n if (!head) throw new Error(\"Internal error: chunk queue empty\");\n const take = Math.min(n - off, head.length);\n head.copy(out, off, 0, take);\n\n if (take === head.length) {\n chunks.shift();\n } else {\n chunks[0] = head.subarray(take);\n }\n off += take;\n }\n\n bufferedBytes -= n;\n return out;\n };\n\n const BLOCKS_PER_PAYLOAD = Math.max(1, Math.min(8, Math.floor(params.blocksPerPayload ?? 1)));\n\n const sendBlocks = async (blocks: Buffer[]): Promise<void> => {\n if (blocks.length === 0) return;\n if (blocks.length > BLOCKS_PER_PAYLOAD) {\n throw new Error(`Internal error: too many blocks in payload (${blocks.length})`);\n }\n\n const parts: Buffer[] = [];\n let samplesSent = 0;\n for (const block of blocks) {\n parts.push(encodeBcMediaAdpcmBlock(block, info.blockSize));\n samplesSent += Math.max(0, (block.length - 4) * 2 + 1);\n }\n\n await client.sendBinaryPayloadNoReply({\n cmdId: BC_CMD_ID_TALK,\n channel,\n ...(channelIdOverride != null ? { channelIdOverride } : {}),\n extensionXml: buildBinaryExtensionXml(channel),\n payload: Buffer.concat(parts),\n messageClass: BC_CLASS_MODERN_24,\n });\n\n const playLengthMs = (samplesSent / info.audioConfig.sampleRate) * 1000;\n\n const now = Date.now();\n if (now > expectedStreamEndMs) expectedStreamEndMs = now + playLengthMs;\n else expectedStreamEndMs += playLengthMs;\n\n const sleepFor = expectedStreamEndMs - Date.now();\n if (sleepFor > 0) await sleepMs(sleepFor);\n };\n\n const pump = async (): Promise<void> => {\n if (pumping) return;\n pumping = true;\n try {\n while (true) {\n if (bufferedBytes >= info.fullBlockSize) {\n const blocks: Buffer[] = [];\n while (blocks.length < BLOCKS_PER_PAYLOAD && bufferedBytes >= info.fullBlockSize) {\n blocks.push(consume(info.fullBlockSize));\n }\n await sendBlocks(blocks);\n continue;\n }\n\n if (closed) {\n if (bufferedBytes > 0) {\n const padded = Buffer.alloc(info.fullBlockSize, 0xff);\n const tail = consume(bufferedBytes);\n tail.copy(padded, 0);\n await sendBlocks([padded]);\n }\n break;\n }\n\n break;\n }\n } finally {\n pumping = false;\n }\n };\n\n const stop = async (): Promise<void> => {\n if (closed) return;\n closed = true;\n await pump();\n\n const remaining = expectedStreamEndMs - Date.now();\n if (remaining > 0) await sleepMs(remaining + 100);\n else await sleepMs(100);\n\n const frame = await client.sendFrame({\n cmdId: BC_CMD_ID_TALK_RESET,\n channel,\n ...(channelIdOverride != null ? { channelIdOverride } : {}),\n payloadXml: \"\",\n messageClass: BC_CLASS_MODERN_24,\n });\n if (frame.header.responseCode !== 200) {\n throw new Error(`TalkReset rejected (responseCode ${frame.header.responseCode})`);\n }\n\n if (params.closeSocketOnStop) {\n try {\n await client.close({ reason: \"talk_session_stop\" });\n } catch {\n // ignore\n }\n }\n };\n\n return {\n info,\n sendAudio: async (adpcm: Buffer) => {\n if (closed) throw new Error(\"Talk session is closed\");\n if (adpcm.length === 0) return;\n enqueue(adpcm);\n await pump();\n },\n stop,\n };\n};\n","export const isReolinkUidLike = (value: string): boolean => {\n const s = value.trim();\n return /^[0-9A-Z]{12,24}$/.test(s) && /[A-Z]/.test(s);\n};\n\nexport const extractReolinkUidLike = (value: unknown): string | undefined => {\n const seen = new Set<unknown>();\n\n const walk = (v: unknown): string | undefined => {\n if (v == null) return undefined;\n\n if (typeof v === \"string\") {\n const s = v.trim();\n if (isReolinkUidLike(s)) return s;\n\n // Best-effort scan for UID-like substrings (useful for XML payloads).\n const m = s.match(/\\b[0-9A-Z]{12,24}\\b/);\n if (m?.[0] && isReolinkUidLike(m[0])) return m[0];\n return undefined;\n }\n\n if (typeof v !== \"object\") return undefined;\n if (seen.has(v)) return undefined;\n seen.add(v);\n\n if (Array.isArray(v)) {\n for (const it of v) {\n const r = walk(it);\n if (r) return r;\n }\n return undefined;\n }\n\n for (const vv of Object.values(v as Record<string, unknown>)) {\n const r = walk(vv);\n if (r) return r;\n }\n\n return undefined;\n };\n\n return walk(value);\n};\n","import { extractReolinkUidLike, isReolinkUidLike } from \"./uid\";\n\nexport const discoverPerChannelUidViaCgiChannelstatus = async (params: {\n channel: number;\n login: () => Promise<void>;\n getChannelstatus: () => Promise<Array<{ value?: { status?: Array<{ channel?: number; uid?: string }> } }>>;\n}): Promise<string | undefined> => {\n await params.login();\n const chStatus = await params.getChannelstatus();\n const entry = chStatus\n .flatMap((r) => r.value?.status ?? [])\n .find((s) => typeof s?.channel === \"number\" && s.channel === params.channel);\n\n const uidCandidate = (entry?.uid ?? \"\").trim();\n return uidCandidate && isReolinkUidLike(uidCandidate) ? uidCandidate : undefined;\n};\n\nexport const discoverDeviceUidViaGetInfoSerial = async (params: {\n getInfo: () => Promise<{ serialNumber?: string }>;\n}): Promise<string | undefined> => {\n const info = await params.getInfo();\n const serial = (info.serialNumber ?? \"\").trim();\n return serial && isReolinkUidLike(serial) ? serial : undefined;\n};\n\nexport const discoverDeviceUidViaCgi = async (params: {\n login: () => Promise<void>;\n getP2p: () => Promise<unknown>;\n getDevInfo: () => Promise<unknown>;\n}): Promise<string | undefined> => {\n await params.login();\n\n try {\n const p2p = await params.getP2p();\n const fromP2p = extractReolinkUidLike(p2p);\n if (fromP2p) return fromP2p;\n } catch {\n // ignore\n }\n\n try {\n const devInfo = await params.getDevInfo();\n const fromDevInfo = extractReolinkUidLike(devInfo);\n if (fromDevInfo) return fromDevInfo;\n } catch {\n // ignore\n }\n\n return undefined;\n};\n\nexport const discoverDeviceUidViaBaichuanGetP2p = async (params: {\n sendXml: (p: { cmdId: number; timeoutMs?: number }) => Promise<string>;\n}): Promise<string | undefined> => {\n const p2pXml = await params.sendXml({ cmdId: 114, timeoutMs: 10_000 });\n return extractReolinkUidLike(p2pXml);\n};\n","import { getXmlText } from \"../../../protocol/xml\";\nimport type { Events } from \"../types\";\n\nconst parseAiTypeToken = (aiTypeRaw: string | undefined): string | undefined => {\n const raw = (aiTypeRaw ?? \"\").trim();\n if (!raw) return undefined;\n\n const token = raw\n .split(\",\")\n .map((t) => t.trim())\n .find((t) => t.length > 0 && t.toLowerCase() !== \"none\");\n\n return token;\n};\n\nexport const parseEventsFromGetEventsXml = (params: { xml: string; channel: number; nowMs: number }): Events => {\n const { xml, channel: ch, nowMs } = params;\n\n const out: Events = { channel: ch };\n\n const applyEvent = (eventXml: string, eventChannel: number): void => {\n if (eventChannel !== ch) return;\n\n const statusUpper = ((getXmlText(eventXml, \"status\") ?? \"\").trim()).toUpperCase();\n const aiTypeRaw = (\n getXmlText(eventXml, \"AItype\") ??\n getXmlText(eventXml, \"aiType\") ??\n getXmlText(eventXml, \"aitype\") ??\n \"\"\n ).trim();\n\n if (statusUpper.includes(\"MD\")) {\n out.motion = { state: 1, timestamp: nowMs, source: \"md\" };\n }\n if (statusUpper.includes(\"PIR\")) {\n out.motion = { state: 1, timestamp: nowMs, source: \"pir\" };\n }\n\n const aiTypeToken = parseAiTypeToken(aiTypeRaw);\n if (aiTypeToken || statusUpper.includes(\"AI\")) {\n out.ai = {\n channel: ch,\n alarm_state: 1,\n ...(aiTypeToken ? { type: aiTypeToken } : {}),\n };\n }\n\n if (statusUpper.includes(\"VIS\")) {\n out.visitor = { detected: true, timestamp: nowMs };\n }\n };\n\n // Format: AlarmEventList\n if (xml.includes(\"<AlarmEventList\")) {\n const alarmEventMatches = xml.matchAll(/<AlarmEvent\\b[^>]*>([\\s\\S]*?)<\\/AlarmEvent>/g);\n for (const match of alarmEventMatches) {\n const alarmXml = match[1] ?? \"\";\n const channelText = getXmlText(alarmXml, \"channelId\");\n const eventChannel = channelText !== undefined ? Number(channelText) : ch;\n applyEvent(alarmXml, eventChannel);\n }\n return out;\n }\n\n // Fallback: Event\n applyEvent(xml, ch);\n return out;\n};\n","import { getXmlText } from \"../../../protocol/xml\";\nimport type { PirState } from \"../types\";\n\nconst parseBoolishNumber = (v: string | undefined): number | undefined => {\n if (v === undefined) return undefined;\n const t = v.trim().toLowerCase();\n if (t === \"true\") return 1;\n if (t === \"false\") return 0;\n const n = Number(t);\n return Number.isFinite(n) ? n : undefined;\n};\n\nconst parseEnabled = (v: string | undefined): boolean => {\n if (v === undefined) return false;\n const t = v.trim().toLowerCase();\n return t === \"1\" || t === \"true\";\n};\n\nexport const parsePirInfoFromXml = (params: { xml: string; channel: number }): PirState => {\n const { xml, channel } = params;\n\n const enable = getXmlText(xml, \"enable\");\n const sensitive = getXmlText(xml, \"sensiValue\");\n const reduceAlarm = getXmlText(xml, \"reduceFalseAlarm\");\n const interval = getXmlText(xml, \"interval\");\n const intervalMax = getXmlText(xml, \"intervalSecMax\");\n\n const state: PirState[\"state\"] = { channel };\n\n const enableN = parseBoolishNumber(enable);\n if (enableN !== undefined) state.enable = enableN;\n\n if (sensitive !== undefined) {\n const n = Number(sensitive);\n if (Number.isFinite(n)) state.sensitive = n;\n }\n\n const reduceAlarmN = parseBoolishNumber(reduceAlarm);\n if (reduceAlarmN !== undefined) state.reduceAlarm = reduceAlarmN;\n\n const intervalN = parseBoolishNumber(interval);\n if (intervalN !== undefined) state.interval = intervalN;\n\n const intervalMaxN = parseBoolishNumber(intervalMax);\n if (intervalMaxN !== undefined) state.intervalMax = intervalMaxN;\n\n return {\n enabled: parseEnabled(enable),\n state,\n };\n};\n","import type { ReolinkBaichuanApi } from \"../ReolinkBaichuanApi\";\nimport {\n discoverDeviceUidViaBaichuanGetP2p,\n discoverDeviceUidViaCgi,\n discoverDeviceUidViaGetInfoSerial,\n} from \"./uidDiscovery\";\nimport { formatErrorForLog } from \"./logging\";\n\nexport const discoverDeviceUidForRecordings = async (params: {\n channel: number;\n getInfo: () => ReturnType<ReolinkBaichuanApi[\"getInfo\"]>;\n cgiLogin: () => Promise<void>;\n cgiGetP2p: () => Promise<any>;\n cgiGetDevInfo: () => Promise<any>;\n sendXml: (p: Parameters<ReolinkBaichuanApi[\"sendXml\"]>[0]) => Promise<string>;\n trace: (message: string) => void;\n enableCgi?: boolean;\n}): Promise<string | undefined> => {\n const trace = params.trace;\n\n trace(\"Attempting auto-discovery via getInfo()\");\n try {\n const uid = await discoverDeviceUidViaGetInfoSerial({\n getInfo: () => params.getInfo(),\n });\n trace(`getInfo().serialNumber: ${uid || \"(missing/invalid)\"}`);\n if (uid) return uid;\n } catch (e) {\n trace(`getInfo() failed: ${formatErrorForLog(e)}`);\n }\n\n if (params.enableCgi !== false) {\n trace(\"Attempting auto-discovery via HTTP CGI API\");\n try {\n const uid = await discoverDeviceUidViaCgi({\n login: () => params.cgiLogin(),\n getP2p: () => params.cgiGetP2p(),\n getDevInfo: () => params.cgiGetDevInfo(),\n });\n trace(`[HTTP CGI] UID candidate: ${uid || \"(none)\"}`);\n if (uid) return uid;\n } catch (e) {\n trace(`[HTTP CGI] Login or requests failed: ${formatErrorForLog(e)}`);\n }\n } else {\n trace(\"Skipping HTTP CGI UID auto-discovery (disabled)\");\n }\n\n trace(\"Attempting auto-discovery via cmdId=114 (GetP2p)\");\n try {\n const uid = await discoverDeviceUidViaBaichuanGetP2p({\n sendXml: (p) => params.sendXml(p),\n });\n trace(\n uid ? `Found UID from cmdId=114: ${uid}` : \"cmdId=114 did not return UID\",\n );\n if (uid) return uid;\n } catch (e) {\n trace(`cmdId=114 failed: ${formatErrorForLog(e)}`);\n }\n\n return undefined;\n};\n","import type { WhiteLedState } from \"../types\";\nimport { buildFloodlightManualXml, getXmlText } from \"../../../protocol/xml\";\n\nexport const parseWhiteLedStateFromXml = (xml: string): WhiteLedState => {\n const enable = getXmlText(xml, \"enable\");\n const state = getXmlText(xml, \"state\");\n const status = getXmlText(xml, \"status\");\n const brightnessText = getXmlText(xml, \"brightness_cur\");\n\n const result: WhiteLedState = {\n enabled: enable === \"1\" || state === \"1\" || status === \"1\",\n };\n\n if (brightnessText !== undefined) {\n result.brightness = Number(brightnessText);\n }\n\n return result;\n};\n\nexport const buildWhiteLedManualPayloadXml = (\n channel: number,\n on: boolean,\n): string => {\n // Reolink firmware commonly expects FloodlightManual for cmd 288.\n // When enabling, 180 seconds has historically worked as a default duration.\n return buildFloodlightManualXml(channel, on ? 1 : 0, on ? 180 : 0);\n};\n\nexport const applyWhiteLedOnOffToXml = (xml: string, on: boolean): string => {\n let modifiedXml = xml;\n const val = on ? 1 : 0;\n\n // Some payloads use <enable>, others use <state> or <status>.\n if (/<enable>[^<]*<\\/enable>/i.test(modifiedXml)) {\n modifiedXml = modifiedXml.replace(\n /<enable>[^<]*<\\/enable>/i,\n `<enable>${val}</enable>`,\n );\n }\n if (/<state>[^<]*<\\/state>/i.test(modifiedXml)) {\n modifiedXml = modifiedXml.replace(\n /<state>[^<]*<\\/state>/i,\n `<state>${val}</state>`,\n );\n }\n if (/<status>[^<]*<\\/status>/i.test(modifiedXml)) {\n modifiedXml = modifiedXml.replace(\n /<status>[^<]*<\\/status>/i,\n `<status>${val}</status>`,\n );\n }\n // FloodlightTask uses <alarmMode> for motion-triggered floodlight\n if (/<alarmMode>[^<]*<\\/alarmMode>/i.test(modifiedXml)) {\n modifiedXml = modifiedXml.replace(\n /<alarmMode>[^<]*<\\/alarmMode>/i,\n `<alarmMode>${val}</alarmMode>`,\n );\n }\n\n return modifiedXml;\n};\n\nexport const applyWhiteLedBrightnessToXml = (\n xml: string,\n brightness: number,\n): string => {\n let modifiedXml = xml;\n\n if (/<brightness_cur>[^<]*<\\/brightness_cur>/i.test(modifiedXml)) {\n modifiedXml = modifiedXml.replace(\n /<brightness_cur>[^<]*<\\/brightness_cur>/i,\n `<brightness_cur>${brightness}</brightness_cur>`,\n );\n }\n\n // If a brightness was set, ensure task is enabled.\n if (/<enable>[^<]*<\\/enable>/i.test(modifiedXml)) {\n modifiedXml = modifiedXml.replace(\n /<enable>[^<]*<\\/enable>/i,\n `<enable>1</enable>`,\n );\n }\n\n return modifiedXml;\n};\n\n/**\n * Parse FloodlightTask XML to extract floodlight-on-motion state.\n * Returns the alarmMode (1 = floodlight turns on when motion detected, 0 = off)\n */\nexport interface FloodlightTaskState {\n /** Whether floodlight turns on when motion is detected (alarmMode) */\n floodlightOnMotion: boolean;\n /** Whether the task is enabled */\n enabled: boolean;\n /** Current brightness (0-100) */\n brightness?: number;\n /** Duration in seconds */\n duration?: number;\n /** Detection types (e.g., \"people,vehicle,dog_cat\") */\n detectType?: string;\n}\n\nexport const parseFloodlightTaskFromXml = (\n xml: string,\n): FloodlightTaskState => {\n const alarmMode = getXmlText(xml, \"alarmMode\");\n const enable = getXmlText(xml, \"enable\");\n const brightnessText = getXmlText(xml, \"brightness_cur\");\n const durationText = getXmlText(xml, \"duration\");\n const detectType = getXmlText(xml, \"detectType\");\n\n const result: FloodlightTaskState = {\n floodlightOnMotion: alarmMode === \"1\",\n enabled: enable === \"1\",\n };\n\n if (brightnessText !== undefined) {\n result.brightness = Number(brightnessText);\n }\n if (durationText !== undefined) {\n result.duration = Number(durationText);\n }\n if (detectType !== undefined) {\n result.detectType = detectType;\n }\n\n return result;\n};\n\n/**\n * Apply floodlight-on-motion enable/disable to FloodlightTask XML.\n * Sets both <alarmMode> and <enable> fields.\n */\nexport const applyFloodlightOnMotionToXml = (\n xml: string,\n on: boolean,\n): string => {\n let modifiedXml = xml;\n const val = on ? 1 : 0;\n\n // FloodlightTask uses both <alarmMode> and <enable> for motion-triggered floodlight\n if (/<alarmMode>[^<]*<\\/alarmMode>/i.test(modifiedXml)) {\n modifiedXml = modifiedXml.replace(\n /<alarmMode>[^<]*<\\/alarmMode>/i,\n `<alarmMode>${val}</alarmMode>`,\n );\n }\n if (/<enable>[^<]*<\\/enable>/i.test(modifiedXml)) {\n modifiedXml = modifiedXml.replace(\n /<enable>[^<]*<\\/enable>/i,\n `<enable>${val}</enable>`,\n );\n }\n\n return modifiedXml;\n};\n\n/**\n * Apply floodlight settings (duration, detectType) to FloodlightTask XML.\n */\nexport const applyFloodlightSettingsToXml = (\n xml: string,\n settings: {\n duration?: number;\n detectType?: string;\n brightness?: number;\n },\n): string => {\n let modifiedXml = xml;\n\n if (settings.duration !== undefined) {\n if (/<duration>[^<]*<\\/duration>/i.test(modifiedXml)) {\n modifiedXml = modifiedXml.replace(\n /<duration>[^<]*<\\/duration>/i,\n `<duration>${settings.duration}</duration>`,\n );\n }\n }\n\n if (settings.detectType !== undefined) {\n if (/<detectType>[^<]*<\\/detectType>/i.test(modifiedXml)) {\n modifiedXml = modifiedXml.replace(\n /<detectType>[^<]*<\\/detectType>/i,\n `<detectType>${settings.detectType}</detectType>`,\n );\n }\n }\n\n if (settings.brightness !== undefined) {\n if (/<brightness_cur>[^<]*<\\/brightness_cur>/i.test(modifiedXml)) {\n modifiedXml = modifiedXml.replace(\n /<brightness_cur>[^<]*<\\/brightness_cur>/i,\n `<brightness_cur>${settings.brightness}</brightness_cur>`,\n );\n }\n }\n\n return modifiedXml;\n};\n","import dgram from \"node:dgram\";\nimport { networkInterfaces } from \"node:os\";\nimport { ReolinkCgiApi } from \"./cgi/ReolinkCgiApi\";\nimport type { Logger } from \"../debug/DebugConfig\";\nimport { BCUDP_DISCOVERY_PORT_LOCAL_ANY, BCUDP_DISCOVERY_PORT_LOCAL_UID } from \"../bcudp/constants\";\nimport { decodeBcUdpPacket, encodeDiscoveryPacket } from \"../bcudp/packets\";\nimport { buildC2dS, parseD2cCr, parseD2cDisc } from \"../bcudp/xml\";\n\nexport interface DiscoveredDevice {\n /** Device IP address */\n host: string;\n /** Device HTTP port (default: 80) */\n httpPort?: number;\n /** Device HTTPS port (default: 443) */\n httpsPort?: number;\n /** Device model/type */\n model?: string;\n /** Device UID (if available) */\n uid?: string;\n /** Device name (if available) */\n name?: string;\n /** Firmware version (if available) */\n firmwareVersion?: string;\n /** Discovery method used to find this device */\n discoveryMethod: \"http_probe\" | \"udp_broadcast\" | \"udp_direct\";\n /** Whether HTTPS is supported */\n supportsHttps?: boolean;\n /** Whether the device is accessible via HTTP */\n httpAccessible?: boolean;\n}\n\n/**\n * Discover a single device via UDP unicast (useful when broadcast is blocked).\n * This does NOT establish a BCUDP session; it only extracts UID/model/name if present.\n */\nexport async function discoverViaUdpDirect(host: string, options: DiscoveryOptions): Promise<DiscoveredDevice[]> {\n if (!options.enableUdpDiscovery) return [];\n\n const logger = options.logger;\n const timeoutMs = options.udpBroadcastTimeoutMs ?? 1500;\n const discovered: DiscoveredDevice[] = [];\n\n const targetHost = host?.trim();\n if (!targetHost) return [];\n\n logger?.log?.(`[Discovery] Starting UDP direct discovery to ${targetHost} on ports ${BCUDP_DISCOVERY_PORT_LOCAL_ANY} and ${BCUDP_DISCOVERY_PORT_LOCAL_UID}...`);\n\n return new Promise((resolve) => {\n const socket = dgram.createSocket(\"udp4\");\n let timeout: NodeJS.Timeout | undefined;\n\n socket.on(\"message\", (msg, rinfo) => {\n try {\n if (rinfo.address !== targetHost) return;\n const packet = decodeBcUdpPacket(msg);\n if (packet.kind !== \"discovery\") return;\n\n const cr = parseD2cCr(packet.xml);\n const disc = cr ? undefined : parseD2cDisc(packet.xml);\n if (!cr && !disc) return;\n\n const uidMatch = /<uid>([^<]+)<\\/uid>/i.exec(packet.xml);\n const modelMatch = /<model>([^<]+)<\\/model>/i.exec(packet.xml);\n const nameMatch = /<name>([^<]+)<\\/name>/i.exec(packet.xml);\n const deviceIdMatch = /<deviceId>([^<]+)<\\/deviceId>/i.exec(packet.xml);\n\n const uid = (uidMatch?.[1] ?? deviceIdMatch?.[1])?.trim();\n const model = modelMatch?.[1]?.trim();\n const name = nameMatch?.[1]?.trim();\n\n const result: DiscoveredDevice = {\n host: rinfo.address,\n discoveryMethod: \"udp_direct\",\n ...(uid ? { uid } : {}),\n ...(model ? { model } : {}),\n ...(name ? { name } : {}),\n };\n\n discovered.push(result);\n\n // We got what we need; close early.\n try {\n socket.close();\n } catch {\n // ignore\n }\n } catch {\n // ignore\n }\n });\n\n socket.on(\"error\", (err) => {\n logger?.warn?.(`[Discovery] UDP direct socket error: ${err.message}`);\n });\n\n socket.bind(() => {\n const localPort = socket.address().port;\n const discoveryPorts = [BCUDP_DISCOVERY_PORT_LOCAL_ANY, BCUDP_DISCOVERY_PORT_LOCAL_UID];\n\n for (const port of discoveryPorts) {\n try {\n const tid = Math.floor(Math.random() * 0xff) || 1;\n const xml = buildC2dS({ clientPort: localPort });\n const packet = encodeDiscoveryPacket(tid, xml);\n socket.send(packet, port, targetHost, (err) => {\n if (err) {\n logger?.debug?.(`[Discovery] Failed to send UDP direct to ${targetHost}:${port}: ${err.message}`);\n }\n });\n } catch {\n // ignore\n }\n }\n\n timeout = setTimeout(() => {\n try {\n socket.close();\n } catch {\n // ignore\n }\n }, timeoutMs);\n });\n\n socket.on(\"close\", () => {\n if (timeout) clearTimeout(timeout);\n if (discovered.length === 0) {\n logger?.log?.(`[Discovery] UDP direct complete. No response from ${targetHost}.`);\n } else {\n logger?.log?.(`[Discovery] UDP direct complete. Found ${discovered.length} device(s).`);\n }\n resolve(discovered);\n });\n });\n}\n\nexport type DiscoveryOptions = {\n /** Network CIDR to scan (e.g., \"192.168.1.0/24\"). If not provided, auto-detects local network */\n networkCidr?: string;\n /** Username to use for authentication attempts (default: \"admin\") */\n username?: string;\n /** Password to use for authentication attempts (default: empty, will try unauthenticated) */\n password?: string;\n /** Timeout per HTTP probe in milliseconds (default: 2000) */\n httpProbeTimeoutMs?: number;\n /** Timeout for UDP broadcast in milliseconds (default: 5000) */\n udpBroadcastTimeoutMs?: number;\n /** Maximum number of concurrent HTTP probes (default: 50) */\n maxConcurrentProbes?: number;\n /** Logger instance for debug output */\n logger?: Logger;\n /** Whether to enable UDP broadcast discovery (default: true) */\n enableUdpDiscovery?: boolean;\n /** Whether to enable HTTP port scanning (default: true) */\n enableHttpScanning?: boolean;\n /** Ports to scan for HTTP (default: [80, 443]) */\n httpPorts?: number[];\n};\n\n/**\n * Get local network interfaces and their CIDR ranges.\n */\nfunction getLocalNetworks(): string[] {\n const networks: string[] = [];\n const interfaces = networkInterfaces();\n\n for (const ifaceName of Object.keys(interfaces)) {\n const iface = interfaces[ifaceName];\n if (!iface) continue;\n\n for (const addr of iface) {\n // Skip internal and non-IPv4 addresses\n if (addr.internal || addr.family !== \"IPv4\" || !addr.netmask) continue;\n\n // Calculate CIDR from IP and netmask\n const ipParts = addr.address.split(\".\").map(Number);\n const maskParts = addr.netmask.split(\".\").map(Number);\n\n // Count network bits\n let cidr = 0;\n for (let i = 0; i < 4; i++) {\n const maskValue = maskParts[i];\n if (maskValue === undefined || !Number.isFinite(maskValue)) break;\n if (maskValue === 255) {\n cidr += 8;\n } else if (maskValue === 0) {\n break;\n } else {\n // Count bits in partial octet\n let bits = 0;\n let m: number = maskValue;\n while (m > 0) {\n if (m & 1) bits++;\n m = m >> 1;\n }\n cidr += bits;\n break;\n }\n }\n\n const networkCidr = `${addr.address.split(\".\").slice(0, 3).join(\".\")}.0/${cidr}`;\n if (!networks.includes(networkCidr)) {\n networks.push(networkCidr);\n }\n }\n }\n\n return networks;\n}\n\n/**\n * Parse CIDR notation to get IP range.\n */\nfunction parseCidr(cidr: string): { start: number; end: number; count: number } | null {\n const parts = cidr.split(\"/\");\n const network = parts[0];\n const prefixStr = parts[1];\n if (!network) return null;\n const prefix = Number.parseInt(prefixStr ?? \"24\", 10);\n if (!Number.isFinite(prefix) || prefix < 0 || prefix > 32) return null;\n\n const ipParts = network.split(\".\").map(Number);\n if (ipParts.length !== 4 || ipParts.some((p) => !Number.isFinite(p) || p < 0 || p > 255)) return null;\n\n const networkBits = prefix;\n const hostBits = 32 - networkBits;\n\n // Calculate network address\n let networkAddr = 0;\n for (let i = 0; i < 4; i++) {\n const part = ipParts[i];\n if (part === undefined || !Number.isFinite(part)) return null;\n networkAddr = (networkAddr << 8) | (part & 0xff);\n }\n\n // Mask network bits\n const mask = ((1 << networkBits) - 1) << hostBits;\n networkAddr &= mask;\n\n // Calculate host range (skip network and broadcast addresses for /24)\n const hostCount = 1 << hostBits;\n const start = prefix >= 24 ? networkAddr + 1 : networkAddr;\n const end = prefix >= 24 ? networkAddr + hostCount - 2 : networkAddr + hostCount - 1;\n\n return {\n start,\n end,\n count: end - start + 1,\n };\n}\n\n/**\n * Convert IP number to string.\n */\nfunction ipNumberToString(ip: number): string {\n return `${(ip >>> 24) & 0xff}.${(ip >>> 16) & 0xff}.${(ip >>> 8) & 0xff}.${ip & 0xff}`;\n}\n\n/**\n * Probe a single IP address for Reolink device via HTTP/CGI.\n */\nasync function probeHttpDevice(\n ip: string,\n port: number,\n options: {\n username?: string;\n password?: string;\n timeoutMs: number;\n logger?: Logger;\n useHttps?: boolean;\n },\n): Promise<DiscoveredDevice | null> {\n const { username, password, timeoutMs, logger, useHttps } = options;\n\n try {\n // Try to connect and get device info without authentication first\n const cgi = new ReolinkCgiApi({\n host: ip,\n port,\n useHttps: useHttps ?? false,\n username: username ?? \"admin\",\n password: password ?? \"\",\n timeoutMs,\n });\n\n // Try to get device info (this will fail if auth is required, which is fine)\n try {\n const info = await cgi.getInfo();\n if (info?.type) {\n logger?.log?.(`[Discovery] Found Reolink device at ${ip}:${port} (${useHttps ? \"HTTPS\" : \"HTTP\"}) - ${info.type}`);\n const result: DiscoveredDevice = {\n host: ip,\n discoveryMethod: \"http_probe\",\n supportsHttps: useHttps ?? false,\n httpAccessible: !useHttps,\n };\n if (port !== undefined) {\n if (useHttps) {\n result.httpsPort = port;\n } else {\n result.httpPort = port;\n }\n }\n if (info.type) result.model = info.type.trim();\n if (info.name) result.name = info.name.trim();\n if (info.firmwareVersion) result.firmwareVersion = info.firmwareVersion.trim();\n return result;\n }\n } catch {\n // If unauthenticated fails, try with credentials if provided\n if (username && password) {\n try {\n await cgi.login();\n const info = await cgi.getInfo();\n if (info?.type) {\n logger?.log?.(`[Discovery] Found authenticated Reolink device at ${ip}:${port} (${useHttps ? \"HTTPS\" : \"HTTP\"}) - ${info.type}`);\n const result: DiscoveredDevice = {\n host: ip,\n discoveryMethod: \"http_probe\",\n supportsHttps: useHttps ?? false,\n httpAccessible: !useHttps,\n };\n if (port !== undefined) {\n if (useHttps) {\n result.httpsPort = port;\n } else {\n result.httpPort = port;\n }\n }\n if (info.type) result.model = info.type.trim();\n if (info.name) result.name = info.name.trim();\n if (info.firmwareVersion) result.firmwareVersion = info.firmwareVersion.trim();\n return result;\n }\n } catch {\n // Ignore auth failures\n }\n }\n }\n\n // Note: We already tried via ReolinkCgiApi above, so if we get here\n // the device is either not Reolink or not accessible\n } catch (err) {\n // Ignore connection errors\n const msg = err instanceof Error ? err.message : String(err);\n if (!msg.includes(\"ECONNREFUSED\") && !msg.includes(\"ETIMEDOUT\")) {\n logger?.warn?.(`[Discovery] Error probing ${ip}:${port}: ${msg}`);\n }\n }\n\n return null;\n}\n\n/**\n * Discover devices via HTTP port scanning.\n */\nexport async function discoverViaHttpScan(options: DiscoveryOptions): Promise<DiscoveredDevice[]> {\n if (!options.enableHttpScanning) return [];\n\n const logger = options.logger;\n const networkCidr = options.networkCidr ?? getLocalNetworks()[0];\n const httpPorts = options.httpPorts ?? [80, 443];\n const timeoutMs = options.httpProbeTimeoutMs ?? 2000;\n const maxConcurrent = options.maxConcurrentProbes ?? 50;\n\n if (!networkCidr) {\n logger?.warn?.(\"[Discovery] No network CIDR available for HTTP scanning\");\n return [];\n }\n\n logger?.log?.(`[Discovery] Starting HTTP scan on network ${networkCidr}...`);\n\n const ipRange = parseCidr(networkCidr);\n if (!ipRange) {\n logger?.warn?.(`[Discovery] Invalid CIDR: ${networkCidr}`);\n return [];\n }\n\n const discovered: DiscoveredDevice[] = [];\n const ipAddresses: Array<{ ip: string; port: number; useHttps: boolean }> = [];\n\n // Generate IP addresses to scan\n for (let ipNum = ipRange.start; ipNum <= ipRange.end && ipNum <= ipRange.start + 254; ipNum++) {\n const ip = ipNumberToString(ipNum);\n for (const port of httpPorts) {\n ipAddresses.push({ ip, port, useHttps: port === 443 });\n }\n }\n\n logger?.log?.(`[Discovery] Scanning ${ipAddresses.length} IP:port combinations...`);\n\n // Probe in batches to limit concurrency\n for (let i = 0; i < ipAddresses.length; i += maxConcurrent) {\n const batch = ipAddresses.slice(i, i + maxConcurrent);\n const batchResults = await Promise.allSettled(\n batch.map(({ ip, port, useHttps }) => {\n const probeOptions: {\n username?: string;\n password?: string;\n timeoutMs: number;\n logger?: Logger;\n useHttps?: boolean;\n } = {\n timeoutMs,\n useHttps,\n };\n if (options.username !== undefined) probeOptions.username = options.username;\n if (options.password !== undefined) probeOptions.password = options.password;\n if (logger !== undefined) probeOptions.logger = logger;\n return probeHttpDevice(ip, port, probeOptions);\n }),\n );\n\n for (const result of batchResults) {\n if (result.status === \"fulfilled\" && result.value) {\n discovered.push(result.value);\n }\n }\n }\n\n logger?.log?.(`[Discovery] HTTP scan complete. Found ${discovered.length} device(s).`);\n return discovered;\n}\n\n/**\n * Discover devices via UDP broadcast (for battery cameras).\n */\nexport async function discoverViaUdpBroadcast(options: DiscoveryOptions): Promise<DiscoveredDevice[]> {\n if (!options.enableUdpDiscovery) return [];\n\n const logger = options.logger;\n const timeoutMs = options.udpBroadcastTimeoutMs ?? 5000;\n const discovered: DiscoveredDevice[] = [];\n\n logger?.log?.(`[Discovery] Starting UDP broadcast discovery on ports ${BCUDP_DISCOVERY_PORT_LOCAL_ANY} and ${BCUDP_DISCOVERY_PORT_LOCAL_UID}...`);\n\n return new Promise((resolve) => {\n const socket = dgram.createSocket(\"udp4\");\n const devices = new Map<string, DiscoveredDevice>();\n let timeout: NodeJS.Timeout | undefined;\n\n socket.on(\"message\", (msg, rinfo) => {\n try {\n const packet = decodeBcUdpPacket(msg);\n if (packet.kind === \"discovery\") {\n try {\n const host = rinfo.address;\n \n // Try to parse D2C_CR (discovery response to C2D_S/C2D_C)\n const cr = parseD2cCr(packet.xml);\n if (cr && !devices.has(host)) {\n logger?.log?.(`[Discovery] Found device via UDP broadcast: ${host} (cid=${cr.cid}, did=${cr.did})`);\n const result: DiscoveredDevice = {\n host,\n discoveryMethod: \"udp_broadcast\",\n };\n \n // Try to extract UID, model, name from XML if present\n const uidMatch = /<uid>([^<]+)<\\/uid>/i.exec(packet.xml);\n const modelMatch = /<model>([^<]+)<\\/model>/i.exec(packet.xml);\n const nameMatch = /<name>([^<]+)<\\/name>/i.exec(packet.xml);\n const deviceIdMatch = /<deviceId>([^<]+)<\\/deviceId>/i.exec(packet.xml);\n\n const uid = uidMatch?.[1] ?? deviceIdMatch?.[1];\n const model = modelMatch?.[1];\n const name = nameMatch?.[1];\n\n if (model) result.model = model.trim();\n if (uid) result.uid = uid.trim();\n if (name) result.name = name.trim();\n \n devices.set(host, result);\n return;\n }\n \n // Fallback: try to parse D2C_DISC (disconnect message, but might contain info)\n const disc = parseD2cDisc(packet.xml);\n if (disc && !devices.has(host)) {\n // Try to extract UID, model, name from XML if present\n const uidMatch = /<uid>([^<]+)<\\/uid>/i.exec(packet.xml);\n const modelMatch = /<model>([^<]+)<\\/model>/i.exec(packet.xml);\n const nameMatch = /<name>([^<]+)<\\/name>/i.exec(packet.xml);\n const deviceIdMatch = /<deviceId>([^<]+)<\\/deviceId>/i.exec(packet.xml);\n\n const uid = uidMatch?.[1] ?? deviceIdMatch?.[1];\n const model = modelMatch?.[1];\n const name = nameMatch?.[1];\n\n logger?.log?.(`[Discovery] Found device via UDP broadcast: ${host}${uid ? ` (UID: ${uid})` : \"\"}`);\n const result: DiscoveredDevice = {\n host,\n discoveryMethod: \"udp_broadcast\",\n };\n if (model) result.model = model.trim();\n if (uid) result.uid = uid.trim();\n if (name) result.name = name.trim();\n devices.set(host, result);\n }\n } catch (err) {\n // Ignore parse errors\n logger?.debug?.(\n `[Discovery] Failed to parse UDP discovery response from ${rinfo.address}:${rinfo.port}: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n }\n } catch (err) {\n // Ignore decode errors\n }\n });\n\n socket.on(\"error\", (err) => {\n logger?.warn?.(`[Discovery] UDP socket error: ${err.message}`);\n });\n\n socket.bind(() => {\n socket.setBroadcast(true);\n const localPort = socket.address().port;\n\n // Send discovery packets using proper C2D_S messages\n // Port 2015: general discovery (C2D_S without UID)\n // Port 2018: UID-specific discovery (would use C2D_C, but we don't have UIDs here)\n const discoveryPorts = [BCUDP_DISCOVERY_PORT_LOCAL_ANY, BCUDP_DISCOVERY_PORT_LOCAL_UID];\n \n for (const port of discoveryPorts) {\n try {\n // Build C2D_S message for general discovery\n const tid = Math.floor(Math.random() * 0xff) || 1;\n const xml = buildC2dS({ clientPort: localPort });\n const packet = encodeDiscoveryPacket(tid, xml);\n \n // Send to broadcast address\n socket.send(packet, port, \"255.255.255.255\", (err) => {\n if (err) {\n logger?.warn?.(`[Discovery] Failed to send UDP broadcast to port ${port}: ${err.message}`);\n }\n });\n } catch (err) {\n logger?.warn?.(`[Discovery] Error encoding discovery packet for port ${port}: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n\n // Set timeout\n timeout = setTimeout(() => {\n socket.close();\n resolve(Array.from(devices.values()));\n logger?.log?.(`[Discovery] UDP broadcast complete. Found ${devices.size} device(s).`);\n }, timeoutMs);\n });\n\n // Cleanup on close\n socket.on(\"close\", () => {\n if (timeout) clearTimeout(timeout);\n });\n });\n}\n\n/**\n * Discover Reolink devices on the local network using multiple methods.\n * This is a \"best effort\" discovery that tries HTTP port scanning and UDP broadcast.\n *\n * @param options - Discovery configuration options\n * @returns Array of discovered devices\n *\n * @example\n * ```typescript\n * const devices = await discoverReolinkDevices({\n * username: \"admin\",\n * password: \"password\",\n * logger: console,\n * });\n *\n * for (const device of devices) {\n * console.log(`Found: ${device.host} - ${device.model} (${device.uid})`);\n * }\n * ```\n */\nexport async function discoverReolinkDevices(options: DiscoveryOptions = {}): Promise<DiscoveredDevice[]> {\n const logger = options.logger;\n logger?.log?.(\"[Discovery] Starting Reolink device discovery...\");\n\n const results: DiscoveredDevice[] = [];\n const seenDevices = new Map<string, DiscoveredDevice>();\n\n // Merge results from different discovery methods\n const mergeDevice = (device: DiscoveredDevice) => {\n const key = device.host;\n const existing = seenDevices.get(key);\n if (existing) {\n // Merge information\n if (!existing.model && device.model) existing.model = device.model;\n if (!existing.uid && device.uid) existing.uid = device.uid;\n if (!existing.name && device.name) existing.name = device.name;\n if (!existing.firmwareVersion && device.firmwareVersion) existing.firmwareVersion = device.firmwareVersion;\n if (device.httpPort && !existing.httpPort) existing.httpPort = device.httpPort;\n if (device.httpsPort && !existing.httpsPort) existing.httpsPort = device.httpsPort;\n if (device.supportsHttps !== undefined) existing.supportsHttps = device.supportsHttps;\n if (device.httpAccessible !== undefined) existing.httpAccessible = device.httpAccessible;\n } else {\n seenDevices.set(key, { ...device });\n results.push(seenDevices.get(key)!);\n }\n };\n\n // Run discovery methods in parallel\n const [httpDevices, udpDevices] = await Promise.all([\n discoverViaHttpScan(options),\n discoverViaUdpBroadcast(options),\n ]);\n\n // Merge results\n for (const device of httpDevices) {\n mergeDevice(device);\n }\n for (const device of udpDevices) {\n mergeDevice(device);\n }\n\n logger?.log?.(`[Discovery] Discovery complete. Found ${results.length} unique device(s).`);\n return results;\n}\n\n","import type { BaichuanClientOptions } from \"../client/BaichuanClient\";\nimport type { Logger } from \"../debug/DebugConfig\";\nimport { BC_CLASS_MODERN_20, BC_CLASS_MODERN_24 } from \"../protocol/constants\";\nimport { DUAL_LENS_MODELS, isDualLenseModel, ReolinkBaichuanApi } from \"./baichuan/ReolinkBaichuanApi\";\nimport { discoverViaUdpBroadcast, discoverViaUdpDirect } from \"./discovery\";\nimport type { ReolinkDeviceInfo } from \"./types\";\n\nexport type BaichuanTransport = \"tcp\" | \"udp\";\n\nexport type AutoDetectMode = \"auto\" | BaichuanTransport;\n\nexport type AutoDetectInputs = {\n host: string;\n username: string;\n password: string;\n uid?: string;\n logger?: Logger;\n debugOptions?: BaichuanClientOptions[\"debugOptions\"];\n /**\n * Force a specific transport mode.\n * - `auto` (default): try TCP first, then fallback to UDP when the error looks like a transport failure.\n * - `tcp`: only attempt TCP.\n * - `udp`: only attempt UDP (will try to discover UID if missing).\n */\n mode?: AutoDetectMode;\n /**\n * Maximum number of retries for each phase (TCP login and each UDP method).\n * NOTE: BaichuanClient.login() may still have its own internal retry/backoff.\n */\n maxRetries?: number;\n /**\n * Optional override for BCUDP discovery method.\n * If omitted, autodetect will try `local-direct`, then `local-broadcast`, then `remote`, then `relay`, then `map`.\n */\n udpDiscoveryMethod?: BaichuanClientOptions[\"udpDiscoveryMethod\"];\n};\n\nexport type DeviceType = \"camera\" | \"battery-cam\" | \"nvr\" | \"multifocal\";\n\nexport type AutoDetectResult = {\n type: DeviceType;\n transport: BaichuanTransport;\n uid: string;\n /** If `transport === \"udp\"`, the UDP discovery method that succeeded. */\n udpDiscoveryMethod?: BaichuanClientOptions[\"udpDiscoveryMethod\"];\n deviceInfo?: Partial<ReolinkDeviceInfo>;\n /**\n * Best-effort host network info (from Baichuan).\n * Typically includes ip/mac and sometimes link type.\n */\n hostNetworkInfo?: {\n ip?: string;\n mac?: string;\n /** Active link / link type when available (varies by firmware). */\n activeLink?: string;\n };\n channelNum?: number;\n api: ReolinkBaichuanApi; // The API instance that was successfully used for detection\n};\n\n/**\n * Normalize UID string (trim and return undefined if empty).\n */\nexport function normalizeUid(uid?: string): string | undefined {\n const v = uid?.trim();\n return v ? v : undefined;\n}\n\n/**\n * Mask UID for logging (show first 4 and last 4 characters).\n */\nexport function maskUid(uid: string): string {\n const v = uid.trim();\n if (v.length <= 8) return v;\n return `${v.slice(0, 4)}…${v.slice(-4)}`;\n}\n\nasync function resolveHostToIp(host: string): Promise<string | undefined> {\n try {\n // Avoid pulling in dns for plain IPv4/IPv6 literals.\n // Quick heuristic: if it contains letters, it's likely a hostname.\n if (!/[a-z]/i.test(host)) return host;\n const { lookup } = await import(\"node:dns/promises\");\n const res = await lookup(host);\n return res?.address ?? host;\n } catch {\n return host;\n }\n}\n\nasync function discoverUidForHost(host: string, logger?: Logger): Promise<string | undefined> {\n try {\n const ip = await resolveHostToIp(host);\n const directTarget = ip ?? host;\n\n // 1) Try UDP unicast (local-direct style). This can work even when broadcast is filtered.\n try {\n const directDevices = await discoverViaUdpDirect(directTarget, {\n enableUdpDiscovery: true,\n udpBroadcastTimeoutMs: 1200,\n ...(logger ? { logger } : {}),\n });\n const directMatch = directDevices.find((d) => d.host === directTarget);\n const directUid = normalizeUid(directMatch?.uid);\n if (directUid) {\n logger?.log?.(`[AutoDetect] UID discovered via UDP direct: ${maskUid(directUid)}`);\n return directUid;\n }\n } catch {\n // ignore\n }\n\n // 2) Fallback to UDP broadcast.\n const options = {\n enableUdpDiscovery: true,\n udpBroadcastTimeoutMs: 1500,\n ...(logger ? { logger } : {}),\n };\n const devices = await discoverViaUdpBroadcast(options);\n const match = devices.find((d) => d.host === ip || d.host === host);\n const uid = normalizeUid(match?.uid);\n if (uid) {\n logger?.log?.(`[AutoDetect] UID discovered via UDP broadcast: ${maskUid(uid)}`);\n return uid;\n }\n } catch {\n // ignore\n }\n return undefined;\n}\n\n/**\n * Check if a TCP error should trigger UDP fallback.\n * Only transport/connection errors should fallback, not authentication errors.\n */\nexport function isTcpFailureThatShouldFallbackToUdp(e: unknown): boolean {\n const message = (e as any)?.message || (e as any)?.toString?.() || \"\";\n if (typeof message !== \"string\") return false;\n\n // Fallback only on transport/connection style failures.\n // Wrong credentials won't be fixed by switching to UDP.\n //\n // Note: some devices accept the TCP connection but fail during the initial\n // nonce/encryption negotiation (socket close / timeout / unexpected reply).\n // In `mode=auto`, it's still reasonable to fallback to UDP/BCUDP in that case.\n return (\n message.includes(\"ECONNREFUSED\") ||\n message.includes(\"ETIMEDOUT\") ||\n message.includes(\"EHOSTUNREACH\") ||\n message.includes(\"ENETUNREACH\") ||\n message.includes(\"socket hang up\") ||\n message.includes(\"TCP connection timeout\") ||\n message.includes(\"Baichuan socket closed\") ||\n message.includes(\"timeout waiting for nonce\") ||\n message.includes(\"expected encryption info\") ||\n message.includes(\"ECONNRESET\") ||\n message.includes(\"EPIPE\")\n );\n}\n\n/**\n * Simple ping check to verify IP is reachable.\n */\nasync function pingHost(host: string, timeoutMs: number = 3000): Promise<boolean> {\n return new Promise((resolve) => {\n const { exec } = require(\"child_process\");\n const platform = process.platform;\n const pingCmd =\n platform === \"win32\"\n ? `ping -n 1 -w ${timeoutMs} ${host}`\n : platform === \"darwin\"\n // macOS: -W is in milliseconds (Linux: seconds)\n ? `ping -c 1 -W ${timeoutMs} ${host}`\n // Linux/BSD-ish: -W is in seconds on most distros\n : `ping -c 1 -W ${Math.max(1, Math.floor(timeoutMs / 1000))} ${host}`;\n\n exec(pingCmd, (error: any) => {\n resolve(!error);\n });\n });\n}\n\n/**\n * Create a Baichuan API instance with error handling.\n */\nfunction createBaichuanApi(\n inputs: AutoDetectInputs,\n transport: BaichuanTransport,\n): ReolinkBaichuanApi {\n const base: BaichuanClientOptions = {\n host: inputs.host,\n username: inputs.username,\n password: inputs.password,\n ...(inputs.logger !== undefined ? { logger: inputs.logger } : {}),\n debugOptions: inputs.debugOptions ?? {},\n };\n\n if (transport === \"tcp\") {\n const api = new ReolinkBaichuanApi({\n ...base,\n transport: \"tcp\",\n });\n attachErrorHandler(api, transport, inputs);\n return api;\n }\n\n const uid = normalizeUid(inputs.uid);\n if (!uid) {\n throw new Error(\"UID is required for battery cameras (BCUDP)\");\n }\n\n const api = new ReolinkBaichuanApi({\n ...base,\n transport: \"udp\",\n uid,\n ...(inputs.udpDiscoveryMethod ? { udpDiscoveryMethod: inputs.udpDiscoveryMethod } : {}),\n idleDisconnect: true,\n });\n attachErrorHandler(api, transport, inputs);\n return api;\n}\n\n/**\n * Attach error handler to BaichuanClient to prevent uncaught exceptions.\n */\nfunction attachErrorHandler(api: ReolinkBaichuanApi, transport: BaichuanTransport, inputs: AutoDetectInputs): void {\n try {\n api.client.on(\"error\", (err: unknown) => {\n if (!inputs.logger) return;\n const msg = (err as any)?.message || (err as any)?.toString?.() || String(err);\n // Only log if it's not a recoverable error to avoid spam\n if (\n typeof msg === \"string\" &&\n (msg.includes(\"Baichuan socket closed\") ||\n msg.includes(\"Baichuan UDP stream closed\") ||\n msg.includes(\"Not running\"))\n ) {\n // Silently ignore recoverable socket close errors and \"Not running\" errors\n // \"Not running\" is common for UDP/battery cameras when sleeping or during initialization\n return;\n }\n inputs.logger?.log?.(`[BaichuanClient] error (${transport}) ${inputs.host}: ${msg}`);\n });\n\n // Handle 'close' event to prevent unhandled rejections from pending promises\n api.client.on(\"close\", () => {\n // Socket closed - pending promises will be rejected, but we've already handled errors above\n // This handler prevents the close event from causing issues\n });\n } catch {\n // ignore\n }\n}\n\n/**\n * Auto-detect device type by trying TCP first, then UDP if needed.\n * - First: Ping the IP to verify it's reachable\n * - TCP success: Check if NVR (multiple channels) or regular camera\n * - TCP failure: Try UDP (always battery camera)\n */\nexport async function autoDetectDeviceType(inputs: AutoDetectInputs): Promise<AutoDetectResult> {\n const { host, uid, logger } = inputs;\n\n const mode: AutoDetectMode = (inputs.mode ?? \"auto\") as AutoDetectMode;\n const maxRetriesRaw = inputs.maxRetries;\n const maxRetries = Math.max(1, Math.min(10, typeof maxRetriesRaw === \"number\" && Number.isFinite(maxRetriesRaw) ? Math.floor(maxRetriesRaw) : 1));\n\n const fmtErr = (e: unknown): string => {\n const m = (e as any)?.message || (e as any)?.toString?.() || String(e);\n return typeof m === \"string\" ? m : String(m);\n };\n\n const sleepMs = (ms: number) => new Promise<void>((resolve) => setTimeout(resolve, ms));\n\n const shouldRetryTcp = (e: unknown): boolean => {\n const msg = fmtErr(e);\n // Retry on transport errors, negotiation hiccups, or abrupt socket termination.\n return (\n isTcpFailureThatShouldFallbackToUdp(e) ||\n msg.includes(\"timeout waiting for nonce\") ||\n msg.includes(\"expected encryption info\") ||\n msg.includes(\"Baichuan socket closed\") ||\n msg.includes(\"ECONNRESET\") ||\n msg.includes(\"EPIPE\")\n );\n };\n\n const shouldRetryUdp = (e: unknown): boolean => {\n const msg = fmtErr(e);\n // Battery cams are often flaky/sleepy: retry on common transient signals.\n return (\n msg.includes(\"Not running\") ||\n msg.includes(\"Baichuan UDP stream closed\") ||\n msg.includes(\"Baichuan socket closed\") ||\n msg.includes(\"ETIMEDOUT\") ||\n msg.toLowerCase().includes(\"timeout\")\n );\n };\n\n const withRetries = async <T>(\n label: string,\n max: number,\n op: (attempt: number) => Promise<T>,\n shouldRetry: (e: unknown) => boolean,\n ): Promise<T> => {\n let lastErr: unknown;\n for (let attempt = 1; attempt <= max; attempt++) {\n try {\n if (attempt > 1) {\n logger?.log?.(`[AutoDetect] ${label}: retry ${attempt}/${max}...`);\n }\n return await op(attempt);\n } catch (e) {\n lastErr = e;\n const msg = fmtErr(e);\n const retryable = attempt < max && shouldRetry(e);\n logger?.log?.(`[AutoDetect] ${label} attempt ${attempt}/${max} failed: ${msg}${retryable ? \" (will retry)\" : \"\"}`);\n if (!retryable) throw e;\n // Small backoff to avoid tight reconnect loops.\n await sleepMs(350);\n }\n }\n throw lastErr instanceof Error ? lastErr : new Error(String(lastErr ?? `${label} failed`));\n };\n\n // Best-effort: discover UID if not provided (useful for BCUDP/battery cams).\n // This is cheap and non-invasive (UDP broadcast).\n // const discoveredUid = uid ? undefined : await discoverUidForHost(host, logger);\n // const effectiveUid = normalizeUid(uid) ?? discoveredUid;\n const effectiveUid = normalizeUid(uid);\n\n // Ping the host first to verify it's reachable\n logger?.log?.(`[AutoDetect] Pinging ${host}...`);\n const isReachable = await pingHost(host);\n if (!isReachable) {\n logger?.log?.(`[AutoDetect] Host ${host} is not reachable via ping, but continuing with connection attempt...`);\n } else {\n logger?.log?.(`[AutoDetect] Host ${host} is reachable`);\n }\n\n // Forced UDP mode: skip TCP entirely.\n if (mode === \"udp\") {\n logger?.log?.(`[AutoDetect] Forced mode=udp, skipping TCP and starting UDP discovery/login...`);\n let normalizedUid = effectiveUid;\n if (!normalizedUid) {\n logger?.log?.(`[AutoDetect] UID not provided; attempting UDP discovery for UID...`);\n const discovered = await discoverUidForHost(host, logger);\n const normalizedDiscovered = normalizeUid(discovered);\n if (!normalizedDiscovered) {\n throw new Error(\n `Forced UDP autodetect requires UID (or successful UDP UID discovery), but none was provided/discovered (ip=${host}).`,\n );\n }\n normalizedUid = normalizedDiscovered;\n }\n\n const methodsToTry: Array<NonNullable<BaichuanClientOptions[\"udpDiscoveryMethod\"]>> =\n inputs.udpDiscoveryMethod\n ? [inputs.udpDiscoveryMethod]\n : [\"local-direct\", \"local-broadcast\", \"remote\", \"relay\", \"map\"];\n\n const udpErrors: string[] = [];\n for (const m of methodsToTry) {\n try {\n logger?.log?.(`[AutoDetect] Trying UDP discovery method: ${m}...`);\n const udpApi = await withRetries(\n `UDP(${m})`,\n maxRetries,\n async (attempt) => {\n const api = createBaichuanApi({ ...inputs, uid: normalizedUid, udpDiscoveryMethod: m }, \"udp\");\n try {\n await api.login();\n return api;\n } catch (e) {\n try {\n await api.close({ reason: `autodetect:udp_failed:${m}:attempt_${attempt}` });\n } catch {\n // ignore\n }\n throw e;\n }\n },\n shouldRetryUdp,\n );\n\n // Reuse the same detection logic used by the fallback path.\n const deviceInfo = await udpApi.getInfo();\n const capabilities = await udpApi.getDeviceCapabilities();\n const hostNetworkInfo = await udpApi.getNetworkInfo(undefined, { timeoutMs: 1200 }).catch(() => undefined);\n const channelNum = capabilities?.support?.channelNum ?? 1;\n const model = deviceInfo.type?.trim();\n\n const normalizedModel = model ? model.trim() : undefined;\n const isMultifocalByModel = normalizedModel ? isDualLenseModel(normalizedModel) : false;\n const channelNumValue = typeof channelNum === \"string\" ? Number.parseInt(channelNum, 10) : channelNum;\n const hasDualLensChannelCount = (channelNumValue === 2 || channelNumValue === 3) && Number.isFinite(channelNumValue);\n const isMultifocal = isMultifocalByModel || hasDualLensChannelCount;\n\n if (isMultifocal) {\n const detectionMethod = isMultifocalByModel ? \"model match\" : \"channelNum fallback\";\n logger?.log?.(\n `[AutoDetect] UDP (${m}) connection successful. Detected multi-focal device (${detectionMethod}: model=${normalizedModel ?? \"unknown\"}, channelNum=${channelNum}).`,\n );\n return {\n type: \"multifocal\",\n transport: \"udp\",\n uid: normalizedUid,\n udpDiscoveryMethod: m,\n deviceInfo,\n ...(hostNetworkInfo ? { hostNetworkInfo } : {}),\n channelNum,\n api: udpApi,\n };\n }\n\n logger?.log?.(`[AutoDetect] UDP (${m}) connection successful. Detected battery camera.`);\n return {\n type: \"battery-cam\",\n transport: \"udp\",\n uid: normalizedUid,\n udpDiscoveryMethod: m,\n deviceInfo,\n ...(hostNetworkInfo ? { hostNetworkInfo } : {}),\n channelNum: 1,\n api: udpApi,\n };\n } catch (e) {\n const msg = fmtErr(e);\n udpErrors.push(`${m}: ${msg}`);\n // (best-effort) resources are already closed inside the retry wrapper.\n logger?.log?.(`[AutoDetect] UDP (${m}) failed: ${msg}`);\n }\n }\n\n throw new Error(`Forced UDP autodetect failed for all methods. ${udpErrors.join(\" | \")}`);\n }\n\n // Try TCP first\n let tcpApi: ReolinkBaichuanApi | undefined;\n try {\n logger?.log?.(`[AutoDetect] Trying TCP connection to ${host}...`);\n tcpApi = await withRetries(\n \"TCP\",\n maxRetries,\n async (attempt) => {\n // Create a fresh API for each attempt to avoid stale socket state.\n const api = createBaichuanApi(inputs, \"tcp\");\n try {\n await api.login();\n return api;\n } catch (e) {\n try {\n await api.close({ reason: `autodetect:tcp_failed:attempt_${attempt}` });\n } catch {\n // ignore\n }\n throw e;\n }\n },\n shouldRetryTcp,\n );\n\n // Get device info to check device type\n const api = tcpApi;\n if (!api) throw new Error(\"AutoDetect internal error: TCP API not initialized after successful login\");\n\n // Some older firmwares are picky about message class or do not support the full\n // post-login capability probe sequence. Treat post-login command failures as a degraded\n // detection, not as a TCP transport failure.\n const runProbeVariants = async <T>(\n label: string,\n variants: Array<{ variant: string; op: () => Promise<T> }>,\n ): Promise<{ value: T; variant: string } | undefined> => {\n let lastMsg: string | undefined;\n for (const v of variants) {\n try {\n const value = await v.op();\n logger?.log?.(`[AutoDetect] TCP probe ${label} OK (${v.variant})`);\n return { value, variant: v.variant };\n } catch (e) {\n const msg = fmtErr(e);\n lastMsg = msg;\n logger?.log?.(`[AutoDetect] TCP probe ${label} failed (${v.variant}): ${msg}`);\n }\n }\n if (lastMsg) {\n logger?.log?.(`[AutoDetect] TCP probe ${label} failed (all variants): ${lastMsg}`);\n }\n return undefined;\n };\n\n // Device info probes (firmware-dependent):\n // - Host cmd_id 80 (GetDevInfo)\n // - Channel cmd_id 318 (GetDevInfo per channel)\n // Some older devices respond to one but not the other.\n const infoProbe = await runProbeVariants<Partial<ReolinkDeviceInfo>>(\n \"getInfo\",\n [\n { variant: \"cmd80 class=0x6414\", op: () => api.getInfo(undefined, { timeoutMs: 2500, messageClass: BC_CLASS_MODERN_24 }) },\n { variant: \"cmd80 class=0x6614\", op: () => api.getInfo(undefined, { timeoutMs: 3000, messageClass: BC_CLASS_MODERN_20 }) },\n { variant: \"cmd318(ch0) class=0x6414\", op: () => api.getInfo(0, { timeoutMs: 3000, messageClass: BC_CLASS_MODERN_24 }) },\n { variant: \"cmd318(ch0) class=0x6614\", op: () => api.getInfo(0, { timeoutMs: 3500, messageClass: BC_CLASS_MODERN_20 }) },\n ],\n );\n\n // Support probes (cmd 199). Some firmwares may not support it or are slow.\n const supportProbe = await runProbeVariants<any>(\n \"getSupportInfo\",\n [\n { variant: \"cmd199 class=0x6414\", op: () => api.getSupportInfo({ timeoutMs: 2500, messageClass: BC_CLASS_MODERN_24 }) },\n { variant: \"cmd199 class=0x6614\", op: () => api.getSupportInfo({ timeoutMs: 3500, messageClass: BC_CLASS_MODERN_20 }) },\n ],\n );\n\n const deviceInfo = infoProbe?.value;\n const support = supportProbe?.value;\n\n const channelNumRaw = support?.channelNum;\n const channelNum = typeof channelNumRaw === \"string\" ? Number.parseInt(channelNumRaw, 10) : channelNumRaw;\n const effectiveChannelNum = Number.isFinite(channelNum) && channelNum != null ? channelNum : 1;\n const model = deviceInfo?.type?.trim();\n\n logger?.log?.(\n `[AutoDetect] TCP connection successful. channelNum=${effectiveChannelNum}${support ? \"\" : \" (support probe failed)\"}, model=${model ?? \"unknown\"}${deviceInfo ? \"\" : \" (info probe failed)\"}`,\n );\n\n // Check if it's a multi-focal device using the dual lens model map or channelNum fallback\n const normalizedModel = model ? model.trim() : undefined;\n const isMultifocalByModel = normalizedModel ? isDualLenseModel(normalizedModel) : false;\n\n // Also check if channelNum suggests dual lens (2-3 channels)\n // Handle both number and string types for channelNum\n const channelNumValue = effectiveChannelNum;\n const hasDualLensChannelCount = (channelNumValue === 2 || channelNumValue === 3) && Number.isFinite(channelNumValue);\n\n // Consider it dual lens if model matches OR if channelNum suggests it\n const isMultifocal = isMultifocalByModel || hasDualLensChannelCount;\n\n if (isMultifocal) {\n const detectionMethod = isMultifocalByModel ? \"model match\" : \"channelNum fallback\";\n logger?.log?.(`[AutoDetect] Detected multi-focal device (${detectionMethod}: model=${normalizedModel ?? \"unknown\"}, channelNum=${channelNum})`);\n // Don't close the API, return it for continued use\n return {\n type: \"multifocal\",\n transport: \"tcp\",\n uid: effectiveUid || uid || \"\",\n ...(deviceInfo ? { deviceInfo } : {}),\n // ...(hostNetworkInfo ? { hostNetworkInfo } : {}),\n channelNum: effectiveChannelNum,\n api,\n };\n }\n\n // If channelNum > 1, it's likely an NVR\n if (effectiveChannelNum > 1) {\n logger?.log?.(`[AutoDetect] Detected NVR (${effectiveChannelNum} channels)`);\n // Don't close the API, return it for continued use\n return {\n type: \"nvr\",\n transport: \"tcp\",\n uid: effectiveUid || uid || \"\",\n ...(deviceInfo ? { deviceInfo } : {}),\n // ...(hostNetworkInfo ? { hostNetworkInfo } : {}),\n channelNum: effectiveChannelNum,\n api,\n };\n }\n\n // Single channel device - regular camera\n logger?.log?.(`[AutoDetect] Detected regular camera (single channel)`);\n // Don't close the API, return it for continued use\n return {\n type: \"camera\",\n transport: \"tcp\",\n uid: effectiveUid || uid || \"\",\n ...(deviceInfo ? { deviceInfo } : {}),\n // ...(hostNetworkInfo ? { hostNetworkInfo } : {}),\n channelNum: 1,\n api,\n };\n } catch (tcpError) {\n if (mode === \"tcp\") {\n // Forced TCP mode: never fallback.\n throw tcpError;\n }\n\n // TCP failed, try UDP (battery camera)\n if (tcpApi) {\n try {\n await tcpApi.close({ reason: \"autodetect:tcp_failed\" });\n } catch {\n // ignore\n }\n }\n\n if (!isTcpFailureThatShouldFallbackToUdp(tcpError)) {\n // Not a transport error, rethrow\n throw tcpError;\n }\n\n logger?.log?.(`[AutoDetect] TCP failed, trying UDP (battery camera)...`);\n let normalizedUid = effectiveUid;\n if (!normalizedUid) {\n logger?.log?.(`[AutoDetect] UID not provided; attempting UDP broadcast discovery for UID...`);\n const discovered = await discoverUidForHost(host, logger);\n if (!discovered) {\n throw new Error(\n `TCP connection failed and device likely requires UDP/BCUDP. UID is required for battery cameras (ip=${host}).`\n );\n }\n // Continue with discovered UID.\n const normalizedDiscovered = normalizeUid(discovered);\n if (!normalizedDiscovered) {\n throw new Error(\n `TCP connection failed and device likely requires UDP/BCUDP, but UID discovery returned an empty UID (ip=${host}).`\n );\n }\n normalizedUid = normalizedDiscovered;\n }\n\n try {\n const detectOverUdpApi = async (\n udpApi: ReolinkBaichuanApi,\n udpDiscoveryMethod: NonNullable<BaichuanClientOptions[\"udpDiscoveryMethod\"]>,\n ): Promise<AutoDetectResult> => {\n const deviceInfo = await udpApi.getInfo();\n const capabilities = await udpApi.getDeviceCapabilities();\n const hostNetworkInfo = await udpApi.getNetworkInfo(undefined, { timeoutMs: 1200 }).catch(() => undefined);\n const channelNum = capabilities?.support?.channelNum ?? 1;\n const model = deviceInfo.type?.trim();\n\n // Check if it's a multi-focal device using the dual lens model map or channelNum fallback\n // Multi-focal devices can also be UDP (battery multi-focal cameras)\n const normalizedModel = model ? model.trim() : undefined;\n const isMultifocalByModel = normalizedModel ? isDualLenseModel(normalizedModel) : false;\n\n // Also check if channelNum suggests dual lens (2-3 channels)\n // Handle both number and string types for channelNum\n const channelNumValue = typeof channelNum === \"string\" ? Number.parseInt(channelNum, 10) : channelNum;\n const hasDualLensChannelCount = (channelNumValue === 2 || channelNumValue === 3) && Number.isFinite(channelNumValue);\n\n // Consider it dual lens if model matches OR if channelNum suggests it\n const isMultifocal = isMultifocalByModel || hasDualLensChannelCount;\n\n if (isMultifocal) {\n const detectionMethod = isMultifocalByModel ? \"model match\" : \"channelNum fallback\";\n logger?.log?.(\n `[AutoDetect] UDP (${udpDiscoveryMethod}) connection successful. Detected multi-focal device (${detectionMethod}: model=${normalizedModel ?? \"unknown\"}, channelNum=${channelNum}).`,\n );\n return {\n type: \"multifocal\",\n transport: \"udp\",\n uid: normalizedUid,\n udpDiscoveryMethod,\n deviceInfo,\n ...(hostNetworkInfo ? { hostNetworkInfo } : {}),\n channelNum,\n api: udpApi,\n };\n }\n\n // Regular battery camera\n logger?.log?.(`[AutoDetect] UDP (${udpDiscoveryMethod}) connection successful. Detected battery camera.`);\n return {\n type: \"battery-cam\",\n transport: \"udp\",\n uid: normalizedUid,\n udpDiscoveryMethod,\n deviceInfo,\n ...(hostNetworkInfo ? { hostNetworkInfo } : {}),\n channelNum: 1,\n api: udpApi,\n };\n };\n\n const methodsToTry: Array<NonNullable<BaichuanClientOptions[\"udpDiscoveryMethod\"]>> =\n [\"local-direct\", \"local-broadcast\", \"remote\", \"relay\", \"map\"];\n\n const udpErrors: string[] = [];\n for (const m of methodsToTry) {\n try {\n logger?.log?.(`[AutoDetect] Trying UDP discovery method: ${m}...`);\n const udpApi = await withRetries(\n `UDP(${m})`,\n maxRetries,\n async (attempt) => {\n const api = createBaichuanApi({ ...inputs, uid: normalizedUid, udpDiscoveryMethod: m }, \"udp\");\n try {\n await api.login();\n return api;\n } catch (e) {\n try {\n await api.close({ reason: `autodetect:udp_failed:${m}:attempt_${attempt}` });\n } catch {\n // ignore\n }\n throw e;\n }\n },\n shouldRetryUdp,\n );\n return await detectOverUdpApi(udpApi, m);\n } catch (e) {\n const msg = (e as any)?.message || (e as any)?.toString?.() || String(e);\n udpErrors.push(`${m}: ${msg}`);\n try {\n // ignore (api already closed in retry wrapper)\n } catch {\n // ignore\n }\n logger?.log?.(`[AutoDetect] UDP (${m}) failed: ${msg}`);\n }\n }\n\n throw new Error(`UDP discovery failed for all methods. ${udpErrors.join(\" | \")}`);\n } catch (udpError) {\n logger?.log?.(\n `[AutoDetect] Both TCP and UDP failed. TCP error: ${tcpError}, UDP error: ${udpError}`\n );\n throw new Error(\n `Failed to connect via both TCP and UDP. TCP: ${(tcpError as any)?.message || tcpError}, UDP: ${(udpError as any)?.message || udpError}`\n );\n }\n }\n}\n\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8BO,SAAS,aAAa,GAAqE;AAChG,QAAM,YAAY,yBAAyB,EAAE,YAAY;AACzD,QAAM,YAAY,YAAY,KAAK;AACnC,QAAM,MAAM,OAAO,MAAM,SAAS;AAElC,QAAM,QAAS,EAAE,SAAU;AAC3B,MAAI,MAAM,WAAW,EAAG,OAAM,IAAI,MAAM,uBAAuB;AAC/D,QAAM,KAAK,KAAK,CAAC;AAEjB,MAAI,cAAc,EAAE,UAAU,GAAG,CAAC;AAClC,MAAI,cAAc,EAAE,YAAY,GAAG,CAAC;AACpC,MAAI,WAAW,EAAE,YAAY,KAAM,EAAE;AACrC,MAAI,WAAW,EAAE,aAAa,KAAM,EAAE;AACtC,MAAI,cAAc,EAAE,SAAS,OAAQ,EAAE;AACvC,MAAI,cAAc,EAAE,eAAe,OAAQ,EAAE;AAC7C,MAAI,cAAc,EAAE,eAAe,OAAQ,EAAE;AAC7C,MAAI,WAAW;AACb,QAAI,eAAe,EAAE,iBAAiB,OAAO,GAAG,EAAE;AAAA,EACpD;AACA,SAAO;AACT;AAEO,SAAS,aAAa,KAAmF;AAC9G,MAAI,IAAI,SAAS,GAAI,OAAM,IAAI,MAAM,qCAAqC;AAE1E,QAAM,QAAQ,IAAI,SAAS,GAAG,CAAC;AAC/B,MAAI,CAAC,MAAM,OAAO,QAAQ,KAAK,CAAC,MAAM,OAAO,YAAY,GAAG;AAC1D,UAAM,IAAI,MAAM,2BAA2B,MAAM,SAAS,KAAK,CAAC,EAAE;AAAA,EACpE;AAEA,QAAM,QAAQ,IAAI,aAAa,CAAC;AAChC,QAAM,UAAU,IAAI,aAAa,CAAC;AAClC,QAAM,YAAY,IAAI,UAAU,EAAE;AAClC,QAAM,aAAa,IAAI,UAAU,EAAE;AACnC,QAAM,SAAS,IAAI,aAAa,EAAE;AAClC,QAAM,eAAe,IAAI,aAAa,EAAE;AACxC,QAAM,eAAe,IAAI,aAAa,EAAE;AAExC,QAAM,YAAY,yBAAyB,YAAY,IAAI,KAAK;AAChE,MAAI,IAAI,SAAS,UAAW,OAAM,IAAI,MAAM,sDAAsD;AAElG,QAAM,aAAa,IAAI,aAAa,EAAE;AACtC,QAAM,SAAyB;AAAA,IAC7B,OAAO,OAAO,KAAK,KAAK;AAAA,IACxB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACA,MAAI,cAAc,IAAI;AACpB,WAAO,gBAAgB,IAAI,aAAa,EAAE;AAAA,EAC5C;AAEA,SAAO,EAAE,QAAQ,WAAW,WAAW;AACzC;AASO,IAAM,sBAAN,MAA0B;AAAA,EACvB,SAAoB,OAAO,MAAM,CAAC;AAAA,EAE1C,KAAK,OAAgC;AACnC,QAAI,MAAM,WAAW,EAAG,QAAO,CAAC;AAChC,UAAM,IAAI;AACV,SAAK,SAAS,KAAK,OAAO,WAAW,IAAI,IAAK,OAAO,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC;AAC5E,UAAM,MAAuB,CAAC;AAE9B,WAAO,MAAM;AAEX,UAAI,KAAK,OAAO,SAAS,EAAG;AAG5B,UAAI,CAAC,KAAK,OAAO,SAAS,GAAG,CAAC,EAAE,OAAO,QAAQ,KAAK,CAAC,KAAK,OAAO,SAAS,GAAG,CAAC,EAAE,OAAO,YAAY,GAAG;AACpG,cAAM,MAAM,KAAK,OAAO,QAAQ,QAAQ;AACxC,cAAM,SAAS,KAAK,OAAO,QAAQ,YAAY;AAC/C,cAAM,OAAO,QAAQ,KAAK,SAAS,WAAW,KAAK,MAAM,KAAK,IAAI,KAAK,MAAM;AAC7E,YAAI,SAAS,IAAI;AAEf,eAAK,SAAS,KAAK,OAAO,SAAS,KAAK,IAAI,GAAG,KAAK,OAAO,SAAS,CAAC,CAAC;AACtE;AAAA,QACF;AACA,aAAK,SAAS,KAAK,OAAO,SAAS,IAAI;AACvC,YAAI,KAAK,OAAO,SAAS,GAAI;AAAA,MAC/B;AAEA,UAAI,KAAK,OAAO,SAAS,GAAI;AAC7B,UAAI;AACJ,UAAI;AACF,qBAAa,aAAa,KAAK,MAAM;AAAA,MACvC,QAAQ;AAEN;AAAA,MACF;AAEA,YAAM,EAAE,QAAQ,WAAW,WAAW,IAAI;AAC1C,YAAM,WAAW,YAAY,OAAO;AACpC,UAAI,KAAK,OAAO,SAAS,SAAU;AAEnC,YAAM,MAAM,KAAK,OAAO,SAAS,GAAG,QAAQ;AAC5C,YAAM,OAAO,IAAI,SAAS,SAAS;AAEnC,UAAI,SAAS;AACb,UAAI,cAAc,IAAI;AACpB,cAAM,MAAM,OAAO,iBAAiB;AAEpC,iBAAS,MAAM,IAAI,KAAK,IAAI,KAAK,OAAO,OAAO,IAAI;AAAA,MACrD;AACA,YAAM,YAAY,KAAK,SAAS,GAAG,MAAM;AACzC,YAAM,UAAU,KAAK,SAAS,MAAM;AAEpC,UAAI,KAAK,EAAE,QAAQ,MAAM,WAAW,SAAS,YAAY,IAAI,CAAC;AAC9D,WAAK,SAAS,KAAK,OAAO,SAAS,QAAQ;AAAA,IAC7C;AAEA,WAAO;AAAA,EACT;AACF;;;AC1JA,OAAO,WAAW;AAClB,OAAO,SAAS;AAChB,SAAS,oBAAoB;AAE7B,SAAS,yBAAyB;AAClC,SAAS,eAAe,uBAAuB;;;ACLxC,IAAM,mBAAmB;AACzB,IAAM,kBAAkB;AACxB,IAAM,mBAAmB;AAEzB,IAAM,oBAAoB;AAC1B,IAAM,yBAAyB;AAG/B,IAAM,iCAAiC;AACvC,IAAM,iCAAiC;;;ACL9C,IAAI;AAEJ,SAAS,WAAwB;AAC/B,MAAI,MAAO,QAAO;AAClB,QAAM,IAAI,IAAI,YAAY,GAAG;AAC7B,WAAS,IAAI,GAAG,IAAI,KAAK,KAAK;AAC5B,QAAI,IAAI;AACR,aAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AAC1B,UAAK,IAAI,IAAM,aAAc,MAAM,IAAO,MAAM;AAAA,IAClD;AACA,MAAE,CAAC,IAAI,MAAM;AAAA,EACf;AACA,UAAQ;AACR,SAAO;AACT;AAEO,SAAS,WAAW,KAAqB;AAC9C,QAAM,IAAI,SAAS;AACnB,MAAI,MAAM;AACV,WAAS,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK;AACnC,UAAM,IAAI,IAAI,CAAC;AACf,WAAO,GAAG,MAAM,KAAK,GAAI,IAAM,QAAQ,OAAQ;AAAA,EACjD;AACA,SAAO,QAAQ;AACjB;;;AC1BA,IAAM,cAAc,YAAY,KAAK;AAAA,EACnC;AAAA,EAAY;AAAA,EAAY;AAAA,EAAY;AAAA,EAAY;AAAA,EAAY;AAAA,EAAY;AAAA,EAAY;AACtF,CAAC;AAED,UAAU,UAAU,QAA+C;AACjE,MAAI,MAAM;AACV,SAAO,MAAM;AACX,UAAM,OAAQ,YAAY,MAAM,YAAY,MAAM,KAAM,WAAW,OAAQ;AAE3E,UAAM,OAAO;AACb,UAAO,SAAS,IAAK;AACrB,UAAO,SAAS,KAAM;AACtB,UAAO,SAAS,KAAM;AACtB;AAAA,EACF;AACF;AAEO,SAAS,gBAAgB,KAAa,OAAuB;AAClE,QAAM,MAAM,OAAO,YAAY,MAAM,MAAM;AAC3C,QAAM,KAAK,UAAU,QAAQ,CAAC;AAC9B,WAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,QAAI,CAAC,IAAI,MAAM,CAAC,IAAM,GAAG,KAAK,EAAE;AAAA,EAClC;AACA,SAAO;AACT;AAEO,SAAS,gBAAgB,KAAa,KAAqB;AAEhE,SAAO,gBAAgB,KAAK,GAAG;AACjC;;;ACtBA,SAAS,UAAU,KAAa,QAAwB;AACtD,SAAO,IAAI,YAAY,MAAM;AAC/B;AAEO,SAAS,sBAAsB,KAAa,KAAqB;AACtE,QAAM,WAAW,OAAO,KAAK,KAAK,MAAM;AACxC,QAAM,MAAM,gBAAgB,KAAK,QAAQ;AACzC,QAAM,WAAW,WAAW,GAAG;AAE/B,QAAM,SAAS,OAAO,MAAM,EAAE;AAC9B,SAAO,cAAc,kBAAkB,CAAC;AACxC,SAAO,cAAc,IAAI,WAAW,GAAG,CAAC;AACxC,SAAO,cAAc,GAAG,CAAC;AACzB,SAAO,cAAc,QAAQ,GAAG,EAAE;AAClC,SAAO,cAAc,aAAa,GAAG,EAAE;AACvC,SAAO,OAAO,OAAO,CAAC,QAAQ,GAAG,CAAC;AACpC;AAEO,SAAS,gBAAgB,QAMrB;AACT,QAAM,UAAU,OAAO,WAAW,OAAO,MAAM,CAAC;AAChD,QAAM,SAAS,OAAO,MAAM,EAAE;AAC9B,SAAO,cAAc,iBAAiB,CAAC;AACvC,SAAO,aAAa,OAAO,eAAe,GAAG,CAAC;AAC9C,SAAO,cAAc,GAAG,CAAC;AACzB,SAAO,cAAc,OAAO,YAAY,GAAG,EAAE;AAC7C,SAAO,cAAc,OAAO,aAAa,GAAG,EAAE;AAC9C,SAAO,eAAe,OAAO,gBAAgB,OAAO,GAAG,EAAE;AACzD,SAAO,cAAc,QAAQ,WAAW,GAAG,EAAE;AAC7C,SAAO,OAAO,OAAO,CAAC,QAAQ,OAAO,CAAC;AACxC;AAEO,SAAS,iBAAiB,QAA6E;AAC5G,QAAM,SAAS,OAAO,MAAM,EAAE;AAC9B,SAAO,cAAc,kBAAkB,CAAC;AACxC,SAAO,aAAa,OAAO,eAAe,GAAG,CAAC;AAC9C,SAAO,cAAc,GAAG,CAAC;AACzB,SAAO,cAAc,OAAO,aAAa,GAAG,EAAE;AAC9C,SAAO,cAAc,OAAO,QAAQ,WAAW,GAAG,EAAE;AACpD,SAAO,OAAO,OAAO,CAAC,QAAQ,OAAO,OAAO,CAAC;AAC/C;AAEO,SAAS,kBAAkB,KAA0B;AAC1D,MAAI,IAAI,SAAS,EAAG,OAAM,IAAI,MAAM,wBAAwB;AAC5D,QAAM,QAAQ,IAAI,aAAa,CAAC;AAEhC,MAAI,UAAU,kBAAkB;AAC9B,QAAI,IAAI,SAAS,GAAI,OAAM,IAAI,MAAM,kCAAkC;AACvE,UAAM,cAAc,IAAI,aAAa,CAAC;AACtC,UAAM,WAAW,IAAI,aAAa,CAAC;AACnC,QAAI,aAAa,EAAG,OAAM,IAAI,MAAM,4CAA4C,QAAQ,EAAE;AAC1F,UAAM,MAAM,IAAI,aAAa,EAAE;AAC/B,UAAM,WAAW,IAAI,aAAa,EAAE;AACpC,UAAM,MAAM,IAAI,SAAS,EAAE;AAC3B,QAAI,IAAI,SAAS,YAAa,OAAM,IAAI,MAAM,mCAAmC;AACjF,UAAM,WAAW,IAAI,SAAS,GAAG,WAAW;AAC5C,UAAM,SAAS,WAAW,QAAQ;AAClC,QAAK,aAAa,MAAQ,WAAW,EAAI,OAAM,IAAI,MAAM,8BAA8B;AACvF,UAAM,QAAQ,gBAAgB,KAAK,QAAQ;AAC3C,WAAO,EAAE,MAAM,aAAa,KAAK,KAAK,MAAM,SAAS,MAAM,EAAE;AAAA,EAC/D;AAEA,MAAI,UAAU,iBAAiB;AAC7B,QAAI,IAAI,SAAS,GAAI,OAAM,IAAI,MAAM,4BAA4B;AACjE,UAAM,eAAe,UAAU,KAAK,CAAC;AACrC,UAAM,WAAW,IAAI,aAAa,CAAC;AACnC,QAAI,aAAa,EAAG,OAAM,IAAI,MAAM,sCAAsC,QAAQ,EAAE;AACpF,UAAM,UAAU,IAAI,aAAa,EAAE;AACnC,UAAM,WAAW,IAAI,aAAa,EAAE;AACpC,UAAM,eAAe,IAAI,aAAa,EAAE;AACxC,UAAM,cAAc,IAAI,aAAa,EAAE;AACvC,UAAM,UAAU,IAAI,SAAS,EAAE;AAC/B,QAAI,QAAQ,SAAS,YAAa,OAAM,IAAI,MAAM,6BAA6B;AAC/E,WAAO,EAAE,MAAM,OAAO,cAAc,SAAS,UAAU,cAAc,SAAS,QAAQ,SAAS,GAAG,WAAW,EAAE;AAAA,EACjH;AAEA,MAAI,UAAU,kBAAkB;AAC9B,QAAI,IAAI,SAAS,GAAI,OAAM,IAAI,MAAM,6BAA6B;AAClE,UAAM,eAAe,UAAU,KAAK,CAAC;AACrC,UAAM,WAAW,IAAI,aAAa,CAAC;AACnC,QAAI,aAAa,EAAG,OAAM,IAAI,MAAM,uCAAuC,QAAQ,EAAE;AACrF,UAAM,WAAW,IAAI,aAAa,EAAE;AACpC,UAAM,cAAc,IAAI,aAAa,EAAE;AACvC,UAAM,UAAU,IAAI,SAAS,EAAE;AAC/B,QAAI,QAAQ,SAAS,YAAa,OAAM,IAAI,MAAM,8BAA8B;AAChF,WAAO,EAAE,MAAM,QAAQ,cAAc,UAAU,SAAS,QAAQ,SAAS,GAAG,WAAW,EAAE;AAAA,EAC3F;AAEA,QAAM,IAAI,MAAM,yBAAyB,MAAM,SAAS,EAAE,CAAC,EAAE;AAC/D;;;ACrGO,SAAS,YAAY,OAAuB;AACjD,SAAO,QAAQ,KAAK;AACtB;AAMO,SAAS,UAAU,QAAwC;AAChE,SAAO,YAAY,oBAAoB,OAAO,UAAU,sBAAsB;AAChF;AAQO,SAAS,UAAU,QAA8C;AACtE,QAAM,KAAK,OAAO,MAAM;AACxB,SAAO;AAAA,IACL,eACU,UAAU,OAAO,GAAG,CAAC,YACvB,UAAU,EAAE,CAAC;AAAA,EAEvB;AACF;AASA,SAAS,iBAAiB,KAAoC,MAAkC;AAC9F,QAAM,IAAI,IAAI,OAAO,IAAI,GAAG,kBAAkB,GAAG,KAAK,GAAG,EAAE,KAAK,IAAI;AACpE,MAAI,CAAC,EAAG,QAAO;AACf,QAAM,QAAQ,EAAE,CAAC,KAAK;AACtB,QAAM,KAAK,qBAAqB,KAAK,KAAK,IAAI,CAAC;AAC/C,QAAM,OAAO,yBAAyB,KAAK,KAAK,IAAI,CAAC;AACrD,MAAI,CAAC,MAAM,QAAQ,KAAM,QAAO;AAChC,QAAM,UAAU,OAAO,IAAI;AAC3B,MAAI,CAAC,OAAO,SAAS,OAAO,KAAK,WAAW,KAAK,UAAU,MAAO,QAAO;AACzE,SAAO,EAAE,IAAI,GAAG,KAAK,GAAG,MAAM,QAAQ;AACxC;AAEO,SAAS,WAAW,KAAsC;AAC/D,QAAM,IAAI,kCAAkC,KAAK,GAAG;AACpD,MAAI,CAAC,EAAG,QAAO;AACf,QAAM,OAAO,EAAE,CAAC,KAAK;AACrB,QAAM,MAAM,iBAAiB,OAAO,IAAI;AACxC,QAAM,QAAQ,iBAAiB,SAAS,IAAI;AAC5C,QAAM,MAAM,iBAAiB,OAAO,IAAI;AACxC,QAAM,IAAI,iBAAiB,KAAK,IAAI;AACpC,MAAI,CAAC,OAAO,CAAC,SAAS,CAAC,OAAO,CAAC,EAAG,QAAO;AACzC,SAAO,EAAE,GAAI,MAAM,EAAE,IAAI,IAAI,CAAC,GAAI,GAAI,QAAQ,EAAE,MAAM,IAAI,CAAC,GAAI,GAAI,MAAM,EAAE,IAAI,IAAI,CAAC,GAAI,GAAI,IAAI,EAAE,EAAE,IAAI,CAAC,EAAG;AAC9G;AAKO,SAAS,UAAU,QASf;AACT,QAAM,KAAK,OAAO,MAAM;AACxB,QAAM,QAAQ,OAAO,SAAS;AAC9B,QAAM,MAAM,OAAO,YAAY,OAAO,MAAM,OAAO,QAAQ,SAAS;AACpE,SAAO;AAAA,IACL,eACU,UAAU,OAAO,GAAG,CAAC,kBACjB,UAAU,OAAO,IAAI,EAAE,CAAC,cAAc,OAAO,IAAI,IAAI,2BACnD,UAAU,OAAO,MAAM,EAAE,CAAC,cAAc,OAAO,MAAM,IAAI,uBAC/D,OAAO,GAAG,gBACR,QAAQ,SAAS,OAAO,mBACvB,OAAO,MAAM,eAClB,UAAU,EAAE,CAAC,SACnB,MACF;AAAA,EACF;AACF;AAWO,SAAS,WAAW,KAAsC;AAC/D,QAAM,IAAI,kCAAkC,KAAK,GAAG;AACpD,MAAI,CAAC,EAAG,QAAO;AACf,QAAM,OAAO,EAAE,CAAC,KAAK;AACrB,QAAM,SAAS,uBAAuB,KAAK,IAAI,IAAI,CAAC;AACpD,MAAI,UAAU,KAAM,QAAO;AAC3B,QAAM,MAAM,OAAO,MAAM;AACzB,MAAI,CAAC,OAAO,SAAS,GAAG,EAAG,QAAO;AAClC,QAAM,SAAS,uBAAuB,KAAK,IAAI,IAAI,CAAC;AACpD,QAAM,MAAM,UAAU,OAAO,OAAO,MAAM,IAAI;AAE9C,QAAM,cAAc,CAAC,QAA6C;AAChE,UAAM,KAAK,IAAI,OAAO,IAAI,GAAG,kBAAkB,GAAG,KAAK,GAAG,EAAE,KAAK,IAAI;AACrE,QAAI,CAAC,GAAI,QAAO;AAChB,UAAM,QAAQ,GAAG,CAAC,KAAK;AACvB,UAAM,KAAK,qBAAqB,KAAK,KAAK,IAAI,CAAC;AAC/C,UAAM,OAAO,yBAAyB,KAAK,KAAK,IAAI,CAAC;AACrD,QAAI,CAAC,MAAM,QAAQ,KAAM,QAAO;AAChC,UAAM,UAAU,OAAO,IAAI;AAC3B,QAAI,CAAC,OAAO,SAAS,OAAO,KAAK,WAAW,KAAK,UAAU,MAAO,QAAO;AACzE,WAAO,EAAE,IAAI,GAAG,KAAK,GAAG,MAAM,QAAQ;AAAA,EACxC;AAEA,QAAM,YAAY,YAAY,KAAK;AACnC,QAAM,aAAa,YAAY,MAAM;AACrC,QAAM,cAAc,YAAY,OAAO;AACvC,QAAM,eAAe,YAAY,QAAQ;AAEzC,SAAO;AAAA,IACL;AAAA,IACA,GAAI,OAAO,QAAQ,OAAO,SAAS,GAAG,IAAI,EAAE,IAAI,IAAI,CAAC;AAAA,IACrD,GAAI,YAAY,EAAE,KAAK,UAAU,IAAI,CAAC;AAAA,IACtC,GAAI,aAAa,EAAE,MAAM,WAAW,IAAI,CAAC;AAAA,IACzC,GAAI,cAAc,EAAE,OAAO,YAAY,IAAI,CAAC;AAAA,IAC5C,GAAI,eAAe,EAAE,QAAQ,aAAa,IAAI,CAAC;AAAA,EACjD;AACF;AAKO,SAAS,YAAY,QAAuF;AACjH,QAAM,MAAM,OAAO,OAAO;AAC1B,SAAO;AAAA,IACL,iBACU,OAAO,GAAG,eACT,UAAU,OAAO,IAAI,CAAC,eACvB,GAAG,cACH,OAAO,GAAG,cACV,OAAO,GAAG;AAAA,EAEtB;AACF;AAEO,SAAS,UAAU,QAA4F;AAEpH,QAAM,KAAK,OAAO,MAAM;AACxB,SAAO;AAAA,IACL,eACU,UAAU,OAAO,GAAG,CAAC,oBACf,OAAO,UAAU,qBACvB,OAAO,GAAG,cACV,OAAO,GAAG,gCAEZ,UAAU,EAAE,CAAC;AAAA,EAEvB;AACF;AAEO,SAAS,WAAW,QAA8C;AACvE,SAAO,YAAY,gBAAgB,OAAO,GAAG,cAAc,OAAO,GAAG,iBAAiB;AACxF;AAEO,SAAS,UAAU,QAAuF;AAC/G,QAAM,OAAO,OAAO,QAAQ;AAC5B,SAAO;AAAA,IACL,eACU,OAAO,GAAG,eACT,UAAU,IAAI,CAAC,eAChB,OAAO,GAAG,cACV,OAAO,GAAG,cACV,OAAO,GAAG;AAAA,EAEtB;AACF;AAEO,SAAS,UAAU,QAA2E;AACnG,QAAM,OAAO,OAAO,QAAQ;AAC5B,QAAM,MAAM,OAAO,OAAO,OAAO,QAAQ,OAAO,GAAG,WAAW;AAC9D,SAAO;AAAA,IACL,YACE,MACA,SAAS,UAAU,IAAI,CAAC,eAChB,OAAO,GAAG,cACV,OAAO,GAAG;AAAA,EAEtB;AACF;AAUO,SAAS,WAAW,KAAsC;AAE/D,QAAM,IAAI,iCAAiC,KAAK,GAAG;AACnD,MAAI,CAAC,EAAG,QAAO;AACf,QAAM,OAAO,EAAE,CAAC,KAAK;AACrB,QAAM,MAAM,sBAAsB,KAAK,IAAI,IAAI,CAAC;AAChD,QAAM,MAAM,sBAAsB,KAAK,IAAI,IAAI,CAAC;AAChD,QAAM,MAAM,sBAAsB,KAAK,IAAI,IAAI,CAAC;AAChD,MAAI,OAAO,QAAQ,OAAO,QAAQ,OAAO,KAAM,QAAO;AAEtD,QAAM,MAAM,sBAAsB,KAAK,IAAI,IAAI,CAAC;AAEhD,QAAM,aAAa,6BAA6B,KAAK,IAAI,IAAI,CAAC;AAC9D,QAAM,MAAM,aAAa,sBAAsB,KAAK,UAAU,IAAI,CAAC,IAAI;AACvE,QAAM,KAAK,aAAa,oBAAoB,KAAK,UAAU,IAAI,CAAC,IAAI;AACpE,QAAM,MAAM,aAAa,sBAAsB,KAAK,UAAU,IAAI,CAAC,IAAI;AACvE,QAAM,QAAQ,OAAO,QAAQ,MAAM,QAAQ,OAAO,OAAO;AAAA,IACvD,GAAI,OAAO,OAAO,EAAE,KAAK,OAAO,GAAG,EAAE,IAAI,CAAC;AAAA,IAC1C,GAAI,MAAM,OAAO,EAAE,IAAI,OAAO,EAAE,EAAE,IAAI,CAAC;AAAA,IACvC,GAAI,OAAO,OAAO,EAAE,KAAK,OAAO,GAAG,EAAE,IAAI,CAAC;AAAA,EAC5C,IAAI;AAEJ,SAAO;AAAA,IACL,KAAK,OAAO,GAAG;AAAA,IACf,KAAK,OAAO,GAAG;AAAA,IACf,KAAK,OAAO,GAAG;AAAA,IACf,GAAI,OAAO,OAAO,EAAE,KAAK,OAAO,GAAG,EAAE,IAAI,CAAC;AAAA,IAC1C,GAAI,QAAQ,EAAE,MAAM,IAAI,CAAC;AAAA,EAC3B;AACF;AAIO,SAAS,YAAY,KAAuC;AACjE,QAAM,IAAI,iCAAiC,KAAK,GAAG;AACnD,MAAI,CAAC,EAAG,QAAO;AACf,QAAM,OAAO,EAAE,CAAC,KAAK;AACrB,QAAM,MAAM,sBAAsB,KAAK,IAAI,IAAI,CAAC;AAChD,MAAI,OAAO,KAAM,QAAO;AACxB,QAAM,OAAO,wBAAwB,KAAK,IAAI,IAAI,CAAC;AACnD,QAAM,MAAM,sBAAsB,KAAK,IAAI,IAAI,CAAC;AAChD,QAAM,MAAM,sBAAsB,KAAK,IAAI,IAAI,CAAC;AAChD,QAAM,MAAM,sBAAsB,KAAK,IAAI,IAAI,CAAC;AAChD,SAAO;AAAA,IACL,KAAK,OAAO,GAAG;AAAA,IACf,GAAI,QAAQ,OAAO,EAAE,KAAK,IAAI,CAAC;AAAA,IAC/B,GAAI,OAAO,OAAO,EAAE,KAAK,OAAO,GAAG,EAAE,IAAI,CAAC;AAAA,IAC1C,GAAI,OAAO,OAAO,EAAE,KAAK,OAAO,GAAG,EAAE,IAAI,CAAC;AAAA,IAC1C,GAAI,OAAO,OAAO,EAAE,KAAK,OAAO,GAAG,EAAE,IAAI,CAAC;AAAA,EAC5C;AACF;AAIO,SAAS,UAAU,KAAqC;AAC7D,QAAM,IAAI,6BAA6B,KAAK,GAAG;AAC/C,MAAI,CAAC,EAAG,QAAO;AACf,QAAM,OAAO,EAAE,CAAC,KAAK;AACrB,QAAM,MAAM,sBAAsB,KAAK,IAAI,IAAI,CAAC;AAChD,QAAM,MAAM,sBAAsB,KAAK,IAAI,IAAI,CAAC;AAChD,QAAM,MAAM,sBAAsB,KAAK,IAAI,IAAI,CAAC;AAChD,MAAI,OAAO,QAAQ,OAAO,QAAQ,OAAO,KAAM,QAAO;AACtD,QAAM,OAAO,wBAAwB,KAAK,IAAI,IAAI,CAAC;AACnD,SAAO;AAAA,IACL,KAAK,OAAO,GAAG;AAAA,IACf,KAAK,OAAO,GAAG;AAAA,IACf,KAAK,OAAO,GAAG;AAAA,IACf,GAAI,QAAQ,OAAO,EAAE,KAAK,IAAI,CAAC;AAAA,EACjC;AACF;AAIO,SAAS,aAAa,KAAwC;AACnE,QAAM,IAAI,mCAAmC,KAAK,GAAG;AACrD,MAAI,CAAC,EAAG,QAAO;AACf,QAAM,OAAO,EAAE,CAAC,KAAK;AACrB,QAAM,MAAM,sBAAsB,KAAK,IAAI,IAAI,CAAC;AAChD,QAAM,MAAM,sBAAsB,KAAK,IAAI,IAAI,CAAC;AAChD,MAAI,OAAO,QAAQ,OAAO,KAAM,QAAO;AACvC,SAAO,EAAE,KAAK,OAAO,GAAG,GAAG,KAAK,OAAO,GAAG,EAAE;AAC9C;AAIO,SAAS,WAAW,KAAsC;AAC/D,QAAM,IAAI,+BAA+B,KAAK,GAAG;AACjD,MAAI,CAAC,EAAG,QAAO;AACf,QAAM,OAAO,EAAE,CAAC,KAAK;AACrB,QAAM,MAAM,sBAAsB,KAAK,IAAI,IAAI,CAAC;AAChD,QAAM,MAAM,sBAAsB,KAAK,IAAI,IAAI,CAAC;AAChD,MAAI,OAAO,QAAQ,OAAO,KAAM,QAAO;AACvC,SAAO,EAAE,KAAK,OAAO,GAAG,GAAG,KAAK,OAAO,GAAG,EAAE;AAC9C;;;ALlSA,IAAM,aAAN,MAAiB;AAAA,EACP,gBAA0B,CAAC;AAAA,EAC3B,kBAAiC;AAAA,EACjC,eAAuB;AAAA,EACvB,kBAAiC;AAAA,EAEzC,WAAmB;AACjB,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,OAAa;AACX,UAAM,MAAM,YAAY,IAAI;AAC5B,QAAI,KAAK,oBAAoB,MAAM;AACjC,YAAM,QAAQ,MAAM,KAAK,mBAAmB;AAC5C,WAAK,cAAc,KAAK,IAAI;AAAA,IAC9B;AACA,SAAK,kBAAkB;AAEvB,QAAI,KAAK,oBAAoB,MAAM;AACjC,UAAI,MAAM,KAAK,kBAAkB,KAAM;AACrC,aAAK,kBAAkB;AACvB,cAAM,QAAQ,KAAK,cAAc;AACjC,YAAI,QAAQ,GAAG;AACb,gBAAM,MAAM,KAAK,cAAc,OAAO,CAAC,GAAG,MAAM,IAAI,GAAG,CAAC;AACxD,eAAK,eAAe,KAAK,MAAM,MAAM,KAAK;AAAA,QAC5C,OAAO;AACL,eAAK,eAAe;AAAA,QACtB;AACA,aAAK,gBAAgB,CAAC;AAAA,MACxB;AAAA,IACF,OAAO;AACL,WAAK,kBAAkB;AACvB,WAAK,eAAe;AAAA,IACtB;AAAA,EACF;AACF;AAiDA,SAAS,MAAM,IAA2B;AACxC,SAAO,IAAI,QAAQ,CAAC,MAAM,WAAW,GAAG,EAAE,CAAC;AAC7C;AAEA,IAAM,sBAAsB;AAAA,EAC1B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,IAAM,kBAAkB;AACxB,IAAM,kBAAkB;AACxB,IAAM,qBAAqB;AAOpB,IAAM,cAAN,cAA0B,aAK9B;AAAA,EACgB;AAAA,EACT;AAAA,EACA;AAAA,EACA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EAEA,eAAe;AAAA,EACf,cAAc;AAAA,EACd,WAAW,oBAAI,IAAoB;AAAA,EACnC,OAAO,oBAAI,IAAuB;AAAA,EAElC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEA,aAAa;AAAA,EACb;AAAA,EAEA,eAAe;AAAA,EACf,aAAa,IAAI,WAAW;AAAA;AAAA;AAAA;AAAA,EAK5B,gBAAwB,OAAO,MAAM,CAAC;AAAA,EACtC,eAAe;AAAA;AAAA;AAAA;AAAA;AAAA,EAMf,cAAwB,CAAC;AAAA,EACzB,oBAAoB;AAAA,EACpB,iBAAiB;AAAA,EAEjB,kBAA0B;AAGhC,QAAI,KAAK,gBAAgB,KAAM,QAAO,KAAK;AAC3C,YAAQ,KAAK,MAAM,KAAK,OAAO,IAAI,GAAG,IAAI,OAAO;AAAA,EACnD;AAAA,EAEA,YAAY,SAA6B;AACvC,UAAM;AACN,SAAK,OAAO;AACZ,SAAK,MAAM;AAAA,EACb;AAAA;AAAA,EAGA,cAAuB;AACrB,WAAO,CAAC,CAAC,KAAK,QAAQ,CAAC,CAAC,KAAK,UAAU,KAAK,YAAY;AAAA,EAC1D;AAAA,EAEA,MAAM,UAAyB;AAC7B,QAAI,KAAK,KAAM;AACf,UAAM,OAAO,MAAM,aAAa,MAAM;AACtC,SAAK,OAAO;AAEZ,QAAI;AACF,WAAK,kBAAkB,IAAI,OAAO,IAAI;AACtC,WAAK,kBAAkB,IAAI,OAAO,IAAI;AAAA,IACxC,SAAS,GAAG;AAAA,IAEZ;AAEA,SAAK,GAAG,WAAW,CAAC,KAAK,UAAU;AACjC,UAAI;AACF,cAAM,IAAI,kBAAkB,GAAG;AAC/B,aAAK,aAAa,GAAG,MAAM,SAAS,MAAM,IAAI;AAAA,MAChD,SAAS,GAAG;AACV,aAAK,KAAK,SAAS,aAAa,QAAQ,IAAI,IAAI,MAAM,OAAO,CAAC,CAAC,CAAC;AAAA,MAClE;AAAA,IACF,CAAC;AACD,SAAK,GAAG,SAAS,CAAC,MAAM,KAAK,KAAK,SAAS,CAAC,CAAC;AAC7C,SAAK,GAAG,SAAS,MAAM,KAAK,KAAK,OAAO,CAAC;AAEzC,UAAM,IAAI,QAAc,CAAC,YAAY,KAAK,KAAK,GAAG,WAAW,MAAM,QAAQ,CAAC,CAAC;AAE7E,QAAI,KAAK,KAAK,SAAS,UAAU;AAC/B,WAAK,SAAS,EAAE,MAAM,KAAK,KAAK,MAAM,MAAM,KAAK,KAAK,KAAK;AAC3D,WAAK,WAAW,KAAK,KAAK;AAC1B,WAAK,WAAW,KAAK,KAAK;AAAA,IAC5B,OAAO;AACL,YAAM,KAAK,aAAa,IAAI;AAAA,IAC9B;AAEA,SAAK,YAAY;AAAA,EACnB;AAAA,EAEA,MAAc,aAAa,MAAmC;AAC5D,QAAI,KAAK,KAAK,SAAS,MAAO,OAAM,IAAI,MAAM,gDAAgD;AAE9F,UAAM,SAA+B,KAAK,KAAK,mBAAmB;AAElE,QAAI,WAAW,qBAAqB,WAAW,gBAAgB;AAC7D,YAAM,KAAK,kBAAkB,MAAM,EAAE,WAAW,OAAO,CAAC;AACxD;AAAA,IACF;AAEA,QAAI,WAAW,YAAY,WAAW,SAAS,WAAW,SAAS;AACjE,YAAM,IAAI,MAAM,uCAAuC,OAAO,MAAM,CAAC,EAAE;AAAA,IACzE;AAEA,UAAM,KAAK,gBAAgB,MAAM,MAAM;AAAA,EACzC;AAAA,EAEQ,eAAuB;AAC7B,UAAM,SAAS,kBAAkB;AACjC,eAAW,QAAQ,OAAO,KAAK,MAAM,GAAG;AACtC,YAAM,UAAU,OAAO,IAAI;AAC3B,UAAI,CAAC,QAAS;AACd,iBAAW,QAAQ,SAAS;AAC1B,YAAI,KAAK,WAAW,UAAU,CAAC,KAAK,UAAU;AAC5C,iBAAO,KAAK;AAAA,QACd;AAAA,MACF;AAAA,IACF;AACA,UAAM,IAAI,MAAM,iEAAiE;AAAA,EACnF;AAAA,EAEA,MAAc,gBAAgB,MAAoB,QAAgD;AAChG,QAAI,KAAK,KAAK,SAAS,MAAO,OAAM,IAAI,MAAM,mDAAmD;AAEjG,UAAM,OAAO,KAAK,QAAQ;AAC1B,UAAM,YAAY,OAAO,SAAS,WAAW,IAAK,KAAqB;AACvE,UAAM,MAAO,KAAK,MAAM,KAAK,OAAO,IAAI,UAAU,IAAI,KAAM;AAE5D,UAAM,SAAS,MAAM,KAAK,aAAa,MAAM,KAAK,KAAK,GAAG;AAC1D,UAAM,MAAM,MAAM,KAAK,YAAY,MAAM;AAAA,MACvC,KAAK,KAAK,KAAK;AAAA,MACf;AAAA,MACA;AAAA,MACA,OAAO,OAAO;AAAA,MACd,KAAK,OAAO;AAAA,IACd,CAAC;AAED,UAAM,OAAkC,WAAW,WAAW,UAAU;AACxE,UAAM,YAAY,MAAM,KAAK,WAAW,MAAM,EAAE,GAAG,KAAK,KAAK,KAAK,KAAK,KAAK,IAAI,GAAG,MAAM;AAGzF,UAAM,KAAK,WAAW,MAAM,IAAI,KAAK;AAAA,MACnC,KAAK,IAAI;AAAA,MACT;AAAA,MACA;AAAA,MACA,KAAK,UAAU;AAAA,IACjB,CAAC;AAED,SAAK,WAAW;AAChB,SAAK,WAAW,UAAU;AAC1B,SAAK,MAAM,IAAI;AACf,SAAK,eAAe,UAAU;AAC9B,SAAK,SAAS,EAAE,MAAM,UAAU,OAAO,MAAM,UAAU,MAAM;AAAA,EAC/D;AAAA,EAEA,MAAc,aAAa,MAAoB,KAAsD;AACnG,UAAM,WAAqB,CAAC;AAC5B,eAAW,QAAQ,qBAAqB;AACtC,UAAI;AACF,cAAM,UAAU,MAAM,IAAI,OAAO,MAAM,EAAE,QAAQ,GAAG,KAAK,KAAK,CAAC;AAC/D,mBAAW,KAAK,SAAS;AACvB,cAAI,EAAE,WAAW,CAAC,SAAS,SAAS,EAAE,OAAO,EAAG,UAAS,KAAK,EAAE,OAAO;AAAA,QACzE;AAAA,MACF,QAAQ;AAAA,MAER;AAAA,IACF;AACA,QAAI,SAAS,WAAW,GAAG;AACzB,YAAM,IAAI,MAAM,8DAA8D;AAAA,IAChF;AAEA,UAAM,QAAQ,KAAK,IAAI;AACvB,QAAI;AACJ,eAAW,MAAM,UAAU;AACzB,YAAM,YAAY,mBAAmB,KAAK,IAAI,IAAI;AAClD,UAAI,aAAa,EAAG;AACpB,UAAI;AACF,cAAM,MAAM,MAAM,KAAK,gBAAgB,MAAM,KAAK,EAAE,MAAM,IAAI,MAAM,gBAAgB,GAAG,KAAK,IAAI,WAAW,GAAK,CAAC;AACjH,eAAO;AAAA,MACT,SAAS,GAAG;AACV,kBAAU,aAAa,QAAQ,IAAI,IAAI,MAAM,OAAO,CAAC,CAAC;AAAA,MACxD;AAAA,IACF;AACA,UAAM,WAAW,IAAI,MAAM,uBAAuB;AAAA,EACpD;AAAA,EAEA,MAAc,gBACZ,MACA,KACA,MACA,WACyC;AACzC,UAAM,OAAO,KAAK,MAAM,KAAK,OAAO,IAAI,UAAU,IAAI,OAAO;AAC7D,UAAM,MAAM,UAAU,EAAE,IAAI,CAAC;AAC7B,UAAM,MAAM,sBAAsB,KAAK,GAAG;AAE1C,WAAO,MAAM,IAAI,QAAQ,CAAC,SAAS,WAAW;AAC5C,YAAM,WAAW,WAAW,MAAM;AAChC,gBAAQ;AACR,eAAO,IAAI,MAAM,2BAA2B,KAAK,IAAI,IAAI,KAAK,IAAI,GAAG,CAAC;AAAA,MACxE,GAAG,SAAS;AAEZ,YAAM,QAAQ,CAAC,QAAgB;AAC7B,YAAI;AACF,gBAAM,IAAI,kBAAkB,GAAG;AAC/B,cAAI,EAAE,SAAS,YAAa;AAC5B,cAAK,EAAE,QAAQ,MAAQ,QAAQ,EAAI;AACnC,gBAAM,KAAK,WAAW,EAAE,GAAG;AAC3B,cAAI,CAAC,IAAI,OAAO,CAAC,IAAI,MAAO;AAC5B,kBAAQ;AACR,kBAAQ,EAAE,KAAK,GAAG,KAAK,OAAO,GAAG,MAAM,CAAC;AAAA,QAC1C,QAAQ;AAAA,QAER;AAAA,MACF;AAEA,YAAM,OAAO,MAAM;AACjB,YAAI;AACF,eAAK,KAAK,KAAK,KAAK,MAAM,KAAK,IAAI;AAAA,QACrC,QAAQ;AAAA,QAER;AAAA,MACF;AAEA,YAAM,aAAa,gBAAgB,MAAM,kBAAkB;AAC3D,YAAM,UAAU,MAAM;AACpB,qBAAa,QAAQ;AACrB,sBAAc,UAAU;AACxB,aAAK,IAAI,WAAW,KAAK;AAAA,MAC3B;AAEA,WAAK,GAAG,WAAW,KAAK;AACxB,WAAK;AAAA,IACP,CAAC;AAAA,EACH;AAAA,EAEA,MAAc,YACZ,MACA,QAC4F;AAC5F,UAAM,UAAU,KAAK,aAAa;AAClC,UAAM,OAAO,KAAK,MAAM,KAAK,OAAO,IAAI,UAAU,IAAI,OAAO;AAC7D,UAAM,MAAM,UAAU;AAAA,MACpB,KAAK,OAAO;AAAA,MACZ,KAAK,EAAE,IAAI,SAAS,MAAM,OAAO,UAAU;AAAA,MAC3C,OAAO,OAAO;AAAA,MACd,KAAK,OAAO;AAAA,MACZ,QAAQ;AAAA,MACR,UAAU;AAAA,IACZ,CAAC;AACD,UAAM,MAAM,sBAAsB,KAAK,GAAG;AAC1C,UAAM,UAAoB,EAAE,MAAM,OAAO,IAAI,IAAI,MAAM,OAAO,IAAI,KAAK;AAEvE,UAAM,SAAS,MAAM,IAAI,QAA4E,CAAC,SAAS,WAAW;AACxH,YAAM,UAAU,WAAW,MAAM;AAC/B,gBAAQ;AACR,eAAO,IAAI,MAAM,8BAA8B,eAAe,IAAI,CAAC;AAAA,MACrE,GAAG,eAAe;AAElB,YAAM,QAAQ,CAAC,KAAa,UAA4B;AACtD,YAAI;AACF,gBAAM,IAAI,kBAAkB,GAAG;AAC/B,cAAI,EAAE,SAAS,YAAa;AAC5B,cAAK,EAAE,QAAQ,MAAQ,QAAQ,EAAI;AAEnC,gBAAM,IAAI,WAAW,EAAE,GAAG;AAC1B,cAAI,CAAC,EAAG;AACR,cAAI,EAAE,QAAQ,MAAM,EAAE,QAAQ,IAAI;AAChC,oBAAQ;AACR,mBAAO,IAAI,MAAM,8BAA8B,EAAE,GAAG,GAAG,CAAC;AACxD;AAAA,UACF;AACA,cAAI,EAAE,OAAO,KAAM;AAEnB,gBAAM,YAAY,CAAC,WAA0C;AAC3D,gBAAI,CAAC,QAAQ,GAAI,QAAO;AACxB,kBAAM,OAAO,OAAO,SAAS,IAAI,MAAM,OAAO,OAAO;AACrD,gBAAI,CAAC,KAAM,QAAO;AAClB,mBAAO,EAAE,MAAM,OAAO,IAAI,KAAK;AAAA,UACjC;AAEA,gBAAM,MAAM,UAAU,EAAE,GAAG;AAC3B,gBAAM,OAAO,UAAU,EAAE,IAAI;AAC7B,gBAAM,QAAQ,UAAU,EAAE,KAAK;AAC/B,cAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAO;AAE7B,kBAAQ;AACR,kBAAQ;AAAA,YACN,KAAK,EAAE;AAAA,YACP,GAAI,MAAM,EAAE,IAAI,IAAI,CAAC;AAAA,YACrB,GAAI,OAAO,EAAE,KAAK,IAAI,CAAC;AAAA,YACvB,GAAI,QAAQ,EAAE,MAAM,IAAI,CAAC;AAAA,UAC3B,CAAC;AAAA,QACH,QAAQ;AAAA,QAER;AAAA,MACF;AAEA,YAAM,OAAO,MAAM;AACjB,YAAI;AACF,eAAK,KAAK,KAAK,QAAQ,MAAM,QAAQ,IAAI;AAAA,QAC3C,QAAQ;AAAA,QAER;AAAA,MACF;AAEA,YAAM,aAAa,gBAAgB,MAAM,kBAAkB;AAC3D,YAAM,UAAU,MAAM;AACpB,qBAAa,OAAO;AACpB,sBAAc,UAAU;AACxB,aAAK,IAAI,WAAW,KAAK;AAAA,MAC3B;AAEA,WAAK,GAAG,WAAW,KAAK;AACxB,WAAK;AAAA,IACP,CAAC;AAED,WAAO;AAAA,MACL,KAAK;AAAA,MACL,KAAK,OAAO;AAAA,MACZ,GAAI,OAAO,MAAM,EAAE,KAAK,OAAO,IAAI,IAAI,CAAC;AAAA,MACxC,GAAI,OAAO,OAAO,EAAE,MAAM,OAAO,KAAK,IAAI,CAAC;AAAA,MAC3C,GAAI,OAAO,QAAQ,EAAE,OAAO,OAAO,MAAM,IAAI,CAAC;AAAA,IAChD;AAAA,EACF;AAAA,EAEA,MAAc,WACZ,MACA,KACA,QACqE;AACrE,QAAI,WAAW,UAAU;AACvB,YAAM,QAAoF,CAAC;AAC3F,UAAI,IAAI,IAAK,OAAM,KAAK,KAAK,mBAAmB,MAAM,EAAE,KAAK,IAAI,KAAK,KAAK,IAAI,KAAK,MAAM,SAAS,MAAM,IAAI,IAAI,CAAC,CAAC;AACnH,UAAI,IAAI,KAAM,OAAM,KAAK,KAAK,mBAAmB,MAAM,EAAE,KAAK,IAAI,KAAK,KAAK,IAAI,KAAK,MAAM,SAAS,YAAY,IAAI,KAAK,CAAC,CAAC;AAC3H,UAAI,MAAM,WAAW,EAAG,OAAM,IAAI,MAAM,kDAAkD;AAC1F,aAAO,MAAM,QAAQ,KAAK,KAAK;AAAA,IACjC;AAEA,QAAI,WAAW,OAAO;AACpB,UAAI,CAAC,IAAI,KAAM,OAAM,IAAI,MAAM,yCAAyC;AACxE,aAAO,MAAM,KAAK,mBAAmB,MAAM,EAAE,KAAK,IAAI,KAAK,KAAK,IAAI,KAAK,MAAM,OAAO,YAAY,IAAI,KAAK,CAAC;AAAA,IAC9G;AAGA,QAAI,CAAC,IAAI,MAAO,OAAM,IAAI,MAAM,4CAA4C;AAC5E,WAAO,MAAM,KAAK,mBAAmB,MAAM,EAAE,KAAK,IAAI,KAAK,KAAK,IAAI,KAAK,MAAM,SAAS,MAAM,IAAI,OAAO,kBAAkB,KAAK,CAAC;AAAA,EACnI;AAAA,EAEA,MAAc,mBACZ,MACA,QACqE;AACrE,UAAM,OAAO,KAAK,MAAM,KAAK,OAAO,IAAI,UAAU,IAAI,OAAO;AAC7D,UAAM,MAAM,UAAU,EAAE,KAAK,OAAO,KAAK,MAAM,OAAO,MAAM,KAAK,OAAO,KAAK,KAAK,KAAK,IAAI,CAAC;AAC5F,UAAM,MAAM,sBAAsB,KAAK,GAAG;AAE1C,WAAO,MAAM,IAAI,QAAQ,CAAC,SAAS,WAAW;AAC5C,YAAM,UAAU,WAAW,MAAM;AAC/B,gBAAQ;AACR,eAAO,IAAI,MAAM,yBAAyB,OAAO,IAAI,mBAAmB,eAAe,IAAI,CAAC;AAAA,MAC9F,GAAG,eAAe;AAElB,YAAM,QAAQ,CAAC,KAAa,UAA4B;AACtD,YAAI;AACF,gBAAM,IAAI,kBAAkB,GAAG;AAC/B,cAAI,EAAE,SAAS,YAAa;AAC5B,gBAAM,MAAM,YAAY,EAAE,GAAG;AAC7B,cAAI,CAAC,IAAK;AACV,cAAI,IAAI,QAAQ,OAAO,IAAK;AAC5B,cAAI,IAAI,QAAQ,OAAO,IAAK;AAC5B,cAAI,OAAO,qBAAqB,IAAI,QAAQ,QAAQ,OAAO,KAAM;AACjE,cAAI,IAAI,OAAO,KAAM;AACrB,kBAAQ;AACR,kBAAQ,EAAE,KAAK,IAAI,KAAK,OAAO,MAAM,SAAS,OAAO,MAAM,MAAM,IAAI,CAAC;AAAA,QACxE,QAAQ;AAAA,QAER;AAAA,MACF;AAEA,YAAM,OAAO,MAAM;AACjB,YAAI;AACF,eAAK,KAAK,KAAK,OAAO,KAAK,MAAM,OAAO,KAAK,IAAI;AAAA,QACnD,QAAQ;AAAA,QAER;AAAA,MACF;AAEA,YAAM,aAAa,gBAAgB,MAAM,kBAAkB;AAC3D,YAAM,UAAU,MAAM;AACpB,qBAAa,OAAO;AACpB,sBAAc,UAAU;AACxB,aAAK,IAAI,WAAW,KAAK;AAAA,MAC3B;AAEA,WAAK,GAAG,WAAW,KAAK;AACxB,WAAK;AAAA,IACP,CAAC;AAAA,EACH;AAAA,EAEA,MAAc,mBACZ,MACA,QACqE;AACrE,WAAO,MAAM,IAAI,QAAQ,CAAC,SAAS,WAAW;AAC5C,YAAM,UAAU,WAAW,MAAM;AAC/B,gBAAQ;AACR,eAAO,IAAI,MAAM,yBAAyB,OAAO,IAAI,mBAAmB,eAAe,IAAI,CAAC;AAAA,MAC9F,GAAG,eAAe;AAElB,UAAI,QAA+B;AACnC,UAAI;AACJ,UAAI;AACJ,UAAI;AACJ,UAAI;AACJ,UAAI;AAEJ,YAAM,QAAQ,MAAM;AAClB,YAAI,UAAU,cAAc,OAAO,QAAQ,OAAO,QAAQ,SAAS,QAAQ,SAAS,KAAM;AAC1F,YAAI;AACF,gBAAM,OAAO,UAAU,EAAE,KAAK,OAAO,KAAK,MAAM,OAAO,MAAM,KAAK,OAAO,KAAK,KAAK,KAAK,KAAK,IAAI,CAAC;AAClG,gBAAM,OAAO,sBAAsB,KAAK,IAAI;AAC5C,eAAK,KAAK,MAAM,OAAO,KAAK;AAAA,QAC9B,QAAQ;AAAA,QAER;AAAA,MACF;AAEA,YAAM,QAAQ,CAAC,KAAa,SAA2B;AACrD,YAAI;AACF,gBAAM,IAAI,kBAAkB,GAAG;AAC/B,cAAI,EAAE,SAAS,YAAa;AAE5B,cAAI,UAAU,UAAU;AACtB,kBAAM,KAAK,UAAU,EAAE,GAAG;AAC1B,gBAAI,CAAC,GAAI;AACT,gBAAI,GAAG,QAAQ,OAAO,IAAK;AAC3B,gBAAI,GAAG,QAAQ,OAAO,IAAK;AAC3B,iBAAK,GAAG,QAAQ,QAAQ,OAAO,KAAM;AACrC,gBAAI,KAAK,YAAY,OAAO,WAAW,KAAM;AAC7C,gBAAI,OAAO,WAAW,QAAQ,KAAK,SAAS,OAAO,WAAW,MAAM;AAAA,YAEpE;AAEA,kBAAM,GAAG;AACT,kBAAM,EAAE;AACR,oBAAQ,KAAK;AACb,oBAAQ,KAAK;AACb,oBAAQ;AAER,kBAAM;AACN,sBAAU,gBAAgB,OAAO,kBAAkB;AACnD;AAAA,UACF;AAEA,gBAAM,MAAM,YAAY,EAAE,GAAG;AAC7B,cAAI,CAAC,IAAK;AACV,cAAI,IAAI,QAAQ,OAAO,IAAK;AAC5B,cAAI,IAAI,QAAQ,OAAO,IAAK;AAC5B,cAAI,IAAI,OAAO,QAAQ,OAAO,KAAM;AACpC,cAAI,IAAI,QAAQ,IAAK;AACrB,eAAK,IAAI,QAAQ,QAAQ,OAAO,KAAM;AAEtC,kBAAQ;AACR,kBAAQ,EAAE,KAAK,OAAO,KAAK,SAAS,OAAO,KAAK,MAAM,KAAK,OAAO,EAAE,CAAC;AAAA,QACvE,QAAQ;AAAA,QAER;AAAA,MACF;AAEA,YAAM,UAAU,MAAM;AACpB,qBAAa,OAAO;AACpB,YAAI,QAAS,eAAc,OAAO;AAClC,aAAK,IAAI,WAAW,KAAK;AAAA,MAC3B;AAEA,WAAK,GAAG,WAAW,KAAK;AAAA,IAC1B,CAAC;AAAA,EACH;AAAA,EAEA,MAAc,WAAW,MAAoB,KAAe,QAAgF;AAC1I,UAAM,MAAM,YAAY,EAAE,KAAK,OAAO,KAAK,MAAM,OAAO,MAAM,KAAK,GAAG,KAAK,OAAO,KAAK,KAAK,OAAO,IAAI,CAAC;AACxG,aAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AAC1B,YAAM,OAAO,KAAK,MAAM,KAAK,OAAO,IAAI,UAAU,IAAI,OAAO;AAC7D,YAAM,MAAM,sBAAsB,KAAK,GAAG;AAC1C,UAAI;AACF,aAAK,KAAK,KAAK,IAAI,MAAM,IAAI,IAAI;AAAA,MACnC,QAAQ;AAAA,MAER;AACA,YAAM,MAAM,kBAAkB;AAAA,IAChC;AAAA,EACF;AAAA,EAEA,MAAc,kBAAkB,MAAoB,MAA0E;AAC5H,QAAI,KAAK,KAAK,SAAS,MAAO,OAAM,IAAI,MAAM,qDAAqD;AAKnG,UAAM,QAAQ,CAAC,gCAAgC,8BAA8B;AAC7E,UAAM,gBAAgB;AACtB,UAAM,cAAc,KAAK,KAAK,cAAc,IAAI,KAAK;AACrD,UAAM,YAAY,MAAM,aAAa;AACrC,UAAM,sBAAsB,cAAc,kBAAkB,aAAa,MAAQ;AACjF,UAAM,mBAAmB;AACzB,UAAM,gBAAgB;AAEtB,UAAM,UAAU,KAAK,IAAI;AAEzB,SAAK,aAAa,IAAI;AAEtB,UAAM,OAAO,KAAK,QAAQ;AAC1B,UAAM,YAAY,OAAO,SAAS,WAAW,IAAK,KAAqB;AACvE,UAAM,MAAO,KAAK,MAAM,KAAK,OAAO,IAAI,UAAU,IAAI,KAAM;AAI5D,UAAM,MAAM,UAAU,EAAE,KAAK,KAAK,KAAK,KAAK,YAAY,WAAW,KAAK,KAAK,KAAK,IAAI,CAAC;AAEvF,UAAM,QAAQ,MAAM,IAAI,QAAgG,CAAC,SAAS,WAAW;AAC3I,YAAM,UAAU,WAAW,MAAM;AAC/B,YAAI,WAAY,eAAc,UAAU;AACxC,aAAK,IAAI,WAAW,KAAK;AACzB,eAAO,IAAI,MAAM,iCAAiC,gBAAgB,4CAA4C,CAAC;AAAA,MACjH,GAAG,gBAAgB;AAEnB,UAAI;AACJ,UAAI,aAAa;AACjB,UAAI;AACJ,UAAI;AACJ,UAAI;AACJ,UAAI,QAAQ;AACZ,UAAI,OAAO;AACX,UAAI,QAAQ;AACZ,UAAI;AAEJ,YAAM,gBAAgB,CAAC,WAAmB;AACxC,YAAI,CAAC,WAAY;AAGjB,YAAI,cAAe;AAEnB,wBAAgB,WAAW,MAAM;AAC/B,gBAAM,IAAI;AACV,gBAAM,MAAM;AACZ,gBAAM,MAAM;AACZ,cAAI,CAAC,EAAG;AACR,eAAK,IAAI,WAAW,KAAK;AACzB,uBAAa,OAAO;AACpB,cAAI,WAAY,eAAc,UAAU;AACxC,eAAK,KAAK,SAAS,sBAAsB,EAAE,QAAQ,GAAI,OAAO,OAAO,EAAE,IAAI,IAAI,CAAC,GAAI,GAAG,EAAE,CAAC;AAC1F,kBAAQ,EAAE,GAAG,GAAG,GAAI,OAAO,OAAO,EAAE,IAAI,IAAI,CAAC,GAAI,GAAI,OAAO,OAAO,EAAE,IAAI,IAAI,CAAC,EAAG,CAAC;AAAA,QACpF,GAAG,GAAG;AAAA,MACR;AAEA,YAAM,QAAQ,CAAC,OAAe,UAAkB;AAC9C,YAAI,MAAO;AACX,YAAI,CAAC,WAAY;AAGjB,YAAI,iBAAiB,KAAM;AAC3B,YAAI;AACF,gBAAM,OAAO,KAAK,MAAM,KAAK,OAAO,IAAI,UAAU,IAAI,OAAO;AAC7D,gBAAM,OAAO,UAAU,EAAE,GAAI,iBAAiB,OAAO,EAAE,KAAK,cAAc,IAAI,CAAC,GAAI,KAAK,WAAW,KAAK,KAAK,KAAK,KAAK,MAAM,QAAQ,CAAC;AACtI,gBAAM,OAAO,sBAAsB,KAAK,IAAI;AAC5C,eAAK,KAAK,MAAM,OAAO,KAAK;AAC5B,kBAAQ;AACR,eAAK,KAAK,SAAS,oBAAoB,EAAE,KAAK,eAAe,KAAK,WAAW,KAAK,KAAK,WAAW,KAAK,OAAO,MAAM,CAAC;AAAA,QACvH,SAAS,GAAG;AACV,eAAK,KAAK,SAAS,0BAA0B,CAAC;AAAA,QAChD;AAAA,MACF;AAEA,YAAM,QAAQ,CAAC,KAAa,OAAe,OAAe,OAAiE;AACzH,YAAI,MAAO;AACX,YAAI;AACF,gBAAM,OAAO,UAAU,EAAE,KAAK,GAAG,KAAK,MAAM,GAAG,QAAQ,SAAS,KAAK,GAAG,KAAK,KAAK,GAAG,KAAK,KAAK,KAAK,IAAI,CAAC;AACzG,gBAAM,OAAO,sBAAsB,KAAK,IAAI;AAC5C,eAAK,KAAK,MAAM,OAAO,KAAK;AAC5B,kBAAQ;AACR,eAAK,KAAK,SAAS,oBAAoB,EAAE,KAAK,GAAG,KAAK,KAAK,GAAG,KAAK,KAAK,GAAG,KAAK,OAAO,MAAM,CAAC;AAAA,QAChG,SAAS,GAAG;AACV,eAAK,KAAK,SAAS,0BAA0B,CAAC;AAAA,QAChD;AAAA,MACF;AAEA,YAAM,QAAQ,CAAC,KAAa,UAA4B;AACtD,YAAI;AACF,gBAAM,IAAI,kBAAkB,GAAG;AAC/B,cAAI,EAAE,SAAS,YAAa;AAG5B,eAAK,KAAK,SAAS,gBAAgB,EAAE,KAAK,EAAE,KAAK,OAAO,MAAM,SAAS,OAAO,MAAM,MAAM,YAAY,EAAE,IAAI,MAAM,GAAG,GAAG,EAAE,CAAC;AAI3H,gBAAM,MAAM,YAAY,EAAE,GAAG;AAC7B,cAAI,KAAK;AACP,4BAAgB,IAAI;AACpB,gBAAI,CAAC,cAAc,IAAI,OAAO,QAAQ,IAAI,OAAO,MAAM;AACrD,2BAAa,EAAE,KAAK,IAAI,KAAK,KAAK,IAAI,KAAK,OAAO,MAAM,SAAS,OAAO,MAAM,KAAK;AAAA,YACrF;AAEA,gBAAI,YAAY;AACd,oBAAM,MAAM,SAAS,MAAM,IAAI;AAC/B,4BAAc,KAAK;AAAA,YACrB;AAAA,UACF;AAGA,gBAAM,KAAK,UAAU,EAAE,GAAG;AAC1B,cAAI,IAAI;AACN,mBAAO;AACP,4BAAgB,GAAG;AACnB,gBAAI,CAAC,YAAY;AACf,2BAAa,EAAE,KAAK,GAAG,KAAK,KAAK,GAAG,KAAK,OAAO,MAAM,SAAS,OAAO,MAAM,KAAK;AAAA,YACnF;AACA,iBAAK,KAAK,SAAS,kBAAkB,EAAE,KAAK,GAAG,KAAK,KAAK,GAAG,KAAK,KAAK,GAAG,KAAK,OAAO,MAAM,SAAS,OAAO,MAAM,KAAK,CAAC;AACvH,kBAAM,EAAE,KAAK,MAAM,SAAS,MAAM,MAAM,EAAE;AAE1C,0BAAc,KAAK;AACnB;AAAA,UACF;AAEA,gBAAM,SAAS,WAAW,EAAE,GAAG;AAC/B,cAAI,CAAC,OAAQ;AACb,cAAI,OAAO,QAAQ,EAAG;AAEtB,eAAK,KAAK,SAAS,qBAAqB,EAAE,YAAY,OAAO,MAAM,SAAS,OAAO,MAAM,MAAM,KAAK,OAAO,OAAO,eAAe,OAAO,OAAO,MAAM,CAAC;AAEtJ,0BAAgB,EAAE;AAClB,uBAAa,EAAE,KAAK,OAAO,KAAK,KAAK,OAAO,KAAK,OAAO,MAAM,SAAS,OAAO,MAAM,KAAK;AACzF,cAAI,OAAO,OAAO,KAAM,iBAAgB,OAAO;AAG/C,gBAAM,MAAM,SAAS,MAAM,IAAI;AAI/B,cAAI,QAAQ,OAAO;AACjB,0BAAc,MAAM;AAAA,UACtB,OAAO;AAGL,gBAAI,iBAAiB,MAAM;AACzB,kBAAI,cAAe,cAAa,aAAa;AAC7C,8BAAgB,WAAW,MAAM;AAC/B,8BAAc,YAAY;AAAA,cAC5B,GAAG,IAAI;AAAA,YACT,OAAO;AACL,4BAAc,WAAW;AAAA,YAC3B;AAAA,UACF;AACA;AAAA,QACF,QAAQ;AAAA,QAER;AAAA,MACF;AACA,WAAK,GAAG,WAAW,KAAK;AAGxB,YAAM,gBAAgB,MAAM;AAC1B,cAAM,OAAO,KAAK,MAAM,KAAK,OAAO,IAAI,GAAG,IAAI,OAAO;AACtD,cAAM,SAAS,sBAAsB,KAAK,GAAG;AAE7C,cAAM,YAAY,KAAK,IAAI,IAAI;AAC/B,cAAM,SAAS,MAAM;AACnB,cAAI,cAAc,gBAAgB;AAChC,gBAAI,YAAY;AAEd,kBAAI,sBAAsB,KAAK,YAAY,oBAAqB,QAAO,CAAC,UAAU;AAClF,qBAAO,CAAC,YAAY,aAAa;AAAA,YACnC;AAEA,mBAAO,CAAC,aAAa;AAAA,UACvB;AAEA,iBAAO,CAAC,aAAa;AAAA,QACvB,GAAG;AAEH,mBAAW,QAAQ,MAAM,KAAK,IAAI,IAAI,KAAK,CAAC,GAAG;AAC7C,qBAAW,QAAQ,OAAO;AACxB,gBAAI;AACF,mBAAK,KAAK,QAAQ,MAAM,IAAI;AAC5B;AACA,mBAAK,KAAK,SAAS,kBAAkB,EAAE,YAAY,MAAM,KAAK,CAAC;AAAA,YACjE,SAAS,GAAG;AACV,mBAAK,KAAK,SAAS,aAAa,QAAQ,IAAI,IAAI,MAAM,OAAO,CAAC,CAAC,CAAC;AAAA,YAClE;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAGA,oBAAc;AAGd,mBAAa,gBAAgB,MAAM;AACjC,sBAAc;AAAA,MAChB,GAAG,aAAa;AAAA,IAClB,CAAC;AAED,SAAK,WAAW,MAAM;AACtB,SAAK,WAAW,MAAM;AACtB,SAAK,MAAM,MAAM;AACjB,SAAK,eAAe,MAAM;AAE1B,SAAK,SAAS,EAAE,MAAM,MAAM,OAAO,MAAM,MAAM,MAAM;AAAA,EACvD;AAAA,EAEQ,cAAoB;AAC1B,QAAI,CAAC,KAAK,QAAQ,CAAC,KAAK,UAAU,KAAK,YAAY,QAAQ,KAAK,YAAY,MAAM;AAChF,YAAM,IAAI,MAAM,iBAAiB;AAAA,IACnC;AAGA,SAAK,gBAAgB;AAGrB,SAAK,cAAc;AAGnB,SAAK,WAAW,gBAAgB,MAAM;AACpC,UAAI;AACF,aAAK,YAAY;AAAA,MACnB,SAAS,GAAG;AACV,aAAK,KAAK,SAAS,aAAa,QAAQ,IAAI,IAAI,MAAM,OAAO,CAAC,CAAC,CAAC;AAAA,MAClE;AAAA,IACF,GAAG,EAAE;AAGL,SAAK,cAAc,gBAAgB,MAAM;AACvC,UAAI;AACF,aAAK,kBAAkB;AAAA,MACzB,SAAS,GAAG;AACV,aAAK,KAAK,SAAS,aAAa,QAAQ,IAAI,IAAI,MAAM,OAAO,CAAC,CAAC,CAAC;AAAA,MAClE;AAAA,IACF,GAAG,GAAG;AAIN,SAAK,UAAU,gBAAgB,MAAM;AACnC,UAAI;AACF,aAAK,cAAc;AAAA,MACrB,SAAS,GAAG;AACV,aAAK,KAAK,SAAS,aAAa,QAAQ,IAAI,IAAI,MAAM,OAAO,CAAC,CAAC,CAAC;AAAA,MAClE;AAAA,IACF,GAAG,GAAI;AAAA,EACT;AAAA,EAEQ,gBAAsB;AAC5B,QAAI,CAAC,KAAK,QAAQ,CAAC,KAAK,UAAU,KAAK,YAAY,QAAQ,KAAK,YAAY,KAAM;AAElF,UAAM,MAAM,KAAK,gBAAgB;AAEjC,QAAI;AAGJ,UAAM,WAAW,EAAE,KAAK,KAAK,UAAU,KAAK,KAAK,SAAS,CAAC;AAE3D,UAAM,MAAM,sBAAsB,KAAK,GAAG;AAE1C,SAAK,KAAK,SAAS,eAAe,EAAE,KAAK,KAAK,MAAM,KAAK,OAAO,MAAM,MAAM,KAAK,OAAO,KAAK,CAAC;AAG9F,SAAK,KAAK,KAAK,KAAK,KAAK,OAAO,MAAM,KAAK,OAAO,IAAI;AAAA,EAKxD;AAAA,EAEQ,kBAAyD;AAG/D,UAAM,wBAAwB;AAM9B,QAAI,KAAK,gBAAgB,GAAG;AAC1B,aAAO,EAAE,UAAU,YAAY,SAAS,OAAO,MAAM,CAAC,EAAE;AAAA,IAC1D;AACA,QAAI,eAAe,KAAK;AACxB,WAAO,KAAK,SAAS,IAAI,YAAY,EAAG;AAExC,QAAI,MAAM,eAAe;AAEzB,eAAW,KAAK,KAAK,SAAS,KAAK,GAAG;AACpC,UAAI,IAAI,IAAK,OAAM;AAAA,IACrB;AAGA,UAAM,aAAa,eAAe,wBAAwB;AAC1D,QAAI,MAAM,WAAY,OAAM;AAE5B,QAAI,MAAM,cAAc;AACtB,aAAO,EAAE,UAAW,eAAe,MAAO,GAAG,SAAS,OAAO,MAAM,CAAC,EAAE;AAAA,IACxE;AAEA,UAAM,MAAM,MAAM,eAAe;AACjC,UAAM,QAAQ,OAAO,YAAY,GAAG;AACpC,aAAS,IAAI,GAAG,IAAI,KAAK,KAAK;AAC5B,YAAM,CAAC,IAAI,KAAK,SAAS,IAAI,eAAe,CAAC,IAAI,IAAI;AAAA,IACvD;AACA,WAAO,EAAE,UAAW,eAAe,MAAO,GAAG,SAAS,MAAM;AAAA,EAC9D;AAAA,EAEQ,kBAAwB;AAC9B,QAAI,KAAK,YAAY,KAAM;AAC3B,UAAM,EAAE,UAAU,QAAQ,IAAI,KAAK,gBAAgB;AACnD,SAAK,gBAAgB,gBAAgB;AAAA,MACnC,cAAc,KAAK;AAAA;AAAA;AAAA,MAEnB,SAAS,aAAa,cAAc,QAAQ,WAAW,IAAI,aAAa;AAAA,MACxE;AAAA,MACA,cAAc,KAAK,WAAW,SAAS;AAAA,MACvC;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEQ,cAAoB;AAC1B,QAAI,CAAC,KAAK,QAAQ,CAAC,KAAK,UAAU,KAAK,cAAc,WAAW,EAAG;AACnE,SAAK,KAAK,KAAK,KAAK,eAAe,KAAK,OAAO,MAAM,KAAK,OAAO,IAAI;AACrE,SAAK;AACL,QAAI,KAAK,eAAe,QAAQ,GAAG;AACjC,WAAK,KAAK,SAAS,gBAAgB,EAAE,SAAS,KAAK,WAAW,SAAS,EAAE,CAAC;AAAA,IAC5E;AAAA,EACF;AAAA,EAEQ,YAAY,SAA4B,QAAc;AAG5D,QAAI,WAAW,UAAU;AACvB,UAAI,KAAK,aAAc;AACvB,WAAK,eAAe;AACpB,mBAAa,MAAM;AACjB,aAAK,eAAe;AACpB,YAAI;AACF,eAAK,YAAY;AAAA,QACnB,SAAS,GAAG;AACV,eAAK,KAAK,SAAS,aAAa,QAAQ,IAAI,IAAI,MAAM,OAAO,CAAC,CAAC,CAAC;AAAA,QAClE;AAAA,MACF,CAAC;AACD,WAAK,KAAK,SAAS,iBAAiB,EAAE,OAAO,CAAC;AAAA,IAChD;AAAA,EACF;AAAA,EAEQ,oBAA0B;AAChC,QAAI,CAAC,KAAK,QAAQ,CAAC,KAAK,OAAQ;AAChC,eAAW,CAAC,EAAE,KAAK,KAAK,KAAK,MAAM;AACjC,WAAK,KAAK,KAAK,MAAM,KAAK,KAAK,OAAO,MAAM,KAAK,OAAO,IAAI;AAAA,IAC9D;AAAA,EACF;AAAA,EAEQ,oBAAoB,UAAkB,SAAuB;AAGnE,QAAI,aAAa,YAAY;AAC3B,iBAAW,KAAK,KAAK,KAAK,KAAK,GAAG;AAChC,YAAI,KAAK,UAAU;AACjB,eAAK,KAAK,OAAO,CAAC;AAAA,QACpB;AAAA,MACF;AACA,eAAS,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK;AACvC,cAAM,IAAI,QAAQ,CAAC,KAAK;AACxB,YAAI,IAAI,GAAG;AACT,gBAAM,MAAO,WAAW,IAAI,MAAO;AACnC,eAAK,KAAK,OAAO,GAAG;AAAA,QACtB;AAAA,MACF;AAAA,IACF;AACA,SAAK,WAAW,KAAK;AAAA,EACvB;AAAA,EAEQ,gBAAsB;AAC5B,QAAI,KAAK,eAAgB;AACzB,SAAK,iBAAiB;AACtB,iBAAa,MAAM;AACjB,WAAK,iBAAiB;AACtB,UAAI;AACF,eAAO,KAAK,oBAAoB,KAAK,YAAY,QAAQ,KAAK,qBAAqB;AACjF,eAAK,KAAK,QAAQ,KAAK,YAAY,KAAK,iBAAiB,CAAE;AAAA,QAC7D;AAAA,MACF,SAAS,GAAG;AACV,aAAK,KAAK,SAAS,aAAa,QAAQ,IAAI,IAAI,MAAM,OAAO,CAAC,CAAC,CAAC;AAAA,MAClE,UAAE;AAEA,YAAI,KAAK,qBAAqB,KAAK,YAAY,QAAQ;AACrD,eAAK,cAAc,CAAC;AACpB,eAAK,oBAAoB;AAAA,QAC3B;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEQ,gBAAsB;AAE5B,WAAO,KAAK,SAAS,IAAI,KAAK,WAAW,GAAG;AAC1C,YAAM,QAAQ,KAAK,SAAS,IAAI,KAAK,WAAW;AAChD,WAAK,SAAS,OAAO,KAAK,WAAW;AACrC,WAAK;AACL,WAAK,YAAY,KAAK,KAAK;AAAA,IAC7B;AACA,QAAI,KAAK,YAAY,SAAS,KAAK,mBAAmB;AACpD,WAAK,cAAc;AAAA,IACrB;AAAA,EACF;AAAA,EAEQ,aAAa,GAAyC,OAAe,OAAqB;AAKhG,UAAM,eAAe,CAAC,WAAmB;AACvC,UAAI,CAAC,KAAK,UAAU,KAAK,OAAO,SAAS,SAAS,KAAK,OAAO,SAAS,OAAO;AAC5E,aAAK,SAAS,EAAE,MAAM,OAAO,MAAM,MAAM;AACzC,aAAK,KAAK,SAAS,iBAAiB,EAAE,QAAQ,MAAM,OAAO,MAAM,MAAM,CAAC;AAAA,MAC1E;AAAA,IACF;AAEA,QAAI,CAAC,KAAK,OAAQ,cAAa,cAAc;AAE7C,QAAI,EAAE,SAAS,OAAO;AACpB,UAAI,KAAK,YAAY,QAAQ,EAAE,iBAAiB,KAAK,UAAU;AAC7D,qBAAa,KAAK;AAClB,aAAK,oBAAoB,EAAE,UAAU,EAAE,OAAO;AAAA,MAChD;AACA;AAAA,IACF;AAEA,QAAI,EAAE,SAAS,QAAQ;AACrB,UAAI,KAAK,YAAY,QAAQ,EAAE,iBAAiB,KAAK,UAAU;AAC7D,qBAAa,MAAM;AAEnB,YAAI,EAAE,YAAY,KAAK,aAAa;AAClC,eAAK,SAAS,IAAI,EAAE,UAAU,EAAE,OAAO;AACvC,eAAK,cAAc;AACnB,eAAK,gBAAgB;AACrB,cAAI,KAAK,cAAc,QAAQ,GAAG;AAChC,iBAAK,KAAK,SAAS,gBAAgB,EAAE,aAAa,KAAK,YAAY,CAAC;AAAA,UACtE;AAAA,QACF;AAAA,MACF;AACA;AAAA,IACF;AAGA,QAAI,EAAE,SAAS,aAAa;AAC1B,WAAK,KAAK,SAAS,0BAA0B,EAAE,KAAK,EAAE,KAAK,KAAK,EAAE,IAAI,CAAC;AAGvE,YAAM,KAAK,WAAW,EAAE,GAAG;AAC3B,UAAI,MAAM,KAAK,YAAY,QAAQ,KAAK,YAAY,QAAQ,GAAG,QAAQ,KAAK,YAAY,GAAG,QAAQ,KAAK,UAAU;AAChH,aAAK,KAAK,SAAS,6BAA6B,EAAE,GAAG,IAAI,OAAO,MAAM,CAAC;AACvE,YAAI;AACF,uBAAa,QAAQ;AACrB,eAAK,cAAc;AAAA,QACrB,SAAS,GAAG;AACV,eAAK,KAAK,SAAS,4BAA4B,CAAC;AAAA,QAClD;AACA;AAAA,MACF;AAKA,YAAM,KAAK,UAAU,EAAE,GAAG;AAC1B,UAAI,MAAM,KAAK,YAAY,QAAQ,KAAK,YAAY,QAAQ,GAAG,QAAQ,KAAK,YAAY,GAAG,QAAQ,KAAK,UAAU;AAChH,YAAI;AACF,uBAAa,OAAO;AACpB,eAAK,MAAM,GAAG;AACd,eAAK,KAAK,SAAS,4BAA4B,EAAE,KAAK,GAAG,KAAK,KAAK,GAAG,KAAK,KAAK,GAAG,KAAK,OAAO,MAAM,CAAC;AACtG,gBAAM,MAAM,KAAK,IAAI;AACrB,gBAAM,aAAa;AACnB,cAAI,KAAK,kBAAkB,QAAQ,MAAM,KAAK,kBAAkB,YAAY;AAC1E,kBAAM,OAAO,UAAU,EAAE,KAAK,GAAG,KAAK,MAAM,GAAG,QAAQ,SAAS,KAAK,GAAG,KAAK,KAAK,GAAG,KAAK,KAAK,KAAK,IAAI,CAAC;AACzG,kBAAM,OAAO,sBAAsB,EAAE,KAAK,IAAI;AAC9C,iBAAK,MAAM,KAAK,MAAM,OAAO,KAAK;AAClC,iBAAK,aAAa;AAClB,iBAAK,iBAAiB;AACtB,iBAAK,KAAK,SAAS,8BAA8B,EAAE,KAAK,GAAG,KAAK,KAAK,GAAG,KAAK,KAAK,GAAG,KAAK,OAAO,MAAM,CAAC;AAAA,UAC1G,OAAO;AACL,iBAAK,KAAK,SAAS,6BAA6B,EAAE,SAAS,MAAM,KAAK,gBAAgB,YAAY,KAAK,GAAG,KAAK,KAAK,GAAG,KAAK,KAAK,GAAG,KAAK,OAAO,MAAM,CAAC;AAAA,UACzJ;AAAA,QACF,SAAS,GAAG;AACV,eAAK,KAAK,SAAS,oCAAoC,CAAC;AAAA,QAC1D;AACA;AAAA,MACF;AAEA,YAAM,MAAM,YAAY,EAAE,GAAG;AAC7B,UAAI,OAAO,KAAK,YAAY,QAAQ,KAAK,YAAY,MAAM;AAEzD,YAAI,IAAI,QAAQ,KAAK,YAAY,IAAI,QAAQ,KAAK,UAAU;AAC1D,eAAK,MAAM,IAAI;AACf,eAAK,KAAK,SAAS,8BAA8B,GAAG;AACpD;AAAA,QACF;AAAA,MACF;AAEA,YAAM,OAAO,aAAa,EAAE,GAAG;AAC/B,UAAI,QAAQ,KAAK,YAAY,QAAQ,KAAK,YAAY,QAAQ,KAAK,QAAQ,KAAK,YAAY,KAAK,QAAQ,KAAK,UAAU;AACtH,aAAK,KAAK,SAAS,+BAA+B,EAAE,GAAG,MAAM,OAAO,MAAM,CAAC;AAE3E,aAAK;AAAA,UACH;AAAA,UACA,IAAI,MAAM,yCAAyC,KAAK,OAAO,OAAO,QAAQ,KAAK,GAAG,KAAK,EAAE,GAAG;AAAA,QAClG;AACA,aAAK,KAAK,MAAM;AAChB;AAAA,MACF;AAAA,IACF;AACA;AAAA,EACF;AAAA,EAEA,MAAM,KAAmB;AACvB,QAAI,CAAC,KAAK,QAAQ,CAAC,KAAK,UAAU,KAAK,YAAY,KAAM,OAAM,IAAI,MAAM,+BAA+B;AACxG,UAAM,aAAa,KAAK,MAAM;AAC9B,aAAS,MAAM,GAAG,MAAM,IAAI,QAAQ,OAAO,YAAY;AACrD,YAAM,UAAU,IAAI,SAAS,KAAK,KAAK,IAAI,IAAI,QAAQ,MAAM,UAAU,CAAC;AACxE,YAAM,WAAW,KAAK,iBAAiB;AACvC,WAAK,eAAgB,KAAK,eAAe,MAAO;AAChD,YAAM,MAAM,iBAAiB,EAAE,cAAc,KAAK,UAAU,UAAU,SAAS,OAAO,KAAK,OAAO,EAAE,CAAC;AACrG,WAAK,KAAK,IAAI,UAAU,EAAE,UAAU,KAAK,KAAK,IAAI,KAAK,IAAI,EAAE,CAAC;AAC9D,WAAK,KAAK,KAAK,KAAK,KAAK,OAAO,MAAM,KAAK,OAAO,IAAI;AAAA,IACxD;AAAA,EACF;AAAA,EAEA,MAAM,QAAuB;AAC3B,QAAI,KAAK,SAAU,eAAc,KAAK,QAAQ;AAC9C,QAAI,KAAK,YAAa,eAAc,KAAK,WAAW;AACpD,QAAI,KAAK,QAAS,eAAc,KAAK,OAAO;AAC5C,SAAK,WAAW;AAChB,SAAK,cAAc;AACnB,SAAK,UAAU;AAEf,UAAM,IAAI,KAAK;AACf,SAAK,OAAO;AACZ,QAAI,CAAC,EAAG;AACR,UAAM,IAAI,QAAc,CAAC,YAAY,EAAE,MAAM,MAAM,QAAQ,CAAC,CAAC;AAAA,EAC/D;AACF;;;AMxoCA,IAAM,OAAO,MAAY;AAEzB;AAEA,SAAS,cAAc,MAAW,MAAgD;AAChF,QAAM,KAAK,OAAO,IAAI;AACtB,MAAI,OAAO,OAAO,WAAY,QAAO,GAAG,KAAK,IAAI;AAEjD,QAAM,WAAW,MAAM;AACvB,MAAI,OAAO,aAAa,WAAY,QAAO,SAAS,KAAK,IAAI;AAE7D,SAAO;AACT;AAEO,SAAS,SAAS,QAA6B;AACpD,QAAM,OAAY,UAAU;AAE5B,QAAM,MAAc;AAAA,IAClB,KAAK,cAAc,MAAM,KAAK;AAAA,IAC9B,MAAM,cAAc,MAAM,MAAM;AAAA,IAChC,MAAM,cAAc,MAAM,MAAM;AAAA,IAChC,OAAO,cAAc,MAAM,OAAO;AAAA,IAClC,OAAO,cAAc,MAAM,OAAO;AAAA,EACpC;AAGA,MAAI,QAAQ,CAAC,QAAwB,mBAAmB,KAAK,GAAG;AAChE,SAAO;AACT;AAEO,SAAS,mBAA2B;AACzC,QAAM,MAAc;AAAA,IAClB,KAAK;AAAA,IACL,MAAM;AAAA,IACN,MAAM;AAAA,IACN,OAAO;AAAA,IACP,OAAO;AAAA,EACT;AACA,MAAI,QAAQ,MAAc;AAC1B,SAAO;AACT;AAEA,SAAS,YAAY,OAAyB;AAC5C,UAAQ,OAAO;AAAA,IACb,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT;AACE,aAAO;AAAA,EACX;AACF;AAEO,SAAS,aAAa,SAIlB;AACT,QAAM,OAAO,SAAS,SAAS,IAAI;AACnC,QAAM,QAAQ,SAAS,SAAS;AAChC,QAAM,MAAM,SAAS;AAErB,QAAM,UAAU,YAAY,KAAK;AACjC,QAAM,YAAY,CAAC,aAAgC,YAAY,QAAQ,KAAK,WAAW,YAAY;AAEnG,QAAM,SAAS,MAAM,IAAI,GAAG,OAAO;AAEnC,QAAM,OAAO,CAAC,OAAiC;AAC7C,WAAO,CAAC,YAAsB,mBAA8B;AAC1D,UAAI,OAAO,YAAY,SAAU,IAAG,GAAG,MAAM,GAAG,OAAO,IAAI,GAAG,cAAc;AAAA,eACnE,YAAY,OAAW,IAAG,QAAQ,SAAS,GAAG,cAAc;AAAA,UAChE,IAAG,OAAO,QAAQ,GAAG,GAAG,cAAc;AAAA,IAC7C;AAAA,EACF;AAEA,QAAM,MAAc;AAAA,IAClB,OAAO,UAAU,OAAO,IAAI,KAAK,KAAK,KAAK,IAAI;AAAA,IAC/C,MAAM,UAAU,MAAM,IAAI,KAAK,KAAK,IAAI,IAAI;AAAA,IAC5C,MAAM,UAAU,MAAM,IAAI,KAAK,KAAK,IAAI,IAAI;AAAA,IAC5C,KAAK,UAAU,MAAM,IAAI,KAAK,KAAK,GAAG,IAAI;AAAA,IAC1C,OAAO,UAAU,OAAO,IAAI,KAAK,KAAK,KAAK,IAAI;AAAA,EACjD;AAEA,MAAI,QAAQ,CAAC,aAA6B;AACxC,UAAM,WAAW,MAAM,GAAG,GAAG,IAAI,QAAQ,KAAK;AAC9C,WAAO,aAAa,EAAE,MAAM,OAAO,KAAK,SAAS,CAAC;AAAA,EACpD;AAEA,SAAO;AACT;AAEO,SAAS,mBAAmB,MAA2B,KAAqB;AAEjF,QAAM,IAAK,KAAgB,MAAO,OAAkB,SAAS,IAAkB;AAC/E,QAAM,SAAS,IAAI,GAAG;AAEtB,QAAM,OAAO,CAAC,OAAiC;AAC7C,WAAO,CAAC,YAAsB,mBAA8B;AAC1D,UAAI,OAAO,YAAY,SAAU,IAAG,GAAG,MAAM,GAAG,OAAO,IAAI,GAAG,cAAc;AAAA,eACnE,YAAY,OAAW,IAAG,QAAQ,SAAS,GAAG,cAAc;AAAA,UAChE,IAAG,OAAO,QAAQ,GAAG,GAAG,cAAc;AAAA,IAC7C;AAAA,EACF;AAEA,QAAM,MAAc;AAAA,IAClB,KAAK,KAAK,EAAE,GAAG;AAAA,IACf,MAAM,KAAK,EAAE,IAAI;AAAA,IACjB,MAAM,KAAK,EAAE,IAAI;AAAA,IACjB,OAAO,KAAK,EAAE,KAAK;AAAA,IACnB,OAAO,KAAK,EAAE,KAAK;AAAA,EACrB;AACA,MAAI,QAAQ,CAAC,aAA6B,mBAAmB,KAAK,QAAQ;AAC1E,SAAO;AACT;AAMO,SAAS,sBAAsB,MAAmB,UAAmB,OAAe;AACzF,QAAM,IAAI,SAAS,IAAI;AACvB,QAAM,MAAc;AAAA,IAClB,KAAK,EAAE;AAAA,IACP,MAAM,EAAE;AAAA,IACR,MAAM,EAAE;AAAA,IACR,OAAO,EAAE;AAAA,IACT,OAAO,UAAU,EAAE,QAAQ;AAAA,EAC7B;AACA,MAAI,QAAQ,CAAC,QAAwB,sBAAsB,mBAAmB,KAAK,GAAG,GAAG,OAAO;AAChG,SAAO;AACT;AAEA,IAAI;AAEG,SAAS,gBAAgB,QAA+C;AAC7E,iBAAe,SAAU,OAAe,MAAO,SAAoB,SAAS,MAAoB,IAAI;AACtG;AAEO,SAAS,kBAA0B;AACxC,SAAO,gBAAgB,SAAS,OAAO;AACzC;;;ACjKA,SAAS,gBAAAA,qBAAoB;AAC7B,SAAS,kBAAkB;AAC3B,OAAO,SAAS;AA0DhB,SAAS,UAAU,OAAwB;AACzC,SACE,UAAU,0BACV,UAAU,wBACV,UAAU,yBACV,UAAU;AAEd;AAiEO,IAAM,iBAAN,MAAM,wBAAuBC,cAQjC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUD,OAAwB,oBAAoB,oBAAI,IAG9C;AAAA,EAEe;AAAA,EACA;AAAA,EACA;AAAA,EAET;AAAA,EACA;AAAA;AAAA;AAAA,EAGA,gBAAgB;AAAA,EAExB,wBAAiC;AAC/B,WAAO,KAAK,KAAK,sBAAsB;AAAA,EACzC;AAAA,EACQ;AAAA,EACA,YAA2B;AAAA,EAClB,SAAS,IAAI,oBAAoB;AAAA,EACjC,UAAU,oBAAI,IAG7B;AAAA,EACM,eAAe;AAAA,EAEf;AAAA,EAOA;AAAA,EASA,SAAS;AAAA,EACjB,WAAW;AAAA;AAAA,EACX,aAAa;AAAA;AAAA,EAEL;AAAA,EACA,wBAAwB;AAAA;AAAA;AAAA,EAGxB,8BAA8B;AAAA,EAC9B,wBAAwB;AAAA,EACxB,8BAA8B;AAAA,EAE9B;AAAA,EACA;AAAA,EACA,YAAY;AAAA,EACH,UAAU,oBAAI,IAO7B;AAAA,EAEM;AAAA,EACA;AAAA,EACA;AAAA,EAWA;AAAA;AAAA,EAYS,YAOZ,CAAC;AAAA;AAAA,EAGW,YAOZ,CAAC;AAAA;AAAA,EAGN,OAAwB,kBAAkB,oBAAI,IAAY;AAAA,IACxD;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AAAA,EAED,MAA0B,EAAE,MAAM,OAAO;AAAA;AAAA,EACjC;AAAA;AAAA,EAGA,qBAAqB,oBAAI,IAAyB;AAAA;AAAA,EAGlD,uCAAuC;AAAA;AAAA,EAGvC,mBAAmB,oBAAI,IAG7B;AAAA;AAAA,EAGM,kBAAkB,oBAAI,IAG5B;AAAA;AAAA;AAAA,EAIe,kBAAkB,oBAAI,IAUrC;AAAA,EAEF,YAAY,SAAgC;AAC1C,UAAM;AACN,SAAK,OAAO;AACZ,SAAK,WAAW,sBAAsB,QAAQ,YAAY;AAE1D,SAAK,SAAS;AAAA,MACZ,QAAQ;AAAA,MACR,KAAK,SAAS,WACZ,KAAK,SAAS,qBACd,KAAK,SAAS,mBACd,KAAK,SAAS,aACd,KAAK,SAAS,eACd,KAAK,SAAS;AAAA,IAClB;AAIA,SAAK,GAAG,SAAS,CAAC,QAAQ;AACxB,WAAK,SAAS,SAAS;AAAA,QACrB,SAAS,KAAK,WAAW,OAAO,GAAG;AAAA,QACnC,MAAO,KAAa;AAAA,MACtB,CAAC;AAAA,IACH,CAAC;AAAA,EAEH;AAAA,EAEQ,mBAAmB,WAAkC;AAE3D,UAAM,QAAQ,WAAW,EAAE,MAAM,GAAG,EAAE,CAAC,KAAK,WAAW,EAAE,MAAM,GAAG,CAAC;AACnE,WAAO,GAAG,SAAS,IAAI,KAAK;AAAA,EAC9B;AAAA,EAEQ,SAAS,OAAe,MAAsB;AACpD,UAAM,SAAS;AACf,UAAM,MAAM,GAAG,MAAM,IAAI,KAAK;AAC9B,UAAM,IAAS,KAAK;AAEpB,QAAI,OAAO,EAAE,SAAS,YAAY;AAChC,QAAE,KAAK,KAAK,IAAI;AAChB;AAAA,IACF;AACA,QAAI,OAAO,EAAE,QAAQ,YAAY;AAC/B,QAAE,IAAI,KAAK,IAAI;AACf;AAAA,IACF;AACA,QAAI,OAAO,EAAE,SAAS,YAAY;AAChC,QAAE,KAAK,KAAK,IAAI;AAAA,IAClB;AAAA,EACF;AAAA,EAEQ,6BAAqC;AAC3C,UAAM,IAAI,KAAK,KAAK;AACpB,QAAI,OAAO,MAAM,YAAY,OAAO,SAAS,CAAC,KAAK,IAAI,EAAG,QAAO;AACjE,WAAO;AAAA,EACT;AAAA,EAEQ,0BAAmC;AACzC,WAAO,KAAK,KAAK,mBAAmB;AAAA,EACtC;AAAA,EAEQ,2BAAiC;AACvC,QAAI,CAAC,KAAK,oBAAqB;AAC/B,iBAAa,KAAK,mBAAmB;AACrC,SAAK,sBAAsB;AAAA,EAC7B;AAAA,EAEQ,kBAAkB,QAAsB;AAC9C,SAAK,uBAAuB,KAAK,IAAI;AACrC,SAAK,SAAS,iBAAiB,EAAE,QAAQ,MAAM,KAAK,qBAAqB,CAAC;AAAA,EAC5E;AAAA,EAEQ,8BAAuC;AAC7C,QAAI,CAAC,KAAK,wBAAwB,EAAG,QAAO;AAC5C,QAAI,CAAC,KAAK,kBAAkB,EAAG,QAAO;AACtC,QAAI,KAAK,QAAQ,OAAO,EAAG,QAAO;AAClC,QAAI,KAAK,oCAAoC,EAAG,QAAO;AACvD,QAAI,KAAK,QAAQ,OAAO,EAAG,QAAO;AAClC,WAAO;AAAA,EACT;AAAA,EAEQ,0BAAgC;AACtC,QAAI,CAAC,KAAK,wBAAwB,EAAG;AACrC,SAAK,yBAAyB;AAE9B,QAAI,CAAC,KAAK,4BAA4B,EAAG;AACzC,QAAI,KAAK,wBAAwB,KAAM;AAEvC,UAAM,YAAY,KAAK,2BAA2B;AAClD,UAAM,YAAY,KAAK,IAAI,IAAI,KAAK;AACpC,UAAM,UAAU,KAAK,IAAI,GAAG,YAAY,SAAS;AAEjD,SAAK,sBAAsB,WAAW,MAAM;AAC1C,UAAI;AACF,YAAI,CAAC,KAAK,4BAA4B,EAAG;AACzC,YAAI,KAAK,wBAAwB,KAAM;AACvC,cAAM,WAAW,KAAK,IAAI,IAAI,KAAK;AACnC,YAAI,WAAW,WAAW;AACxB,eAAK,wBAAwB;AAC7B;AAAA,QACF;AACA,cAAM,MAAM,KAAK;AACjB,cAAM,WAAW,KAAK,KAAK,MACvB,KAAK,KAAK,IAAI,UAAU,GAAG,CAAC,IAC5B;AACJ,aAAK,SAAS,mBAAmB;AAAA,UAC/B,WAAW;AAAA,UACX;AAAA,UACA,WAAW,KAAK;AAAA,UAChB,MAAM,KAAK,KAAK;AAAA,UAChB;AAAA,UACA,KAAK;AAAA,QACP,CAAC;AACD,aAAK,SAAS,mBAAmB;AAAA,UAC/B,WAAW;AAAA,UACX;AAAA,UACA,WAAW,KAAK;AAAA,UAChB,MAAM,KAAK,KAAK;AAAA,UAChB;AAAA,UACA,KAAK;AAAA,QACP,CAAC;AACD,aAAK,KAAK,MAAM,EAAE,QAAQ,kBAAkB,CAAC;AAAA,MAC/C,SAAS,GAAG;AACV,aAAK,SAAS,yBAAyB,CAAC;AAAA,MAC1C;AAAA,IACF,GAAG,OAAO;AACV,SAAK,oBAAoB,QAAQ;AAAA,EACnC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,0BAA0B,SAAS,aAAa,UAAU,GAAS;AACjE,QAAI;AACF,UAAI,CAAC,KAAK,wBAAwB,EAAG;AACrC,UAAI,KAAK,cAAc,MAAO;AAE9B,YAAM,YAAY,KAAK,2BAA2B;AAClD,YAAM,IAAI,KAAK,IAAI,GAAG,KAAK,MAAM,OAAO,CAAC;AAIzC,WAAK,uBAAuB,KAAK,IAAI,IAAI,YAAY;AACrD,YAAM,MAAM,KAAK;AACjB,YAAM,WAAW,KAAK,KAAK,MACvB,KAAK,KAAK,IAAI,UAAU,GAAG,CAAC,IAC5B;AACJ,WAAK,SAAS,6BAA6B;AAAA,QACzC;AAAA,QACA,SAAS;AAAA,QACT;AAAA,QACA,MAAM,KAAK,KAAK;AAAA,QAChB;AAAA,QACA,KAAK;AAAA,MACP,CAAC;AACD,WAAK,wBAAwB;AAAA,IAC/B,QAAQ;AAAA,IAER;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,wBAOc;AACZ,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,cACE,SAAS,KAAK,2BAA2B,GACzC,QACY;AACZ,UAAM,KAAK,KAAK,IAAI,GAAG,KAAK,MAAM,MAAM,CAAC;AACzC,UAAM,KAAK,KAAK;AAChB,UAAM,UAAU,KAAK,IAAI,IAAI;AAE7B,QAAI;AACJ,QAAI,KAAK,GAAG;AACV,cAAQ,WAAW,MAAM,KAAK,cAAc,IAAI,SAAS,GAAG,EAAE;AAC9D,YAAM,QAAQ;AAAA,IAChB;AAEA,SAAK,QAAQ,IAAI,IAAI,EAAE,OAAO,SAAS,OAAO,CAAC;AAC/C,SAAK,SAAS,mBAAmB;AAAA,MAC/B;AAAA,MACA,QAAQ;AAAA,MACR;AAAA,MACA;AAAA,MACA,SAAS,KAAK,QAAQ;AAAA,IACxB,CAAC;AAGD,SAAK,yBAAyB;AAE9B,WAAO,MAAM,KAAK,cAAc,IAAI,QAAQ;AAAA,EAC9C;AAAA,EAEQ,cAAc,IAAY,KAA2C;AAC3E,UAAM,IAAI,KAAK,QAAQ,IAAI,EAAE;AAC7B,QAAI,CAAC,EAAG;AACR,QAAI,EAAE,MAAO,cAAa,EAAE,KAAK;AACjC,SAAK,QAAQ,OAAO,EAAE;AACtB,SAAK,SAAS,mBAAmB,EAAE,IAAI,KAAK,SAAS,KAAK,QAAQ,KAAK,CAAC;AACxE,SAAK,wBAAwB;AAAA,EAC/B;AAAA,EAEQ,uBAA+B;AAErC,UAAM,OAAO,KAAK,KAAK,OAAO,IAAI,KAAK,EAAE,YAAY;AACrD,UAAM,QAAQ,KAAK,KAAK,QAAQ,IAAI,KAAK;AACzC,UAAM,UAAU,KAAK,KAAK,WAAW;AACrC,WAAO,GAAG,IAAI,IAAI,GAAG,IAAI,OAAO;AAAA,EAClC;AAAA,EAEQ,uCAA6C;AACnD,UAAM,mBAAmB,KAAK,oCAAoC;AAClE,QAAI,qBAAqB,KAAK,qCAAsC;AAEpE,UAAM,MAAM,KAAK,qBAAqB;AACtC,UAAM,MAAM,gBAAe,kBAAkB,IAAI,GAAG,KAAK;AAAA,MACvD,qBAAqB;AAAA,IACvB;AACA,UAAM,YAAY,KAAK;AAAA,MACrB;AAAA,MACA,IAAI,uBAAuB,mBAAmB,IAAI;AAAA,IACpD;AACA,QAAI,cAAc,EAAG,iBAAe,kBAAkB,OAAO,GAAG;AAAA;AAE9D,sBAAe,kBAAkB,IAAI,KAAK;AAAA,QACxC,qBAAqB;AAAA,MACvB,CAAC;AAEH,SAAK,uCAAuC;AAAA,EAC9C;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,0BAAmC;AACjC,QAAI,KAAK,oCAAoC,EAAG,QAAO;AACvD,UAAM,MAAM,KAAK,qBAAqB;AACtC,UAAM,MAAM,gBAAe,kBAAkB,IAAI,GAAG;AACpD,YAAQ,KAAK,uBAAuB,KAAK;AAAA,EAC3C;AAAA,EAEQ,SAAS,OAAe,MAAsB;AACpD,QAAI,KAAK,SAAS,SAAS;AACzB,WAAK,OAAO,MAAM,oBAAoB,KAAK,IAAI,IAAI;AACnD,WAAK,KAAK,SAAS,OAAO,IAAI;AAAA,IAChC;AAAA,EACF;AAAA,EAEA,eAA8B;AAC5B,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,uBAA+B;AAC7B,WAAO,KAAK,KAAK,WAAW;AAAA,EAC9B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,mBAAyB;AACvB,SAAK,cAAc;AACnB,QAAI,KAAK,kBAAkB,EAAG,MAAK,eAAe;AAAA,EACpD;AAAA;AAAA,EAGA,gBAAoC;AAClC,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAGA,gBASc;AACZ,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAGA,eAOG;AACD,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAGA,gBAAoC;AAClC,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAGA,gBASc;AACZ,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAGA,eAOG;AACD,WAAO,KAAK;AAAA,EACd;AAAA,EAEQ,SAAS,MAMR;AACP,UAAM,MAAM,KAAK,IAAI;AACrB,SAAK,aAAa;AAClB,SAAK,aAAa,EAAE,MAAM,KAAK,GAAG,KAAK;AACvC,SAAK,UAAU,KAAK,KAAK,UAAU;AACnC,QAAI,KAAK,UAAU,SAAS,GAAI,MAAK,UAAU,MAAM;AAAA,EACvD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,mBAAmB,SAAS,MAAiB;AAC3C,QAAI,KAAK,cAAc,MAAO,QAAO;AAGrC,QAAI,KAAK,wBAAwB,GAAG;AAClC,WAAK;AACL,aAAO,CAAC,KAAK,kBAAkB;AAAA,IACjC;AAIA,QAAI,CAAC,KAAK,kBAAkB,EAAG,QAAO;AACtC,QAAI,KAAK,wBAAwB,EAAG,QAAO;AAC3C,QAAI,KAAK,cAAc,KAAM,QAAO;AACpC,WAAO,KAAK,IAAI,IAAI,KAAK,cAAc;AAAA,EACzC;AAAA,EAEA,iBAA8B;AAC5B,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,qBAAyC;AACvC,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,oBAA6B;AAC3B,QAAI,KAAK,cAAc,OAAO;AAC5B,aAAO,KAAK,cAAc,UAAa,CAAC,KAAK,UAAU;AAAA,IACzD;AACA,QAAI,KAAK,cAAc,OAAO;AAC5B,aACE,KAAK,cAAc,WAAc,KAAK,UAAU,cAAc,KAAK;AAAA,IAEvE;AACA,WAAO;AAAA,EACT;AAAA,EAEA,IAAI,WAAmB;AACrB,WAAO,KAAK,KAAK;AAAA,EACnB;AAAA,EAEQ,iBAAuB;AAC7B,QAAI,KAAK,eAAgB;AAOzB,QAAI,WAAW;AACf,QAAI,KAAK,cAAc,OAAO;AAC5B,UAAI,CAAC,KAAK,uBAAuB,EAAG;AACpC,iBAAW;AAAA,IACb;AAEA,QAAI,YAAY,EAAG;AAEnB,SAAK,iBAAiB,YAAY,MAAM;AAGtC,UAAI,KAAK,cAAc,OAAO;AAC5B,YAAI;AACF,eAAK,iBAAiB;AAAA,QACxB,SAAS,GAAG;AACV,eAAK,SAAS,4BAA4B,CAAC;AAAA,QAC7C;AACA;AAAA,MACF;AAGA,UAAI,KAAK,sBAAuB;AAChC,WAAK,wBAAwB;AAC7B,YAAM,YAAY;AAChB,YAAI;AACF,gBAAM,KAAK,SAAS;AAAA,QACtB,SAAS,GAAG;AACV,eAAK,SAAS,wBAAwB,CAAC;AAAA,QACzC,UAAE;AACA,eAAK,wBAAwB;AAAA,QAC/B;AAAA,MACF,GAAG;AAAA,IACL,GAAG,QAAQ;AAEX,SAAK,eAAe,QAAQ;AAAA,EAC9B;AAAA,EAEQ,sCAA+C;AACrD,eAAW,OAAO,KAAK,mBAAmB,OAAO,GAAG;AAClD,UAAI,IAAI,OAAO,EAAG,QAAO;AAAA,IAC3B;AACA,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,8BAAuC;AACrC,WAAO,KAAK,oCAAoC;AAAA,EAClD;AAAA,EAEQ,yBAAkC;AAExC,QAAI,KAAK,cAAc,MAAO,QAAO;AACrC,QAAI,CAAC,KAAK,UAAW,QAAO;AAC5B,QAAI,CAAC,KAAK,SAAU,QAAO;AAI3B,QAAI,KAAK,oCAAoC,EAAG,QAAO;AACvD,WAAO;AAAA,EACT;AAAA,EAEQ,mBAAyB;AAC/B,QAAI,KAAK,cAAc,MAAO;AAC9B,QAAI,CAAC,KAAK,UAAW;AAErB,QAAI,CAAC,KAAK,SAAU;AAEpB,UAAM,SAAS,KAAK,WAAW;AAC/B,UAAM,SAAS,aAAa;AAAA,MAC1B,OAAO;AAAA,MACP,SAAS;AAAA,MACT,WAAW;AAAA,MACX,YAAY;AAAA,MACZ;AAAA,MACA,cAAc;AAAA,MACd,cAAc;AAAA,MACd,eAAe;AAAA,IACjB,CAAC;AAED,SAAK,SAAS,yBAAyB;AAAA,MACrC;AAAA,MACA,WAAW;AAAA,MACX,YAAY;AAAA,IACd,CAAC;AACD,SAAK,SAAS;AAAA,MACZ,OAAO;AAAA,MACP,cAAc;AAAA,MACd;AAAA,MACA,WAAW;AAAA,MACX,YAAY;AAAA,IACd,CAAC;AACD,SAAK,UAAU,MAAM;AAAA,EACvB;AAAA,EAEQ,gBAAsB;AAC5B,QAAI,KAAK,gBAAgB;AACvB,oBAAc,KAAK,cAAc;AACjC,WAAK,iBAAiB;AAAA,IACxB;AACA,SAAK,wBAAwB;AAAA,EAC/B;AAAA,EAEA,MAAc,WAA0B;AAEtC,QAAI,CAAC,KAAK,aAAa,CAAC,KAAK,UAAW;AAGxC,QAAI,CAAC,KAAK,SAAU;AAGpB,QAAI;AAGF,YAAM,KAAK,UAAU;AAAA,QACnB,OAAO;AAAA,QACP,SAAS,KAAK,KAAK,WAAW;AAAA,QAC9B,mBAAmB;AAAA;AAAA,QACnB,cAAc;AAAA,QACd,YAAY;AAAA,QACZ,cAAc;AAAA;AAAA,QACd,UAAU;AAAA,MACZ,CAAC;AAAA,IACH,SAAS,GAAG;AAEV,WAAK,SAAS,yBAAyB,CAAC;AAAA,IAC1C;AAAA,EACF;AAAA,EAEA,MAAM,UAAyB;AAC7B,UAAM,UAAU,KAAK,KAAK,aAAa;AACvC,QAAI,YAAY,OAAO;AACrB,YAAM,KAAK,WAAW;AACtB;AAAA,IACF;AACA,QAAI,YAAY,OAAO;AACrB,YAAM,KAAK,4BAA4B;AACvC,YAAM,KAAK,WAAW;AACtB;AAAA,IACF;AAEA,QAAI;AAGF,YAAM,QAAQ,KAAK;AAAA,QACjB,KAAK,WAAW;AAAA,QAChB,IAAI;AAAA,UAAc,CAAC,GAAG,WACpB;AAAA,YACE,MACE,OAAO,IAAI,MAAM,8CAA8C,CAAC;AAAA,YAClE;AAAA,UACF;AAAA,QACF;AAAA,MACF,CAAC;AAAA,IACH,SAAS,GAAG;AACV,WAAK,SAAS,mBAAmB,CAAC;AAGlC,UAAI,CAAC,KAAK,KAAK,KAAK;AAClB,cAAM,IAAI;AAAA,UACR;AAAA,QACF;AAAA,MACF;AACA,YAAM,KAAK,4BAA4B;AACvC,YAAM,KAAK,WAAW;AAAA,IACxB;AAAA,EACF;AAAA,EAEA,MAAc,8BAA6C;AACzD,UAAM,MAAM,KAAK,IAAI;AACrB,UAAM,SAAS,KAAK,8BAA8B;AAClD,QAAI,UAAU,EAAG;AACjB,UAAM,MAAM,KAAK;AACjB,UAAM,WAAW,KAAK,KAAK,MAAM,KAAK,KAAK,IAAI,UAAU,GAAG,CAAC,IAAI;AACjE,SAAK,SAAS,0BAA0B;AAAA,MACtC,WAAW;AAAA,MACX,MAAM,KAAK,KAAK;AAAA,MAChB;AAAA,MACA,KAAK;AAAA,MACL;AAAA,IACF,CAAC;AACD,UAAM,IAAI,QAAc,CAAC,YAAY,WAAW,SAAS,MAAM,CAAC;AAAA,EAClE;AAAA,EAEA,MAAc,aAA4B;AACxC,QAAI,KAAK,aAAa,CAAC,KAAK,UAAU,WAAW;AAC/C,WAAK,YAAY;AACjB;AAAA,IACF;AACA,UAAM,OAAO,KAAK,KAAK,QAAQ;AAC/B,UAAM,OAAO,IAAI,iBAAiB,EAAE,MAAM,KAAK,KAAK,MAAM,KAAK,CAAC;AAChE,SAAK,YAAY;AACjB,SAAK,YAAY;AACjB,SAAK,eAAe;AACpB,SAAK,kBAAkB,KAAK,mBAAmB,KAAK;AAGpD,QAAI;AACF,WAAK,aAAa,MAAM,GAAM;AAAA,IAChC,SAAS,GAAG;AACV,WAAK,SAAS,2BAA2B,CAAC;AAAA,IAC5C;AAEA,SAAK,GAAG,QAAQ,CAAC,UAAU;AACzB,YAAM,SAAS,KAAK,OAAO,KAAK,KAAK;AACrC,iBAAW,KAAK,OAAQ,MAAK,YAAY,CAAC;AAAA,IAC5C,CAAC;AACD,SAAK,GAAG,SAAS,MAAM;AACrB,YAAMC,OAAM,KAAK;AAEjB,UAAI,SAAS,KAAK,WAAW;AAC3B,aAAK,YAAY;AAAA,MACnB;AAGA,WAAK,mBAAmB;AAGxB,UAAI,CAAC,KAAK,WAAW;AACnB,aAAK,QAAQ;AAAA,MACf;AAEA,WAAK,cAAc;AACnB,WAAK,eAAe;AAEpB,YAAM,UAAU,KAAK;AACrB,WAAK,mBAAmB;AACxB,WAAK,qBAAqB;AAAA,QACxB,MAAM,KAAK,IAAI;AAAA,QACf,WAAW;AAAA,QACX,WAAW,WAAW;AAAA,QACtB,QAAQ,SAAS,UAAU;AAAA,MAC7B;AAEA,YAAM,qBAA+B;AAAA,QACnC;AAAA,QACA,QAAQ,KAAK,KAAK,IAAI;AAAA,MACxB;AACA,UAAIA,KAAK,oBAAmB,KAAK,OAAOA,IAAG,EAAE;AAC7C,YAAM,UAAU,KAAK,KAAK,QAAQ;AAClC,UAAI,WAAW,KAAM,oBAAmB,KAAK,QAAQ,OAAO,EAAE;AAC9D,UAAI,KAAK,YAAY,SAAS;AAC5B,2BAAmB,KAAK,eAAe,KAAK,WAAW,KAAK,EAAE;AAChE,UAAI,KAAK,YAAY,SAAS;AAC5B,2BAAmB,KAAK,eAAe,KAAK,WAAW,KAAK,EAAE;AAChE,WAAK,SAAS,gBAAgB,mBAAmB,KAAK,GAAG,CAAC;AAG1D,WAAK,kBAAkB;AAGvB,WAAK,WAAW;AAChB,WAAK,aAAa;AAElB,WAAK,KAAK,OAAO;AAGjB,YAAM,iBAAiB,MAAM,KAAK,KAAK,QAAQ,QAAQ,CAAC;AACxD,WAAK,QAAQ,MAAM;AACnB,iBAAW,CAAC,EAAE,CAAC,KAAK,gBAAgB;AAGlC,qBAAa,MAAM;AACjB,cAAI;AACF,cAAE,OAAO,IAAI,MAAM,wBAAwB,CAAC;AAAA,UAC9C,SAAS,GAAG;AAAA,UAEZ;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF,CAAC;AACD,SAAK,GAAG,SAAS,CAAC,QAAQ,KAAK,KAAK,SAAS,GAAG,CAAC;AAEjD,UAAM,IAAI,QAAc,CAAC,SAAS,WAAW;AAC3C,WAAK,KAAK,WAAW,MAAM,QAAQ,CAAC;AACpC,WAAK,KAAK,SAAS,CAAC,MAAM,OAAO,CAAC,CAAC;AAAA,IACrC,CAAC;AAED,UAAM,MAAM,KAAK;AAGjB,UAAM,eAAe,KAAK;AAC1B,UAAM,cAAc,KAAK;AACzB,UAAM,YAAY,KAAK;AACvB,UAAM,SAAS,eACX,GAAG,YAAY,GAAG,cAAc,IAAI,WAAW,KAAK,EAAE,GAAG,YAAY,IAAI,SAAS,KAAK,EAAE,KACzF;AAEJ,UAAM,cAAc,KAAK;AACzB,UAAM,aAAa,KAAK;AACxB,UAAM,WAAW,KAAK;AACtB,UAAM,OAAO,cACT,GAAG,WAAW,GAAG,aAAa,IAAI,UAAU,KAAK,EAAE,GAAG,WAAW,IAAI,QAAQ,KAAK,EAAE,KACpF;AACJ,SAAK;AAAA,MACH;AAAA,MACA,sBAAsB,KAAK,KAAK,IAAI,SAAS,IAAI,GAC5C,MAAM,QAAQ,GAAG,KAAK,EAAE,GACxB,SAAS,WAAW,MAAM,KAAK,EAAE,GACjC,OAAO,SAAS,IAAI,KAAK,EAAE;AAAA,IAClC;AAEA,SAAK,eAAe;AACpB,SAAK,wBAAwB;AAAA,EAC/B;AAAA,EAEA,MAAc,aAA4B;AACxC,QAAI,KAAK,WAAW;AAGlB,UAAI,KAAK,UAAU,cAAc,GAAG;AAClC,aAAK,YAAY;AACjB;AAAA,MACF;AACA,UAAI;AACF,cAAM,KAAK,UAAU,MAAM;AAAA,MAC7B,QAAQ;AAAA,MAER;AACA,WAAK,YAAY;AAAA,IACnB;AACA,QAAI,CAAC,KAAK,KAAK,KAAK;AAClB,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AACA,UAAM,OAAO,IAAI,YAAY;AAAA,MAC3B,MAAM;AAAA,MACN,KAAK,KAAK,KAAK;AAAA;AAAA,MAEf,GAAI,KAAK,KAAK,MAAM,KAAK,IAAI,EAAE,YAAY,KAAK,KAAK,KAAK,KAAK,EAAE,IAAI,CAAC;AAAA,MACtE,GAAI,KAAK,KAAK,qBACV,EAAE,iBAAiB,KAAK,KAAK,mBAAmB,IAChD,CAAC;AAAA,IACP,CAAC;AACD,SAAK,YAAY;AACjB,SAAK,YAAY;AACjB,SAAK,eAAe;AACpB,SAAK,kBAAkB,KAAK,mBAAmB,KAAK;AAEpD,SAAK,GAAG,QAAQ,CAAC,UAAU;AACzB,YAAM,SAAS,KAAK,OAAO,KAAK,KAAK;AACrC,iBAAW,KAAK,OAAQ,MAAK,YAAY,CAAC;AAAA,IAC5C,CAAC;AACD,SAAK,GAAG,SAAS,MAAM;AACrB,YAAMA,OAAM,KAAK;AAEjB,UAAI,SAAS,KAAK,WAAW;AAC3B,aAAK,YAAY;AAAA,MACnB;AAGA,UAAI,KAAK,oBAAoB;AAC3B,aAAK,mBAAmB;AAAA,MAC1B;AAEA,WAAK,cAAc;AACnB,WAAK,eAAe;AAEpB,YAAM,UAAU,KAAK;AACrB,WAAK,mBAAmB;AACxB,WAAK,qBAAqB;AAAA,QACxB,MAAM,KAAK,IAAI;AAAA,QACf,WAAW;AAAA,QACX,WAAW,WAAW;AAAA,QACtB,QAAQ,SAAS,UAAU;AAAA,MAC7B;AAEA,YAAM,qBAA+B;AAAA,QACnC;AAAA,QACA,QAAQ,KAAK,KAAK,IAAI;AAAA,MACxB;AACA,UAAIA,KAAK,oBAAmB,KAAK,OAAOA,IAAG,EAAE;AAC7C,UAAI,KAAK,KAAK,KAAK;AACjB,cAAMC,YAAW,KAAK,KAAK,IAAI,UAAU,GAAG,CAAC;AAC7C,2BAAmB,KAAK,OAAOA,SAAQ,EAAE;AAAA,MAC3C;AACA,UAAI,KAAK,YAAY,SAAS;AAC5B,2BAAmB,KAAK,eAAe,KAAK,WAAW,KAAK,EAAE;AAChE,UAAI,KAAK,YAAY,SAAS;AAC5B,2BAAmB,KAAK,eAAe,KAAK,WAAW,KAAK,EAAE;AAChE,WAAK,SAAS,gBAAgB,mBAAmB,KAAK,GAAG,CAAC;AAG1D,WAAK,kBAAkB;AAEvB,WAAK,WAAW;AAChB,WAAK,aAAa;AAElB,UAAI,KAAK,sCAAsC;AAC7C,aAAK,mBAAmB,MAAM;AAC9B,aAAK,qCAAqC;AAAA,MAC5C;AACA,WAAK,KAAK,OAAO;AAGjB,YAAM,iBAAiB,MAAM,KAAK,KAAK,QAAQ,QAAQ,CAAC;AACxD,WAAK,QAAQ,MAAM;AACnB,iBAAW,CAAC,EAAE,CAAC,KAAK,gBAAgB;AAGlC,qBAAa,MAAM;AACjB,cAAI;AACF,cAAE,OAAO,IAAI,MAAM,4BAA4B,CAAC;AAAA,UAClD,SAAS,GAAG;AAAA,UAEZ;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF,CAAC;AACD,SAAK,GAAG,SAAS,CAAC,QAAQ;AAGxB,UAAI,KAAK,SAAS,SAAS,UAAU,GAAG;AACtC,cAAM,MAAM,KAAK,IAAI;AACrB,cAAMD,OAAM,KAAK;AACjB,cAAMC,YAAW,KAAK,KAAK,MACvB,KAAK,KAAK,IAAI,UAAU,GAAG,CAAC,IAC5B;AACJ,aAAK,SAAS,YAAY;AAAA,UACxB,WAAW;AAAA,UACX,MAAM,KAAK,KAAK;AAAA,UAChB,KAAAD;AAAA,UACA,KAAKC;AAAA,UACL,SAAS,IAAI;AAAA,QACf,CAAC;AAID,cAAM,eAAe,MAAM,KAAK,8BAA8B;AAC9D,cAAM,SAAS;AACf,cAAM,QAAQ;AACd,cAAM,gBAAgB,eAClB,KAAK;AAAA,UACH;AAAA,UACA,KAAK;AAAA,YACH;AAAA,YACA,KAAK,wBAAwB,IACzB,KAAK,wBAAwB,IAC7B;AAAA,UACN;AAAA,QACF,IACA;AACJ,aAAK,8BAA8B;AACnC,aAAK,wBAAwB;AAC7B,aAAK,8BAA8B,KAAK;AAAA,UACtC,KAAK;AAAA,UACL,MAAM;AAAA,QACR;AACA,aAAK,SAAS,oBAAoB;AAAA,UAChC,WAAW;AAAA,UACX,MAAM,KAAK,KAAK;AAAA,UAChB,KAAAD;AAAA,UACA,KAAKC;AAAA,UACL,WAAW;AAAA,UACX,iBAAiB,KAAK;AAAA,QACxB,CAAC;AAED,aAAK,cAAc;AACnB,aAAK,WAAW;AAChB,aAAK,aAAa;AAElB,YAAI,KAAK,sCAAsC;AAC7C,eAAK,mBAAmB,MAAM;AAC9B,eAAK,qCAAqC;AAAA,QAC5C;AAAA,MACF;AACA,WAAK,KAAK,SAAS,GAAG;AAAA,IACxB,CAAC;AAGD,SAAK,GAAG,SAAS,CAAC,OAAe,SAAmB;AAClD,WAAK,SAAS,OAAO,KAAK,IAAI,IAAI;AAAA,IACpC,CAAC;AAED,UAAM,KAAK,QAAQ;AAEnB,UAAM,WAAW,KAAK,KAAK,MAAM,KAAK,KAAK,IAAI,UAAU,GAAG,CAAC,IAAI;AACjE,UAAM,qBACH,KAAK,KAAK,sBAA6C;AAC1D,UAAM,MAAM,KAAK;AACjB,UAAM,gBAAqC,MAAc,QAAQ;AACjE,SAAK;AAAA,MACH;AAAA,MACA,sBAAsB,KAAK,KAAK,IAAI,GAAG,MAAM,QAAQ,GAAG,KAAK,EAAE,GAAG,gBAAgB,WAAW,aAAa,KAAK,EAAE,QAAQ,QAAQ,uBAAuB,kBAAkB;AAAA,IAC5K;AACA,SAAK,eAAe;AACpB,SAAK,wBAAwB;AAAA,EAC/B;AAAA,EAEA,MAAM,MAAM,SAA8C;AACxD,UAAM,YAAY;AAAA,MACf,KAAK,aAAa,CAAC,KAAK,UAAU,aAAc,KAAK;AAAA,IACxD;AACA,UAAM,UAAU,SAAS,UAAU,gBAAgB,KAAK,KAAK;AAC7D,QAAI,WAAW;AACb,WAAK,mBAAmB;AAAA,QACtB,MAAM,KAAK,IAAI;AAAA,QACf;AAAA,MACF;AAAA,IACF;AAIA,QAAI;AACF,YAAM,MAAM,KAAK;AACjB,YAAM,YAAY,KAAK;AACvB,YAAM,OAAO,KAAK,KAAK;AACvB,YAAM,OAAO,KAAK,KAAK,QAAQ;AAC/B,YAAM,WAAW,KAAK,KAAK,MACvB,KAAK,KAAK,IAAI,UAAU,GAAG,CAAC,IAC5B;AACJ,WAAK,SAAS,WAAW;AAAA,QACvB;AAAA,QACA;AAAA,QACA,GAAI,cAAc,QAAQ,EAAE,KAAK,IAAI,CAAC;AAAA,QACtC,GAAI,MAAM,EAAE,IAAI,IAAI,CAAC;AAAA,QACrB,GAAI,WAAW,EAAE,KAAK,SAAS,IAAI,CAAC;AAAA,QACpC;AAAA,QACA;AAAA,QACA,iBAAiB,KAAK,kBAAkB;AAAA,QACxC,UAAU,KAAK;AAAA,QACf,YAAY,KAAK;AAAA,QACjB,SAAS,KAAK,QAAQ;AAAA,QACtB,SAAS,KAAK,QAAQ;AAAA,QACtB,oBAAoB,KAAK,mBAAmB;AAAA,MAC9C,CAAC;AAID,WAAK,QAAQ,IAAI,4BAA4B,IAAI,KAAK,MAAM,KAAK;AAC/D,cAAM,QAAQ,IAAI,MAAM,8BAA8B,EAAE;AACxD,YAAI,OAAO;AACT,gBAAM,QAAQ,MAAM,MAAM,IAAI,EAAE,MAAM,GAAG,EAAE,EAAE,KAAK,IAAI;AACtD,eAAK,SAAS,iBAAiB,KAAK;AAAA,QACtC;AAAA,MACF;AAAA,IACF,QAAQ;AAAA,IAER;AAEA,SAAK,cAAc;AACnB,SAAK,yBAAyB;AAG9B,eAAW,MAAM,MAAM,KAAK,KAAK,QAAQ,KAAK,CAAC,GAAG;AAChD,WAAK,cAAc,IAAI,OAAO;AAAA,IAChC;AAGA,QAAI,KAAK,sCAAsC;AAC7C,WAAK,mBAAmB,MAAM;AAC9B,WAAK,qCAAqC;AAAA,IAC5C;AAUA,UAAM,MAAM,KAAK;AACjB,QAAI,OAAO,CAAC,IAAI,WAAW;AACzB,UAAI;AAEF,YAAI,IAAI;AAAA,MACV,QAAQ;AAAA,MAER;AACA,UAAI;AACF,YAAI,QAAQ;AAAA,MACd,QAAQ;AAAA,MAER;AAEA,YAAM,IAAI,QAAc,CAAC,YAAY;AACnC,YAAI,IAAI,WAAW;AACjB,kBAAQ;AACR;AAAA,QACF;AACA,YAAI,KAAK,SAAS,MAAM,QAAQ,CAAC;AACjC,mBAAW,MAAM,QAAQ,GAAG,GAAG;AAAA,MACjC,CAAC;AAAA,IACH;AAGA,UAAM,MAAM,KAAK;AACjB,QAAI,KAAK;AACP,UAAI;AACF,cAAM,IAAI,MAAM;AAAA,MAClB,SAAS,GAAG;AACV,aAAK,SAAS,mBAAmB,CAAC;AAAA,MACpC;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,YAAY,OAA4B;AAC9C,UAAM,MAAM,KAAK,IAAI;AACrB,SAAK,aAAa;AAClB,SAAK,aAAa;AAAA,MAChB,MAAM;AAAA,MACN,OAAO,MAAM,OAAO;AAAA,MACpB,cAAc,MAAM,OAAO;AAAA,MAC3B,QAAQ,MAAM,OAAO;AAAA,MACrB,WAAW,MAAM,OAAO;AAAA,MACxB,YAAY,MAAM,OAAO;AAAA,IAC3B;AAEA,SAAK,UAAU,KAAK,KAAK,UAAU;AACnC,QAAI,KAAK,UAAU,SAAS,GAAI,MAAK,UAAU,MAAM;AAIrD,QAAI,KAAK,SAAS,SAAS;AACzB,UAAI,MAAM,OAAO,UAAU,GAAG;AAC5B,cAAM,IAAI,KAAK,gBAAgB,IAAI,CAAC,KAAK,EAAE,WAAW,KAAK,QAAQ,EAAE;AACrE,UAAE;AACF,YAAI,MAAM,EAAE,aAAa,KAAM;AAC7B;AAAA,YACE,KAAK;AAAA,YACL,KAAK;AAAA,YACL;AAAA,YACA,qBAAqB,EAAE,MAAM,eAAe,MAAM,OAAO,MAAM,qBAAqB,MAAM,OAAO,YAAY,kBAAkB,MAAM,OAAO,SAAS,mBAAmB,MAAM,OAAO,UAAU,gBAAgB,MAAM,KAAK,MAAM,mBAAmB,MAAM,QAAQ,MAAM;AAAA,UACzQ;AACA,YAAE,YAAY;AACd,YAAE,SAAS;AAAA,QACb;AACA,aAAK,gBAAgB,IAAI,GAAG,CAAC;AAAA,MAC/B,OAAO;AACL;AAAA,UACE,KAAK;AAAA,UACL,KAAK;AAAA,UACL;AAAA,UACA,YAAY,MAAM,OAAO,KAAK,WAAW,MAAM,OAAO,MAAM,iBAAiB,MAAM,OAAO,YAAY,cAAc,MAAM,OAAO,SAAS,eAAe,MAAM,OAAO,UAAU,YAAY,MAAM,KAAK,MAAM,eAAe,MAAM,QAAQ,MAAM,kBAAkB,MAAM,OAAO,iBAAiB,CAAC;AAAA,QACnS;AAAA,MACF;AAAA,IACF;AAMA,QACE,KAAK,cAAc,SACnB,MAAM,OAAO,UAAU,0BACvB;AACA,UAAI;AACF,cAAM,SAAS,aAAa;AAAA,UAC1B,OAAO,MAAM,OAAO;AAAA,UACpB,SAAS;AAAA,UACT,WAAW,MAAM,OAAO;AAAA,UACxB,YAAY,MAAM,OAAO;AAAA,UACzB,QAAQ,MAAM,OAAO;AAAA,UACrB,cAAc;AAAA,UACd,cAAc,MAAM,OAAO;AAAA,UAC3B,eAAe;AAAA,QACjB,CAAC;AAED,aAAK,SAAS,oBAAoB;AAAA,UAChC,QAAQ,MAAM,OAAO;AAAA,UACrB,WAAW,MAAM,OAAO;AAAA,UACxB,YAAY,MAAM,OAAO;AAAA,UACzB,cAAc,MAAM,OAAO;AAAA,QAC7B,CAAC;AACD,aAAK,SAAS;AAAA,UACZ,OAAO,MAAM,OAAO;AAAA,UACpB,cAAc;AAAA,UACd,QAAQ,MAAM,OAAO;AAAA,UACrB,WAAW,MAAM,OAAO;AAAA,UACxB,YAAY,MAAM,OAAO;AAAA,QAC3B,CAAC;AACD,aAAK,UAAU,MAAM;AACrB,aAAK,SAAS,oBAAoB;AAAA,UAChC,QAAQ,MAAM,OAAO;AAAA,UACrB,WAAW,MAAM,OAAO;AAAA,UACxB,YAAY,MAAM,OAAO;AAAA,QAC3B,CAAC;AAAA,MACH,SAAS,GAAG;AAEV,aAAK,SAAS,uBAAuB,CAAC;AAAA,MACxC;AAEA;AAAA,IACF;AAEA,QAAI,KAAK,SAAS,aAAa,UAAU,MAAM,OAAO,KAAK,GAAG;AAC5D;AAAA,QACE,KAAK;AAAA,QACL,KAAK;AAAA,QACL;AAAA,QACA,YAAY,MAAM,OAAO,KAAK,WAAW,MAAM,OAAO,MAAM,iBAAiB,MAAM,OAAO,YAAY,cAAc,MAAM,OAAO,SAAS,YAAY,MAAM,KAAK,MAAM,eAAe,MAAM,QAAQ,MAAM,kBAAkB,MAAM,OAAO,iBAAiB,CAAC;AAAA,MAC7P;AAAA,IACF;AAEA,SAAK,KAAK,SAAS,KAAK;AAExB,UAAM,MAAkB,GAAG,MAAM,OAAO,KAAK,IAAI,MAAM,OAAO,MAAM;AACpE,UAAM,UAAU,KAAK,QAAQ,IAAI,GAAG;AACpC,QAAI,SAAS;AACX,WAAK,QAAQ,OAAO,GAAG;AACvB,cAAQ,QAAQ,KAAK;AACrB;AAAA,IACF;AAIA,UAAM,oBAAoB,KAAK,mBAAmB,IAAI,MAAM,OAAO,KAAK;AACxE,QAAI,qBAAqB,kBAAkB,OAAO,GAAG;AAGnD,UAAI,kBAAkB,IAAI,MAAM,OAAO,MAAM,GAAG;AAC9C,YAAI,KAAK,SAAS,qBAAqB,MAAM,OAAO,UAAU,GAAG;AAC/D,gBAAMC,OAAM,KAAK,IAAI;AACrB,gBAAMC,OAAM,MAAM,OAAO;AACzB,gBAAM,IAAI,KAAK,iBAAiB,IAAIA,IAAG,KAAK;AAAA,YAC1C,WAAWD;AAAA,YACX,QAAQ;AAAA,UACV;AACA,YAAE;AAGF,cAAIA,OAAM,EAAE,aAAa,KAAK;AAC5B;AAAA,cACE,KAAK;AAAA,cACL,KAAK;AAAA,cACL;AAAA,cACA,mCAAmC,MAAM,OAAO,MAAM,WAAW,EAAE,MAAM,kBAAkB,MAAM,OAAO,SAAS,gBAAgB,MAAM,KAAK,MAAM,mBAAmB,MAAM,QAAQ,MAAM,sBAAsB,MAAM,OAAO,iBAAiB,CAAC;AAAA,YAChP;AACA,cAAE,YAAYA;AACd,cAAE,SAAS;AAAA,UACb;AAEA,eAAK,iBAAiB,IAAIC,MAAK,CAAC;AAAA,QAClC;AACA,aAAK,KAAK,QAAQ,KAAK;AAAA,MACzB;AACA;AAAA,IACF;AAGA,QAAI,MAAM,OAAO,UAAU,4BAA4B;AACrD,UAAI;AAEF,cAAM,MAAM,KAAK;AAAA,UACf,MAAM;AAAA,UACN,MAAM,OAAO;AAAA,UACb,KAAK;AAAA,QACP;AACA,YAAI,OAAO,IAAI,WAAW,OAAO,GAAG;AAElC,eAAK,KAAK,eAAe,GAAG;AAAA,QAQ9B,OAAO;AACL,eAAK,SAAS,oCAAoC;AAAA,YAChD,SAAS,MAAM,KAAK;AAAA,YACpB,YAAY,KAAK,UAAU,GAAG,GAAG;AAAA,UACnC,CAAC;AAAA,QACH;AAAA,MACF,SAAS,GAAG;AACV,aAAK,SAAS,2BAA2B,CAAC;AAAA,MAC5C;AAAA,IACF;AAGA,SAAK,KAAK,QAAQ,KAAK;AAGvB,QAAI,MAAM,OAAO,UAAU,IAAI;AAC7B,UAAI;AACF,cAAM,MAAM,KAAK;AACjB,cAAM,cAAc,MAAM,qBAAqB,GAAG,KAAK;AACvD,cAAM,SAAS,KAAK,YAAY,KAAK;AACrC,mBAAW,SAAS,QAAQ;AAC1B;AAAA,YACE,KAAK;AAAA,YACL,KAAK;AAAA,YACL;AAAA,YACA,4BAA4B,MAAM,OAAO,MAAM,cAAc,MAAM,OAAO,SAAS,SAAS,MAAM,IAAI,iBAAiB,MAAM,OAAO,MACjI,MAAM,SAAS,OACZ,WAAY,MAAM,IAAY,QAAQ,SAAS,aAAc,MAAM,IAAY,YAAY,EAAE,KAC7F,OACH,MAAM,SAAS,WACZ,WAAY,MAAM,QAAgB,UAAU,EAAE,KAC9C;AAAA,UACR;AACA,eAAK,KAAK,SAAS,KAAK;AAAA,QAC1B;AAAA,MACF,SAAS,OAAO;AACd,aAAK,SAAS,qBAAqB,KAAK;AAAA,MAC1C;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,qBAAqB,OAAe,QAAsB;AACxD,QAAI,CAAC,KAAK,mBAAmB,IAAI,KAAK,GAAG;AACvC,WAAK,mBAAmB,IAAI,OAAO,oBAAI,IAAI,CAAC;AAAA,IAC9C;AACA,SAAK,mBAAmB,IAAI,KAAK,EAAG,IAAI,MAAM;AAC9C,QAAI,UAAU,KAAK,CAAC,KAAK,iBAAiB,IAAI,MAAM,GAAG;AACrD,WAAK,iBAAiB,IAAI,QAAQ,EAAE,WAAW,KAAK,IAAI,GAAG,QAAQ,EAAE,CAAC;AAAA,IACxE;AAGA,QAAI,KAAK,cAAc,MAAO,MAAK,iBAAiB;AAGpD,SAAK,qCAAqC;AAG1C,SAAK,wBAAwB;AAAA,EAC/B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,uBAAuB,OAAe,QAAuB;AAC3D,UAAM,oBAAoB,KAAK,mBAAmB,IAAI,KAAK;AAC3D,QAAI,CAAC,kBAAmB;AAExB,QAAI,WAAW,QAAW;AACxB,wBAAkB,OAAO,MAAM;AAC/B,UAAI,kBAAkB,SAAS,GAAG;AAChC,aAAK,mBAAmB,OAAO,KAAK;AAAA,MACtC;AACA,UAAI,UAAU,EAAG,MAAK,iBAAiB,OAAO,MAAM;AAAA,IACtD,OAAO;AACL,WAAK,mBAAmB,OAAO,KAAK;AACpC,UAAI,UAAU,EAAG,MAAK,iBAAiB,MAAM;AAAA,IAC/C;AAEA,QAAI,KAAK,cAAc,MAAO,MAAK,iBAAiB;AAGpD,SAAK,qCAAqC;AAG1C,SAAK,wBAAwB;AAAA,EAC/B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,YAAY,OAAsC;AACxD,UAAM,OAAO,MAAM;AACnB,QAAI,KAAK,WAAW,EAAG,QAAO,CAAC;AAE/B,UAAM,MAAM,KAAK;AACjB,UAAM,iBAAiB,MACnB,wBAAwB,GAAG,KAC3B;AAEJ,UAAM,MAAM,KAAK,cAAc,MAAM,MAAM,OAAO,WAAW,KAAK,GAAG;AACrE,QAAI,CAAC,OAAO,CAAC,IAAI,WAAW,OAAO,EAAG,QAAO,CAAC;AAE9C,QAAI,KAAK,SAAS,aAAa;AAC7B,YAAM,UAAU,IAAI,SAAS,MAAM,GAAG,IAAI,MAAM,GAAG,GAAG,CAAC,QAAQ;AAC/D;AAAA,QACE,KAAK;AAAA,QACL,KAAK;AAAA,QACL;AAAA,QACA,YAAY,MAAM,OAAO,KAAK,WAAW,MAAM,OAAO,MAAM,iBAAiB,MAAM,OAAO,YAAY,cAAc,MAAM,OAAO,SAAS,QAAQ,KAAK,UAAU,OAAO,CAAC;AAAA,MAC3K;AAAA,IACF;AAGA,UAAM,oBAAoB,MAAM,OAAO;AACvC,UAAM,kBACJ,sBAAsB,MAAM,IAAI,KAAK,IAAI,GAAG,oBAAoB,CAAC;AAEnE,UAAM,MAAM,KAAK,IAAI;AAGrB,QAAI,IAAI,SAAS,iBAAiB,GAAG;AACnC,YAAMC,OAAsB,CAAC;AAC7B,YAAM,oBAAoB,IAAI;AAAA,QAC5B;AAAA,MACF;AACA,iBAAW,SAAS,mBAAmB;AACrC,cAAM,WAAW,MAAM,CAAC,KAAK;AAC7B,cAAM,cAAc,WAAW,UAAU,WAAW;AACpD,cAAM,UACJ,gBAAgB,SAAY,OAAO,WAAW,IAAI;AACpD,cAAMC,WAAU,WAAW,UAAU,QAAQ,KAAK,IAAI,KAAK;AAC3D,cAAMC,eAAcD,QAAO,YAAY;AAGvC,cAAME,cACJ,WAAW,UAAU,QAAQ,KAC7B,WAAW,UAAU,QAAQ,KAC7B,WAAW,UAAU,QAAQ,KAC7B,IACA,KAAK;AAIP,cAAMC,iBACJ,WAAW,UAAU,WAAW,KAChC,WAAW,UAAU,WAAW,KAChC,IACA,KAAK;AACP,cAAMC,gBAAeD,cAAa,SAC9B,OAAOA,aAAY,IACnB;AACJ,cAAME,kBAAiB,OAAO,SAASD,aAAsB,IACxDA,gBACD;AAEJ,YAAI,KAAK,SAAS,aAAa;AAC7B;AAAA,YACE,KAAK;AAAA,YACL,KAAK;AAAA,YACL;AAAA,YACA,sBAAsB,OAAO,WAAW,KAAK,UAAUJ,OAAM,CAAC,WAAW,KAAK,UAAUE,UAAS,CAAC;AAAA,UACpG;AAAA,QACF;AASA,cAAMI,eAAcN,QAAO,YAAY;AACvC,cAAMO,gBAAeD,aAClB,MAAM,GAAG,EACT,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EACnB,OAAO,CAAC,MAAM,EAAE,SAAS,CAAC;AAE7B,cAAME,iBAAgBD,cAAa,SAAS,SAAS;AACrD,cAAME,kBACJF,cAAa,SAAS,IAAI,KAAKA,cAAa,SAAS,UAAU;AAGjE,cAAMG,yBAAwBH,cAAa;AAAA,UACzC,CAAC,MACC,MAAM,UAAU,MAAM,aAAa,MAAM,QAAQ,MAAM;AAAA,QAC3D;AACA,cAAMI,kBAAiBT,aACnBA,WACG,MAAM,GAAG,EACT,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EACnB,KAAK,CAAC,MAAM,EAAE,SAAS,KAAK,EAAE,YAAY,MAAM,MAAM,IACzD;AAEJ,cAAMU,UAASX,aAAY,SAAS,IAAI;AACxC,cAAMY,gBACJF,oBAAmBC,UAAS,UAAU,SACtC,YAAY;AAEd,cAAME,QAAO,KAAK,gBAAgB,IAAI,OAAO;AAC7C,cAAMC,QAAO;AAAA,UACX,cAAcL;AAAA,UACd,eAAAF;AAAA,UACA,gBAAAC;AAAA,UACA,aAAAI;AAAA,UACA,QAAAD;AAAA,UACA,aAAaP;AAAA,QACf;AAGA,YAAI,CAACS,OAAM;AACT,eAAK,gBAAgB,IAAI,SAASC,KAAI;AAItC,cACEA,MAAK,gBAAgB,UACrBA,MAAK,eACLA,MAAK,gBAAgB,GACrB;AACA,kBAAM,IAAIA,MAAK;AACf,kBAAM,YAGF;AAAA,cACF,QAAQ;AAAA,cACR,QAAQ;AAAA,cACR,OAAO;AAAA,cACP,SAAS;AAAA,cACT,KAAK;AAAA,cACL,SAAS;AAAA,cACT,KAAK;AAAA,cACL,KAAK;AAAA,cACL,KAAK;AAAA,cACL,MAAM;AAAA,cACN,SAAS;AAAA,YACX;AACA,YAAAhB,KAAI,KAAK;AAAA,cACP;AAAA,cACA,MAAM;AAAA,cACN,IAAI;AAAA,gBACF;AAAA,gBACA,MAAM,UAAU,CAAC,KAAK;AAAA,gBACtB,UAAU;AAAA,gBACV,WAAW;AAAA,cACb;AAAA,cACA,WAAW;AAAA,YACb,CAAC;AAAA,UACH;AAGA,cAAIgB,MAAK;AACP,YAAAhB,KAAI,KAAK,EAAE,SAAS,MAAM,WAAW,WAAW,IAAI,CAAC;AACvD,cAAIgB,MAAK;AACP,YAAAhB,KAAI,KAAK,EAAE,SAAS,MAAM,YAAY,WAAW,IAAI,CAAC;AAExD;AAAA,QACF;AAGA,YAAI,CAACe,MAAK,gBAAgBC,MAAK,cAAc;AAC3C,gBAAM,SAASd,aAAY,SAAS,IAAI,IACpC,OACAA,aAAY,SAAS,KAAK,KAAKA,aAAY,SAAS,OAAO,IACzD,QACA;AACN,UAAAF,KAAI,KAAK;AAAA,YACP;AAAA,YACA,MAAM;AAAA,YACN,QAAQ,EAAE,SAAS,OAAO,MAAM,WAAW,KAAK,OAAO;AAAA,YACvD,WAAW;AAAA,UACb,CAAC;AAAA,QACH;AAMA,cAAMiB,iBAAgBF,MAAK,gBAAgBC,MAAK;AAChD,cAAME,sBACJF,MAAK,gBAAgB,UACrBA,MAAK,gBAAgB,KACrBA,MAAK,gBAAgBD,MAAK;AAE5B,YACGE,kBAAiBD,MAAK,gBAAgB,UACtCE,uBAAsBF,MAAK,gBAAgB,QAC5C;AACA,gBAAM,IAAIA,MAAK;AACf,gBAAM,YAGF;AAAA,YACF,QAAQ;AAAA,YACR,QAAQ;AAAA,YACR,OAAO;AAAA,YACP,SAAS;AAAA,YACT,KAAK;AAAA,YACL,SAAS;AAAA,YACT,KAAK;AAAA,YACL,KAAK;AAAA,YACL,KAAK;AAAA,YACL,MAAM;AAAA,YACN,SAAS;AAAA,UACX;AACA,UAAAhB,KAAI,KAAK;AAAA,YACP;AAAA,YACA,MAAM;AAAA,YACN,IAAI;AAAA,cACF;AAAA,cACA,MAAM,UAAU,CAAC,KAAK;AAAA,cACtB,UAAU;AAAA,cACV,WAAW;AAAA,YACb;AAAA,YACA,WAAW;AAAA,UACb,CAAC;AAAA,QACH;AAGA,YAAI,CAACe,MAAK,iBAAiBC,MAAK,eAAe;AAC7C,UAAAhB,KAAI,KAAK,EAAE,SAAS,MAAM,WAAW,WAAW,IAAI,CAAC;AAAA,QACvD;AAEA,YAAI,CAACe,MAAK,kBAAkBC,MAAK,gBAAgB;AAC/C,UAAAhB,KAAI,KAAK,EAAE,SAAS,MAAM,YAAY,WAAW,IAAI,CAAC;AAAA,QACxD;AAGA,aAAK,gBAAgB,IAAI,SAASgB,KAAI;AAAA,MACxC;AACA,aAAOhB;AAAA,IACT;AAGA,UAAM,aAAa,IAAI,MAAM,mCAAmC;AAChE,QAAI,CAAC,WAAY,QAAO,CAAC;AAEzB,UAAM,WAAW,WAAW,CAAC,KAAK;AAClC,UAAM,UAAU,WAAW,UAAU,QAAQ,KAAK,IAAI,KAAK;AAC3D,UAAM,cAAc,OAAO,YAAY;AACvC,UAAM,aACJ,WAAW,UAAU,QAAQ,KAC7B,WAAW,UAAU,QAAQ,KAC7B,WAAW,UAAU,QAAQ,KAC7B,IACA,KAAK;AAEP,UAAM,gBACJ,WAAW,UAAU,WAAW,KAChC,WAAW,UAAU,WAAW,KAChC,IACA,KAAK;AACP,UAAM,eAAe,aAAa,SAAS,OAAO,YAAY,IAAI;AAClE,UAAM,iBAAiB,OAAO,SAAS,YAAsB,IACxD,eACD;AAEJ,UAAM,MAAsB,CAAC;AAE7B,UAAM,cAAc,OAAO,YAAY;AACvC,UAAM,eAAe,YAClB,MAAM,GAAG,EACT,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EACnB,OAAO,CAAC,MAAM,EAAE,SAAS,CAAC;AAE7B,UAAM,gBAAgB,aAAa,SAAS,SAAS;AACrD,UAAM,iBACJ,aAAa,SAAS,IAAI,KAAK,aAAa,SAAS,UAAU;AACjE,UAAM,wBAAwB,aAAa;AAAA,MACzC,CAAC,MAAM,MAAM,UAAU,MAAM,aAAa,MAAM,QAAQ,MAAM;AAAA,IAChE;AAEA,UAAM,iBAAiB,YACnB,UACG,MAAM,GAAG,EACT,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EACnB,KAAK,CAAC,MAAM,EAAE,SAAS,KAAK,EAAE,YAAY,MAAM,MAAM,IACzD;AACJ,UAAM,SAAS,YAAY,SAAS,IAAI;AACxC,UAAM,eACJ,mBAAmB,SAAS,UAAU,SACtC,YAAY;AAEd,UAAM,OAAO,KAAK,gBAAgB,IAAI,eAAe;AACrD,UAAM,OAAO;AAAA,MACX,cAAc;AAAA,MACd;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,aAAa;AAAA,IACf;AAEA,QAAI,CAAC,MAAM;AACT,WAAK,gBAAgB,IAAI,iBAAiB,IAAI;AAE9C,UACE,KAAK,gBAAgB,UACrB,KAAK,eACL,KAAK,gBAAgB,GACrB;AACA,cAAM,IAAI,KAAK;AACf,cAAM,YAGF;AAAA,UACF,QAAQ;AAAA,UACR,QAAQ;AAAA,UACR,OAAO;AAAA,UACP,SAAS;AAAA,UACT,KAAK;AAAA,UACL,SAAS;AAAA,UACT,KAAK;AAAA,UACL,KAAK;AAAA,UACL,KAAK;AAAA,UACL,MAAM;AAAA,UACN,SAAS;AAAA,QACX;AACA,YAAI,KAAK;AAAA,UACP,SAAS;AAAA,UACT,MAAM;AAAA,UACN,IAAI;AAAA,YACF,SAAS;AAAA,YACT,MAAM,UAAU,CAAC,KAAK;AAAA,YACtB,UAAU;AAAA,YACV,WAAW;AAAA,UACb;AAAA,UACA,WAAW;AAAA,QACb,CAAC;AAAA,MACH;AAEA,UAAI,KAAK;AACP,YAAI,KAAK,EAAE,SAAS,iBAAiB,MAAM,WAAW,WAAW,IAAI,CAAC;AACxE,UAAI,KAAK;AACP,YAAI,KAAK;AAAA,UACP,SAAS;AAAA,UACT,MAAM;AAAA,UACN,WAAW;AAAA,QACb,CAAC;AAEH,aAAO;AAAA,IACT;AAEA,QAAI,CAAC,KAAK,gBAAgB,KAAK,cAAc;AAC3C,YAAM,SAAS,YAAY,SAAS,IAAI,IACpC,OACA,YAAY,SAAS,KAAK,KAAK,YAAY,SAAS,OAAO,IACzD,QACA;AACN,UAAI,KAAK;AAAA,QACP,SAAS;AAAA,QACT,MAAM;AAAA,QACN,QAAQ;AAAA,UACN,SAAS;AAAA,UACT,OAAO;AAAA,UACP,WAAW;AAAA,UACX;AAAA,QACF;AAAA,QACA,WAAW;AAAA,MACb,CAAC;AAAA,IACH;AAEA,UAAM,gBAAgB,KAAK,gBAAgB,KAAK;AAChD,UAAM,qBACJ,KAAK,gBAAgB,UACrB,KAAK,gBAAgB,KACrB,KAAK,gBAAgB,KAAK;AAE5B,QACG,iBAAiB,KAAK,gBAAgB,UACtC,sBAAsB,KAAK,gBAAgB,QAC5C;AACA,YAAM,IAAI,KAAK;AACf,YAAM,YAGF;AAAA,QACF,QAAQ;AAAA,QACR,QAAQ;AAAA,QACR,OAAO;AAAA,QACP,SAAS;AAAA,QACT,KAAK;AAAA,QACL,SAAS;AAAA,QACT,KAAK;AAAA,QACL,KAAK;AAAA,QACL,KAAK;AAAA,QACL,MAAM;AAAA,QACN,SAAS;AAAA,MACX;AACA,UAAI,KAAK;AAAA,QACP,SAAS;AAAA,QACT,MAAM;AAAA,QACN,IAAI;AAAA,UACF,SAAS;AAAA,UACT,MAAM,UAAU,CAAC,KAAK;AAAA,UACtB,UAAU;AAAA,UACV,WAAW;AAAA,QACb;AAAA,QACA,WAAW;AAAA,MACb,CAAC;AAAA,IACH;AAEA,QAAI,CAAC,KAAK,iBAAiB,KAAK;AAC9B,UAAI,KAAK,EAAE,SAAS,iBAAiB,MAAM,WAAW,WAAW,IAAI,CAAC;AACxE,QAAI,CAAC,KAAK,kBAAkB,KAAK;AAC/B,UAAI,KAAK,EAAE,SAAS,iBAAiB,MAAM,YAAY,WAAW,IAAI,CAAC;AAEzE,SAAK,gBAAgB,IAAI,iBAAiB,IAAI;AAE9C,WAAO;AAAA,EACT;AAAA,EAEQ,aAAqB;AAC3B,SAAK,SAAU,KAAK,SAAS,IAAK;AAClC,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASO,oBAA4B;AACjC,WAAO,KAAK,WAAW;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOO,iBAAyB;AAC9B,WAAQ,KAAK,SAAS,IAAK;AAAA,EAC7B;AAAA,EAEQ,gBAA4B;AAClC,QAAI,KAAK,cAAc;AACrB,YAAM,IAAI,MAAM,oDAAoD;AACtE,QAAI,CAAC,KAAK,aAAa,KAAK,UAAU;AACpC,YAAM,IAAI,MAAM,sCAAsC;AACxD,WAAO,KAAK;AAAA,EACd;AAAA,EAEQ,UAAU,MAAoB;AACpC,SAAK,aAAa,KAAK,IAAI;AAC3B,QAAI,KAAK,cAAc,OAAO;AAC5B,WAAK,cAAc,EAAE,MAAM,IAAI;AAC/B;AAAA,IACF;AACA,QAAI,CAAC,KAAK;AACR,YAAM,IAAI,MAAM,sCAAsC;AACxD,SAAK,UAAU,MAAM,IAAI;AAAA,EAC3B;AAAA,EAEQ,cACN,QACA,YACA,WACA,KACQ;AACR,UAAM,SAAS,OAAO,KAAK,QAAQ,MAAM;AACzC,UAAM,aAAa,OAAO,KAAK,YAAY,MAAM;AAEjD,QAAI,IAAI,SAAS,OAAQ,QAAO,OAAO,OAAO,CAAC,QAAQ,UAAU,CAAC;AAClE,QAAI,IAAI,SAAS;AACf,aAAO,OAAO,OAAO;AAAA,QACnB,UAAU,QAAQ,SAAS;AAAA,QAC3B,UAAU,YAAY,SAAS;AAAA,MACjC,CAAC;AACH,QAAI,IAAI,SAAS,SAAS,IAAI,SAAS;AACrC,aAAO,OAAO,OAAO;AAAA,QACnB,WAAW,QAAQ,IAAI,GAAG;AAAA,QAC1B,WAAW,YAAY,IAAI,GAAG;AAAA,MAChC,CAAC;AAEH,WAAO,OAAO,OAAO,CAAC,QAAQ,UAAU,CAAC;AAAA,EAC3C;AAAA,EAEQ,iBACN,QACA,SACA,WACA,KACQ;AACR,UAAM,SAAS,OAAO,KAAK,QAAQ,MAAM;AAGzC,QAAI,IAAI,SAAS,OAAQ,QAAO,OAAO,OAAO,CAAC,QAAQ,OAAO,CAAC;AAC/D,QAAI,IAAI,SAAS;AACf,aAAO,OAAO,OAAO,CAAC,UAAU,QAAQ,SAAS,GAAG,OAAO,CAAC;AAC9D,QAAI,IAAI,SAAS,SAAS,IAAI,SAAS;AACrC,aAAO,OAAO,OAAO,CAAC,WAAW,QAAQ,IAAI,GAAG,GAAG,OAAO,CAAC;AAC7D,WAAO,OAAO,OAAO,CAAC,QAAQ,OAAO,CAAC;AAAA,EACxC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAM,yBAAyB,QAab;AAChB,UAAM,WAAW,OAAO,aAAa;AACrC,QAAI,CAAC;AACH,WAAK,kBAAkB,kCAAkC,OAAO,KAAK,EAAE;AACzE,UAAM,KAAK,QAAQ;AACnB,QAAI,CAAC,SAAU,MAAK,wBAAwB;AAE5C,UAAM,UAAU,OAAO,WAAW,KAAK,KAAK,WAAW;AACvD,UAAM,YACJ,OAAO,sBACN,OAAO,WAAW,OAAO,KAAK,gBAAgB,UAAU;AAE3D,UAAM,SAAS,KAAK,WAAW;AAC/B,UAAM,QAAQ,OAAO;AAErB,UAAM,SAAS,OAAO,gBAAgB,wBAAwB,OAAO;AACrE,UAAM,gBAAgB,OAAO,WAAW,QAAQ,MAAM;AACtD,UAAM,UAAU,gBAAgB,OAAO,QAAQ;AAE/C,UAAM,eAAe,OAAO,gBAAgB;AAE5C,UAAM,SAAS,aAAa;AAAA,MAC1B;AAAA,MACA;AAAA,MACA;AAAA,MACA,YAAY,OAAO,cAAc;AAAA,MACjC;AAAA,MACA,cAAc;AAAA,MACd;AAAA,MACA;AAAA,IACF,CAAC;AAED,UAAM,MAAM,OAAO,cAAc,KAAK;AACtC,UAAM,YAAY,KAAK;AAAA,MACrB;AAAA,MACA,OAAO;AAAA,MACP;AAAA,MACA;AAAA,IACF;AACA,UAAM,OAAO,OAAO,OAAO,CAAC,QAAQ,SAAS,CAAC;AAE9C,SAAK,SAAS,MAAM;AAAA,MAClB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,eAAe;AAAA,IACjB,CAAC;AACD,QAAI,KAAK,SAAS,aAAa,UAAU,KAAK,GAAG;AAC/C;AAAA,QACE,KAAK;AAAA,QACL,KAAK;AAAA,QACL;AAAA,QACA,YAAY,KAAK,WAAW,MAAM,cAAc,SAAS,eAAe,OAAO,cAAc,CAAC,YAAY,aAAa,SAAS,EAAE,CAAC,YAAY,OAAO,kBAAkB,aAAa,qBAAqB,OAAO,QAAQ,MAAM;AAAA,MACjO;AAAA,IACF;AACA,SAAK,SAAS;AAAA,MACZ;AAAA,MACA,cAAc;AAAA,MACd;AAAA,MACA;AAAA,MACA,YAAY,OAAO,cAAc;AAAA,IACnC,CAAC;AACD,SAAK,UAAU,IAAI;AAEnB,QAAI,CAAC,SAAU,MAAK,wBAAwB;AAAA,EAC9C;AAAA,EAEA,cACE,KACA,WACA,WACQ;AACR,UAAM,YAAY,CAAC,MAAc,EAAE,SAAS,MAAM;AAElD,UAAM,QAAQ,CAAC,QAAgD;AAC7D,UAAI;AACJ,UAAI;AACF,YAAI,IAAI,SAAS,OAAQ,OAAM;AAAA,iBACtB,IAAI,SAAS,KAAM,OAAM,UAAU,KAAK,SAAS;AAAA,YACrD,OAAM,WAAW,KAAK,IAAI,GAAG;AAAA,MACpC,QAAQ;AACN,eAAO;AAAA,MACT;AACA,YAAM,IAAI,UAAU,GAAG;AACvB,aAAO,EAAE,WAAW,OAAO,IAAI,IAAI;AAAA,IACrC;AAEA,WACE,MAAM,SAAS,MACd,UAAU,SAAS,OAAO,MAAM,EAAE,MAAM,KAAK,CAAC,IAAI,WACnD,MAAM,EAAE,MAAM,OAAO,CAAC,KACtB,UAAU,GAAG;AAAA,EAEjB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,QAAQ,QAiBM;AAClB,UAAM,WAAW,OAAO,aAAa;AACrC,QAAI,CAAC,SAAU,MAAK,kBAAkB,iBAAiB,OAAO,KAAK,EAAE;AACrE,UAAM,KAAK,QAAQ;AACnB,QAAI,CAAC,SAAU,MAAK,wBAAwB;AAE5C,UAAM,UAAU,OAAO,WAAW,KAAK,KAAK,WAAW;AACvD,UAAM,YACJ,OAAO,sBACN,OAAO,WAAW,OAAO,KAAK,gBAAgB,UAAU;AAE3D,UAAM,SAAS,KAAK,WAAW;AAC/B,UAAM,QAAQ,OAAO;AAErB,UAAM,SACJ,OAAO,iBACN,OAAO,WAAW,OAAO,yBAAyB,OAAO,IAAI;AAChE,UAAM,aAAa,OAAO,cAAc;AAExC,UAAM,eAAe,OAAO,gBAAgB;AAC5C,UAAM,gBAAgB,OAAO,WAAW,QAAQ,MAAM;AACtD,UAAM,UAAU,gBAAgB,OAAO,WAAW,YAAY,MAAM;AAEpE,UAAM,SAAS,aAAa;AAAA,MAC1B;AAAA,MACA;AAAA,MACA;AAAA,MACA,YAAY,OAAO,cAAc;AAAA,MACjC;AAAA,MACA,cAAc;AAAA,MACd;AAAA,MACA;AAAA,IACF,CAAC;AACD,UAAM,aAAyB,GAAG,KAAK,IAAI,MAAM;AAEjD,UAAM,MAAM,OAAO,cAAc,KAAK;AACtC,UAAM,YAAY,KAAK,cAAc,QAAQ,YAAY,WAAW,GAAG;AACvE,UAAM,OAAO,OAAO,OAAO,CAAC,QAAQ,SAAS,CAAC;AAE9C,UAAM,YAAY,OAAO,aAAa;AACtC,QAAI;AACJ,UAAM,eAAe,IAAI,QAAuB,CAAC,SAAS,WAAW;AACnE,iBAAW;AACX,YAAM,IAAI,WAAW,MAAM;AACzB,aAAK,QAAQ,OAAO,UAAU;AAC9B,eAAO,IAAI,MAAM,0BAA0B,KAAK,WAAW,MAAM,EAAE,CAAC;AAAA,MACtE,GAAG,SAAS;AACZ,WAAK,QAAQ,IAAI,YAAY;AAAA,QAC3B,SAAS,CAAC,MAAM;AACd,uBAAa,CAAC;AACd,kBAAQ,CAAC;AAAA,QACX;AAAA,QACA,QAAQ,CAAC,MAAM;AACb,uBAAa,CAAC;AACd,iBAAO,CAAC;AAAA,QACV;AAAA,MACF,CAAC;AAAA,IACH,CAAC;AAKD,iBAAa,MAAM,MAAM;AAAA,IAGzB,CAAC;AAED,SAAK,SAAS,MAAM,EAAE,OAAO,QAAQ,WAAW,cAAc,QAAQ,CAAC;AACvE,QAAI,KAAK,SAAS,sBAAsB,UAAU,KAAK,UAAU,IAAI;AACnE;AAAA,QACE,KAAK;AAAA,QACL,KAAK;AAAA,QACL;AAAA,QACA,YAAY,KAAK,WAAW,MAAM,cAAc,SAAS,eAAe,OAAO,cAAc,CAAC,YAAY,aAAa,SAAS,EAAE,CAAC,YAAY,OAAO,kBAAkB,aAAa;AAAA,MACvL;AAAA,IACF;AACA,QAAI,KAAK,SAAS,aAAa,UAAU,KAAK,GAAG;AAC/C;AAAA,QACE,KAAK;AAAA,QACL,KAAK;AAAA,QACL;AAAA,QACA,YAAY,KAAK,WAAW,MAAM,cAAc,SAAS,eAAe,OAAO,cAAc,CAAC,YAAY,aAAa,SAAS,EAAE,CAAC,YAAY,OAAO,kBAAkB,aAAa;AAAA,MACvL;AAAA,IACF;AACA,SAAK,SAAS;AAAA,MACZ;AAAA,MACA,cAAc;AAAA,MACd;AAAA,MACA;AAAA,MACA,YAAY,OAAO,cAAc;AAAA,IACnC,CAAC;AACD,SAAK,UAAU,IAAI;AAEnB,UAAM,QAAQ,MAAM;AACpB,SAAK,SAAS,MAAM;AAAA,MAClB,OAAO,MAAM,OAAO;AAAA,MACpB,cAAc,MAAM,OAAO;AAAA,MAC3B,QAAQ,MAAM,OAAO;AAAA,IACvB,CAAC;AACD,QAAI,KAAK,SAAS,sBAAsB,UAAU,KAAK,UAAU,IAAI;AACnE;AAAA,QACE,KAAK;AAAA,QACL,KAAK;AAAA,QACL;AAAA,QACA,YAAY,MAAM,OAAO,KAAK,WAAW,MAAM,OAAO,MAAM,iBAAiB,MAAM,OAAO,YAAY,cAAc,MAAM,OAAO,SAAS,YAAY,MAAM,KAAK,MAAM,eAAe,MAAM,QAAQ,MAAM,kBAAkB,MAAM,OAAO,iBAAiB,CAAC;AAAA,MAC7P;AAAA,IACF;AACA,QAAI,KAAK,SAAS,aAAa,UAAU,KAAK,GAAG;AAC/C;AAAA,QACE,KAAK;AAAA,QACL,KAAK;AAAA,QACL;AAAA,QACA,YAAY,MAAM,OAAO,KAAK,WAAW,MAAM,OAAO,MAAM,iBAAiB,MAAM,OAAO,YAAY,cAAc,MAAM,OAAO,SAAS,YAAY,MAAM,KAAK,MAAM,eAAe,MAAM,QAAQ,MAAM,kBAAkB,MAAM,OAAO,iBAAiB,CAAC;AAAA,MAC7P;AAAA,IACF;AAIA,QAAI,MAAM,OAAO,iBAAiB,KAAK;AAErC,YAAMmB,QAAO,MAAM;AACnB,UAAIA,MAAK,WAAW,GAAG;AAErB,cAAM,IAAI;AAAA,UACR;AAAA,QACF;AAAA,MACF;AAAA,IAEF;AAGA,UAAM,OAAO,MAAM;AACnB,QAAI,KAAK,WAAW,GAAG;AACrB,UAAI,CAAC,SAAU,MAAK,wBAAwB;AAC5C,aAAO;AAAA,IACT;AAIA,UAAM,MAAM,KAAK,cAAc,MAAM,MAAM,OAAO,WAAW,GAAG;AAChE,QAAI,CAAC,SAAU,MAAK,wBAAwB;AAC5C,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,UAAU,QAeW;AACzB,UAAM,WAAW,OAAO,aAAa;AACrC,QAAI,CAAC,SAAU,MAAK,kBAAkB,mBAAmB,OAAO,KAAK,EAAE;AACvE,UAAM,KAAK,QAAQ;AACnB,QAAI,CAAC,SAAU,MAAK,wBAAwB;AAE5C,UAAM,UAAU,OAAO,WAAW,KAAK,KAAK,WAAW;AACvD,UAAM,YACJ,OAAO,sBACN,OAAO,WAAW,OAAO,KAAK,gBAAgB,UAAU;AAE3D,UAAM,SAAS,OAAO,kBAAkB,KAAK,WAAW;AACxD,UAAM,QAAQ,OAAO;AAErB,UAAM,SACJ,OAAO,iBACN,OAAO,WAAW,OAAO,yBAAyB,OAAO,IAAI;AAChE,UAAM,aAAa,OAAO,cAAc;AAExC,UAAM,eAAe,OAAO,gBAAgB;AAC5C,UAAM,gBAAgB,OAAO,WAAW,QAAQ,MAAM;AACtD,UAAM,UAAU,gBAAgB,OAAO,WAAW,YAAY,MAAM;AAEpE,UAAM,SAAS,aAAa;AAAA,MAC1B;AAAA,MACA;AAAA,MACA;AAAA,MACA,YAAY,OAAO,cAAc;AAAA,MACjC;AAAA,MACA,cAAc;AAAA,MACd;AAAA,MACA;AAAA,IACF,CAAC;AACD,UAAM,aAAyB,GAAG,KAAK,IAAI,MAAM;AAEjD,UAAM,MAAM,OAAO,cAAc,KAAK;AACtC,UAAM,YAAY,KAAK,cAAc,QAAQ,YAAY,WAAW,GAAG;AACvE,UAAM,OAAO,OAAO,OAAO,CAAC,QAAQ,SAAS,CAAC;AAE9C,UAAM,YAAY,OAAO,aAAa;AACtC,QAAI;AACJ,UAAM,eAAe,IAAI,QAAuB,CAAC,SAAS,WAAW;AACnE,iBAAW;AACX,YAAM,IAAI,WAAW,MAAM;AACzB,aAAK,QAAQ,OAAO,UAAU;AAC9B,eAAO,IAAI,MAAM,0BAA0B,KAAK,WAAW,MAAM,EAAE,CAAC;AAAA,MACtE,GAAG,SAAS;AACZ,WAAK,QAAQ,IAAI,YAAY;AAAA,QAC3B,SAAS,CAAC,MAAM;AACd,uBAAa,CAAC;AACd,kBAAQ,CAAC;AAAA,QACX;AAAA,QACA,QAAQ,CAAC,MAAM;AACb,uBAAa,CAAC;AACd,iBAAO,CAAC;AAAA,QACV;AAAA,MACF,CAAC;AAAA,IACH,CAAC;AAKD,iBAAa,MAAM,MAAM;AAAA,IAGzB,CAAC;AAED,SAAK,SAAS,MAAM,EAAE,OAAO,QAAQ,WAAW,cAAc,QAAQ,CAAC;AACvE,QAAI,gBAAe,gBAAgB,IAAI,KAAK,GAAG;AAC7C;AAAA,QACE,KAAK;AAAA,QACL,KAAK;AAAA,QACL;AAAA,QACA,sBAAsB,KAAK,WAAW,MAAM,cAAc,SAAS,eAAe,OAAO,cAAc,CAAC,YAAY,OAAO;AAAA,MAC7H;AAAA,IACF;AACA,QAAI,KAAK,SAAS,sBAAsB,UAAU,KAAK,UAAU,IAAI;AACnE;AAAA,QACE,KAAK;AAAA,QACL,KAAK;AAAA,QACL;AAAA,QACA,YAAY,KAAK,WAAW,MAAM,cAAc,SAAS,eAAe,OAAO,cAAc,CAAC,YAAY,aAAa,SAAS,EAAE,CAAC,YAAY,OAAO,kBAAkB,aAAa;AAAA,MACvL;AAAA,IACF;AACA,QAAI,KAAK,SAAS,aAAa,UAAU,KAAK,GAAG;AAC/C;AAAA,QACE,KAAK;AAAA,QACL,KAAK;AAAA,QACL;AAAA,QACA,YAAY,KAAK,WAAW,MAAM,cAAc,SAAS,eAAe,OAAO,cAAc,CAAC,YAAY,aAAa,SAAS,EAAE,CAAC,YAAY,OAAO,kBAAkB,aAAa;AAAA,MACvL;AAAA,IACF;AACA,SAAK,SAAS;AAAA,MACZ;AAAA,MACA,cAAc;AAAA,MACd;AAAA,MACA;AAAA,MACA,YAAY,OAAO,cAAc;AAAA,IACnC,CAAC;AACD,SAAK,UAAU,IAAI;AAEnB,UAAM,QAAQ,MAAM;AACpB,SAAK,SAAS,MAAM;AAAA,MAClB,OAAO,MAAM,OAAO;AAAA,MACpB,cAAc,MAAM,OAAO;AAAA,MAC3B,QAAQ,MAAM,OAAO;AAAA,IACvB,CAAC;AACD,QAAI,gBAAe,gBAAgB,IAAI,MAAM,OAAO,KAAK,GAAG;AAC1D;AAAA,QACE,KAAK;AAAA,QACL,KAAK;AAAA,QACL;AAAA,QACA,sBAAsB,MAAM,OAAO,KAAK,WAAW,MAAM,OAAO,MAAM,iBAAiB,MAAM,OAAO,YAAY,cAAc,MAAM,OAAO,SAAS,YAAY,MAAM,KAAK,MAAM,eAAe,MAAM,QAAQ,MAAM,kBAAkB,MAAM,OAAO,iBAAiB,CAAC;AAAA,MACvQ;AAAA,IACF;AACA,QAAI,KAAK,SAAS,sBAAsB,UAAU,KAAK,UAAU,IAAI;AACnE;AAAA,QACE,KAAK;AAAA,QACL,KAAK;AAAA,QACL;AAAA,QACA,YAAY,MAAM,OAAO,KAAK,WAAW,MAAM,OAAO,MAAM,iBAAiB,MAAM,OAAO,YAAY,cAAc,MAAM,OAAO,SAAS,YAAY,MAAM,KAAK,MAAM,eAAe,MAAM,QAAQ,MAAM,kBAAkB,MAAM,OAAO,iBAAiB,CAAC;AAAA,MAC7P;AAAA,IACF;AACA,QAAI,KAAK,SAAS,aAAa,UAAU,KAAK,GAAG;AAC/C;AAAA,QACE,KAAK;AAAA,QACL,KAAK;AAAA,QACL;AAAA,QACA,YAAY,MAAM,OAAO,KAAK,WAAW,MAAM,OAAO,MAAM,iBAAiB,MAAM,OAAO,YAAY,cAAc,MAAM,OAAO,SAAS,YAAY,MAAM,KAAK,MAAM,eAAe,MAAM,QAAQ,MAAM,kBAAkB,MAAM,OAAO,iBAAiB,CAAC;AAAA,MAC7P;AAAA,IACF;AACA,QAAI,CAAC,SAAU,MAAK,wBAAwB;AAC5C,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,WAAW,QAeG;AAClB,UAAM,WAAW,OAAO,aAAa;AACrC,QAAI,CAAC,SAAU,MAAK,kBAAkB,oBAAoB,OAAO,KAAK,EAAE;AAIxE,QAAI,OAAO,UAAU,KAAK;AACxB,YAAM,MAAM,MAAM,KAAK,sBAAsB,MAAM;AACnD,UAAI,CAAC,SAAU,MAAK,wBAAwB;AAC5C,aAAO;AAAA,IACT;AAIA,QAAI,OAAO,UAAU,iCAAiC;AACpD,YAAM,MAAM,MAAM,KAAK,8BAA8B,MAAM;AAC3D,UAAI,CAAC,SAAU,MAAK,wBAAwB;AAC5C,aAAO;AAAA,IACT;AAIA,SACG,OAAO,gBAAgB,wBAAwB,wBAChD;AACA,YAAM,MAAM,MAAM,KAAK,2BAA2B,MAAM;AACxD,UAAI,CAAC,SAAU,MAAK,wBAAwB;AAC5C,aAAO;AAAA,IACT;AAEA,UAAM,KAAK,QAAQ;AACnB,QAAI,CAAC,SAAU,MAAK,wBAAwB;AAE5C,UAAM,UAAU,OAAO,WAAW,KAAK,KAAK,WAAW;AACvD,UAAM,YACJ,OAAO,sBACN,OAAO,WAAW,OAAO,KAAK,gBAAgB,UAAU;AAE3D,UAAM,SAAS,KAAK,WAAW;AAC/B,UAAM,QAAQ,OAAO;AAErB,UAAM,SACJ,OAAO,iBACN,OAAO,WAAW,OACf,wBAAwB,OAAO,IAC/B,wBAAwB,MAAS;AACvC,UAAM,aAAa,OAAO,cAAc;AAExC,UAAM,eAAe,OAAO,gBAAgB;AAC5C,UAAM,gBAAgB,OAAO,WAAW,QAAQ,MAAM;AACtD,UAAM,UAAU,gBAAgB,OAAO,WAAW,YAAY,MAAM;AAEpE,UAAM,SAAS,aAAa;AAAA,MAC1B;AAAA,MACA;AAAA,MACA;AAAA,MACA,YAAY;AAAA,MACZ;AAAA,MACA,cAAc;AAAA,MACd;AAAA,MACA;AAAA,IACF,CAAC;AACD,UAAM,aAAyB,GAAG,KAAK,IAAI,MAAM;AAEjD,UAAM,MAAM,OAAO,cAAc,KAAK;AACtC,UAAM,YAAY,KAAK,cAAc,QAAQ,YAAY,WAAW,GAAG;AACvE,UAAM,OAAO,OAAO,OAAO,CAAC,QAAQ,SAAS,CAAC;AAE9C,UAAM,YAAY,OAAO,aAAa;AACtC,QAAI;AACJ,UAAM,eAAe,IAAI,QAAuB,CAAC,SAAS,WAAW;AACnE,iBAAW;AACX,YAAM,IAAI,WAAW,MAAM;AACzB,aAAK,QAAQ,OAAO,UAAU;AAC9B,eAAO,IAAI,MAAM,0BAA0B,KAAK,WAAW,MAAM,EAAE,CAAC;AAAA,MACtE,GAAG,SAAS;AACZ,WAAK,QAAQ,IAAI,YAAY;AAAA,QAC3B,SAAS,CAAC,MAAM;AACd,uBAAa,CAAC;AACd,kBAAQ,CAAC;AAAA,QACX;AAAA,QACA,QAAQ,CAAC,MAAM;AACb,uBAAa,CAAC;AACd,iBAAO,CAAC;AAAA,QACV;AAAA,MACF,CAAC;AAAA,IACH,CAAC;AAKD,iBAAa,MAAM,MAAM;AAAA,IAGzB,CAAC;AAED,SAAK,SAAS,MAAM;AAAA,MAClB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,QAAQ;AAAA,IACV,CAAC;AACD,SAAK,SAAS,EAAE,OAAO,cAAc,GAAG,QAAQ,WAAW,YAAY,EAAE,CAAC;AAC1E,SAAK,UAAU,IAAI;AAEnB,UAAM,QAAQ,MAAM;AACpB,SAAK,SAAS,MAAM;AAAA,MAClB,OAAO,MAAM,OAAO;AAAA,MACpB,cAAc,MAAM,OAAO;AAAA,MAC3B,QAAQ,MAAM,OAAO;AAAA,MACrB,QAAQ;AAAA,IACV,CAAC;AAED,QAAI,MAAM,OAAO,iBAAiB,KAAK;AACrC,YAAM,OAAO,MAAM;AACnB,UAAI,KAAK,WAAW,GAAG;AACrB,cAAM,IAAI;AAAA,UACR;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAGA,UAAM,UAAU,MAAM;AACtB,QAAI,QAAQ,WAAW,EAAG,QAAO,OAAO,MAAM,CAAC;AAE/C,UAAM,YAAY,KAAK;AAAA,MACrB;AAAA,MACA,MAAM,OAAO;AAAA,MACb;AAAA,IACF;AACA,QAAI,CAAC,SAAU,MAAK,wBAAwB;AAC5C,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,2BAA2B,QAWrB;AAClB,UAAM,KAAK,QAAQ;AAEnB,UAAM,UAAU,OAAO,WAAW,KAAK,KAAK,WAAW;AACvD,UAAM,YACJ,OAAO,sBACN,OAAO,WAAW,OAAO,KAAK,gBAAgB,UAAU;AAE3D,UAAM,SAAS,KAAK,WAAW;AAC/B,UAAM,QAAQ,OAAO;AAGrB,UAAM,SAAS,OAAO,gBAAgB,wBAAwB,OAAO;AACrE,UAAM,aAAa,OAAO,cAAc;AAExC,UAAM,eAAe,OAAO,gBAAgB;AAC5C,UAAM,gBAAgB,OAAO,WAAW,QAAQ,MAAM;AACtD,UAAM,UAAU,gBAAgB,OAAO,WAAW,YAAY,MAAM;AAEpE,UAAM,SAAS,aAAa;AAAA,MAC1B;AAAA,MACA;AAAA,MACA;AAAA,MACA,YAAY,OAAO,cAAc;AAAA,MACjC;AAAA,MACA,cAAc;AAAA,MACd;AAAA,MACA;AAAA,IACF,CAAC;AAED,UAAM,MAAM,OAAO,cAAc,KAAK;AACtC,UAAM,YAAY,KAAK,cAAc,QAAQ,YAAY,WAAW,GAAG;AACvE,UAAM,OAAO,OAAO,OAAO,CAAC,QAAQ,SAAS,CAAC;AAE9C,UAAM,YAAY,OAAO,aAAa;AACtC,UAAM,SAAmB,CAAC;AAC1B,QAAI,gBAAgB;AACpB,QAAI,oBAAoB;AACxB,QAAI;AACJ,QAAI;AACJ,QAAI;AAEJ,UAAM,qBAAqB,OAAO,cAAc;AAEhD,WAAO,MAAM,IAAI,QAAgB,CAAC,SAAS,WAAW;AACpD,UAAI;AACJ,UAAI,OAAO;AAEX,YAAM,UAAU,MAAM;AACpB,aAAK,IAAI,SAAS,OAAO;AACzB,YAAI,QAAS,cAAa,OAAO;AAAA,MACnC;AAEA,YAAM,SAAS,CAAC,QAAgB;AAC9B,YAAI,KAAM;AACV,eAAO;AACP,gBAAQ;AACR,gBAAQ,GAAG;AAAA,MACb;AAEA,YAAM,OAAO,CAAC,MAAe;AAC3B,YAAI,KAAM;AACV,eAAO;AACP,gBAAQ;AACR,eAAO,aAAa,QAAQ,IAAI,IAAI,MAAM,OAAO,CAAC,CAAC,CAAC;AAAA,MACtD;AAEA,YAAM,eAAe,CAAC,QAAyB;AAE7C,YAAI,IAAI;AACR,eACE,IAAI,IAAI,WACP,IAAI,CAAC,MAAM,KACV,IAAI,CAAC,MAAM,KACX,IAAI,CAAC,MAAM,MACX,IAAI,CAAC,MAAM,MACX,IAAI,CAAC,MAAM;AAEb;AACF,YAAI,KAAK,IAAI,OAAQ,QAAO;AAC5B,eAAO,IAAI,CAAC,MAAM;AAAA,MACpB;AAEA,YAAM,2BAA2B,CAC/B,SACA,gBACA,eACW;AAGX,YACE,eAAe,UACf,aAAa,KACb,aAAa,QAAQ,QACrB;AACA,gBAAM,gBAAgB,QAAQ,SAAS,GAAG,UAAU;AACpD,gBAAM,YAAY,QAAQ,SAAS,UAAU;AAC7C,gBAAM,gBAAgB,KAAK;AAAA,YACzB;AAAA,YACA;AAAA,YACA;AAAA,UACF;AACA,iBAAO,OAAO,OAAO,CAAC,eAAe,SAAS,CAAC;AAAA,QACjD;AAGA,cAAM,mBAAmB,CAAC,MAAsB;AAC9C,cAAI,EAAE,SAAS,EAAG,QAAO;AACzB,gBAAM,UAAU,KAAK,IAAI,KAAK,MAAM,EAAE,SAAS,CAAC;AAChD,cAAI,QAAQ;AACZ,cAAI,QAAQ;AACZ,mBAAS,IAAI,GAAG,KAAK,SAAS,KAAK;AACjC,kBAAM,QAAQ,EAAE,aAAa,CAAC;AAC9B,kBAAM,WAAW,UAAU;AAC3B,kBAAM,WAAW,UAAU;AAC3B,kBAAM,WAAW,SAAS,cAAc,SAAS;AACjD,kBAAM,WAAW,SAAS,cAAc,SAAS;AACjD,kBAAM,QAAQ,UAAU;AACxB,kBAAM,UAAU,UAAU;AAC1B,gBACE,YACA,YACA,YACA,YACA,SACA,SACA;AACA;AACA,kBAAI,QAAQ,EAAG,SAAQ;AACvB,kBAAI,QAAQ,MAAM,UAAU,EAAG;AAAA,YACjC;AAAA,UACF;AACA,iBAAO,QAAQ,OAAQ,QAAQ,IAAI,MAAQ;AAAA,QAC7C;AAEA,YAAI,IAAI,SAAS,MAAM;AAGrB,gBAAMC,aAAY,KAAK,iBAAiB,SAAS,gBAAgB,GAAG;AACpE,gBAAM,WAAW,iBAAiB,OAAO;AACzC,gBAAM,WAAW,iBAAiBA,UAAS;AAE3C,iBAAO,YAAY,WAAW,UAAUA;AAAA,QAC1C;AAEA,cAAM,aAAa,CAAC,gBAAgB,WAAW,KAAK,GAAG,CAAC,EAAE;AAAA,UACxD,CAAC,GAAG,GAAG,MAAM,OAAO,SAAS,CAAC,KAAK,EAAE,QAAQ,CAAC,MAAM;AAAA,QACtD;AACA,cAAM,YAAY,WAAW;AAAA,UAAI,CAAC,QAChC,KAAK,iBAAiB,SAAS,KAAK,GAAG;AAAA,QACzC;AAGA,mBAAW,KAAK,WAAW;AACzB,cAAI,EAAE,SAAS,KAAK,CAAC,aAAa,CAAC,EAAG,QAAO;AAAA,QAC/C;AACA,eAAO,UAAU,CAAC,KAAK,OAAO,MAAM,CAAC;AAAA,MACvC;AAEA,YAAM,UAAU,CAAC,UAAyB;AACxC,YAAI,MAAM,OAAO,UAAU,MAAO;AAElC,YACE,qBAAqB,UACrB,MAAM,OAAO,eAAe,kBAC5B;AACA;AAAA,QACF;AAIA,YACE,oBAAoB,UACpB,MAAM,OAAO,cAAc,iBAC3B;AACA;AAAA,QACF;AAGA,YACE,iBAAiB,UACjB,MAAM,OAAO,WAAW,cACxB;AACA;AAAA,QACF;AAGA,YACE,MAAM,OAAO,WAAW,UACxB,MAAM,OAAO,gBAAgB,KAC7B;AACA;AAAA,YACE,IAAI;AAAA,cACF,kDAAkD,KAAK,iBAAiB,SAAS,iBAAiB,MAAM,OAAO,SAAS,WAAW,MAAM,iBAAiB,MAAM,OAAO,YAAY;AAAA,YACrL;AAAA,UACF;AACA;AAAA,QACF;AAEA,YAAI;AAGF,cAAI,eAAe;AACnB,cAAI;AACJ,cAAI,MAAM,UAAU,SAAS,GAAG;AAC9B,gBAAI;AACF,oBAAM,SAAS,KAAK;AAAA,gBAClB,MAAM;AAAA,gBACN,MAAM,OAAO;AAAA,gBACb;AAAA,cACF;AACA,kBAAI,OAAO,SAAS,4BAA4B;AAC9C,+BAAe;AAEjB,oBAAM,kBAAkB,OAAO;AAAA,gBAC7B;AAAA,cACF;AACA,kBAAI,mBAAmB,gBAAgB,CAAC,GAAG;AACzC,6BAAa,SAAS,gBAAgB,CAAC,GAAG,EAAE;AAAA,cAC9C;AAAA,YACF,QAAQ;AAAA,YAER;AAAA,UACF;AAEA,gBAAM,YAAY;AAAA,YAChB,MAAM;AAAA,YACN,MAAM,OAAO;AAAA,YACb;AAAA,UACF;AACA,cAAI,UAAU,WAAW,EAAG;AAE5B,cAAI,CAAC,cAAc;AAEjB,gBAAI,aAAa,SAAS,EAAG;AAAA,UAC/B;AAGA,cAAI,iBAAiB,QAAW;AAC9B,2BAAe,MAAM,OAAO;AAC5B,8BAAkB,MAAM,OAAO;AAC/B,+BAAmB,MAAM,OAAO;AAAA,UAClC;AAEA,iBAAO,KAAK,SAAS;AACrB,2BAAiB,UAAU;AAG3B,gBAAM,MAAM,KAAK,IAAI;AACrB,cAAI,KAAK,SAAS,WAAW,MAAM,qBAAqB,KAAO;AAC7D,gCAAoB;AACpB,iBAAK,SAAS,0BAA0B;AAAA,cACtC;AAAA,cACA;AAAA,cACA,OAAO;AAAA,YACT,CAAC;AAAA,UACH;AAGA,cAAI,MAAM,OAAO,iBAAiB,KAAK;AACrC,mBAAO,OAAO,OAAO,MAAM,CAAC;AAAA,UAC9B;AAAA,QACF,SAAS,GAAG;AACV,eAAK,CAAC;AAAA,QACR;AAAA,MACF;AAEA,gBAAU,WAAW,MAAM;AACzB;AAAA,UACE,IAAI;AAAA,YACF,uDAAuD,KAAK,WAAW,MAAM;AAAA,UAC/E;AAAA,QACF;AAAA,MACF,GAAG,SAAS;AAGZ,WAAK,GAAG,SAAS,OAAO;AAExB,UAAI;AACF,aAAK,SAAS,MAAM;AAAA,UAClB;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA,QAAQ;AAAA,UACR,cAAc;AAAA,QAChB,CAAC;AACD,aAAK,SAAS;AAAA,UACZ;AAAA,UACA,cAAc;AAAA,UACd;AAAA,UACA;AAAA,UACA,YAAY;AAAA,QACd,CAAC;AACD,aAAK,UAAU,IAAI;AAAA,MACrB,SAAS,GAAG;AACV,aAAK,CAAC;AAAA,MACR;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEA,MAAc,8BAA8B,QAaxB;AAClB,UAAM,KAAK,QAAQ;AAEnB,UAAM,UAAU,OAAO,WAAW,KAAK,KAAK,WAAW;AAKvD,UAAM,iBAAiB,KAAK,WAAW;AACvC,UAAM,YAAY,OAAO,qBAAqB;AAE9C,UAAM,SAAS,OAAO,kBAAkB;AACxC,UAAM,QAAQ,OAAO;AAGrB,UAAM,SAAS,OAAO,gBAAgB;AACtC,UAAM,aAAa,OAAO,cAAc;AAExC,UAAM,eAAe,OAAO,gBAAgB;AAC5C,UAAM,gBAAgB,OAAO,WAAW,QAAQ,MAAM;AACtD,UAAM,UAAU,gBAAgB,OAAO,WAAW,YAAY,MAAM;AACpE,UAAM,qBAAqB,OAAO,cAAc;AAChD,UAAM,SAAS,aAAa;AAAA,MAC1B;AAAA,MACA;AAAA,MACA;AAAA,MACA,YAAY;AAAA,MACZ;AAAA,MACA,cAAc;AAAA,MACd;AAAA,MACA;AAAA,IACF,CAAC;AAED,UAAM,MAAM,OAAO,cAAc,KAAK;AACtC,UAAM,YAAY,KAAK,cAAc,QAAQ,YAAY,WAAW,GAAG;AACvE,UAAM,OAAO,OAAO,OAAO,CAAC,QAAQ,SAAS,CAAC;AAE9C,UAAM,YAAY,OAAO,aAAa;AAItC,UAAM,gBAAgB;AACtB,UAAM,SAAmB,CAAC;AAC1B,QAAI;AACJ,QAAI;AACJ,QAAI;AACJ,QAAI;AACJ,UAAM,eAAe,CAAC,QAAyB;AAC7C,UAAI,IAAI;AACR,aACE,IAAI,IAAI,WACP,IAAI,CAAC,MAAM,KACV,IAAI,CAAC,MAAM,KACX,IAAI,CAAC,MAAM,MACX,IAAI,CAAC,MAAM,MACX,IAAI,CAAC,MAAM;AAEb;AACF,UAAI,KAAK,IAAI,OAAQ,QAAO;AAC5B,aAAO,IAAI,CAAC,MAAM;AAAA,IACpB;AAEA,UAAM,yBAAyB,CAC7B,SACA,gBACA,eACG;AAGH,UACE,eAAe,UACf,aAAa,KACb,aAAa,QAAQ,QACrB;AACA,cAAM,gBAAgB,QAAQ,SAAS,GAAG,UAAU;AACpD,cAAM,YAAY,QAAQ,SAAS,UAAU;AAC7C,cAAM,gBAAgB,KAAK;AAAA,UACzB;AAAA,UACA;AAAA,UACA;AAAA,QACF;AACA,eAAO,OAAO,OAAO,CAAC,eAAe,SAAS,CAAC;AAAA,MACjD;AAGA,YAAM,mBAAmB,CAAC,MAAsB;AAC9C,YAAI,EAAE,SAAS,EAAG,QAAO;AACzB,cAAM,UAAU,KAAK,IAAI,KAAK,MAAM,EAAE,SAAS,CAAC;AAChD,YAAI,QAAQ;AACZ,YAAI,QAAQ;AACZ,iBAAS,IAAI,GAAG,KAAK,SAAS,KAAK;AACjC,gBAAM,QAAQ,EAAE,aAAa,CAAC;AAC9B,gBAAM,WAAW,UAAU;AAC3B,gBAAM,WAAW,UAAU;AAC3B,gBAAM,WAAW,SAAS,cAAc,SAAS;AACjD,gBAAM,WAAW,SAAS,cAAc,SAAS;AACjD,gBAAM,QAAQ,UAAU;AACxB,gBAAM,UAAU,UAAU;AAC1B,cACE,YACA,YACA,YACA,YACA,SACA,SACA;AACA;AACA,gBAAI,QAAQ,EAAG,SAAQ;AACvB,gBAAI,QAAQ,MAAM,UAAU,EAAG;AAAA,UACjC;AAAA,QACF;AACA,eAAO,QAAQ,OAAQ,QAAQ,IAAI,MAAQ;AAAA,MAC7C;AAEA,UAAI,IAAI,SAAS,MAAM;AAGrB,cAAMA,aAAY,KAAK,iBAAiB,SAAS,gBAAgB,GAAG;AACpE,cAAM,WAAW,iBAAiB,OAAO;AACzC,cAAM,WAAW,iBAAiBA,UAAS;AAE3C,eAAO,YAAY,WAAW,UAAUA;AAAA,MAC1C;AAEA,YAAM,aAAa,CAAC,gBAAgB,WAAW,KAAK,GAAG,CAAC,EAAE;AAAA,QACxD,CAAC,GAAG,GAAG,MAAM,OAAO,SAAS,CAAC,KAAK,EAAE,QAAQ,CAAC,MAAM;AAAA,MACtD;AACA,YAAM,YAAY,WAAW;AAAA,QAAI,CAAC,QAChC,KAAK,iBAAiB,SAAS,KAAK,GAAG;AAAA,MACzC;AAEA,iBAAW,KAAK,WAAW;AACzB,YAAI,EAAE,SAAS,KAAK,CAAC,aAAa,CAAC,EAAG,QAAO;AAAA,MAC/C;AACA,aAAO,UAAU,CAAC,KAAK,OAAO,MAAM,CAAC;AAAA,IACvC;AAEA,WAAO,MAAM,IAAI,QAAgB,CAAC,SAAS,WAAW;AACpD,UAAI;AACJ,UAAI,OAAO;AAEX,YAAM,UAAU,MAAM;AACpB,aAAK,IAAI,SAAS,OAAO;AACzB,YAAI,QAAS,cAAa,OAAO;AACjC,YAAI,UAAW,cAAa,SAAS;AAAA,MACvC;AAEA,YAAM,SAAS,CAAC,QAAgB;AAC9B,YAAI,KAAM;AACV,eAAO;AACP,gBAAQ;AACR,gBAAQ,GAAG;AAAA,MACb;AAEA,YAAM,OAAO,CAAC,MAAe;AAC3B,YAAI,KAAM;AACV,eAAO;AACP,gBAAQ;AACR,eAAO,aAAa,QAAQ,IAAI,IAAI,MAAM,OAAO,CAAC,CAAC,CAAC;AAAA,MACtD;AAEA,YAAM,gBAAgB,MAAM;AAC1B,YAAI,UAAW,cAAa,SAAS;AACrC,oBAAY,WAAW,MAAM;AAC3B,cAAI,OAAO,SAAS,EAAG,QAAO,OAAO,OAAO,MAAM,CAAC;AAAA,QACrD,GAAG,aAAa;AAAA,MAClB;AAEA,YAAM,UAAU,CAAC,UAAyB;AACxC,YAAI,MAAM,OAAO,UAAU,MAAO;AAIlC,YACE,qBAAqB,UACrB,MAAM,OAAO,eAAe,kBAC5B;AACA;AAAA,QACF;AAIA,YACE,oBAAoB,UACpB,MAAM,OAAO,cAAc,iBAC3B;AACA;AAAA,QACF;AAEA,YACE,iBAAiB,UACjB,MAAM,OAAO,WAAW,cACxB;AACA;AAAA,QACF;AAKA,cAAM,KAAK,MAAM,OAAO;AACxB,cAAM,cAAc,MAAM,OAAO,KAAK;AACtC,YAAI,eAAe,MAAM,OAAO,WAAW,QAAQ;AACjD;AAAA,YACE,IAAI;AAAA,cACF,gDAAgD,KAAK,iBAAiB,SAAS,iBAAiB,MAAM,OAAO,SAAS,eAAe,kBAAkB,WAAW,MAAM,OAAO,MAAM,iBAAiB,EAAE;AAAA,YAC1M;AAAA,UACF;AACA;AAAA,QACF;AAEA,YAAI;AACF,cAAI,eAAe;AACnB,cAAI;AACJ,cAAI,MAAM,UAAU,SAAS,GAAG;AAC9B,gBAAI;AACF,oBAAM,SAAS,KAAK;AAAA,gBAClB,MAAM;AAAA,gBACN,MAAM,OAAO;AAAA,gBACb;AAAA,cACF;AACA,kBAAI,OAAO,SAAS,4BAA4B,GAAG;AACjD,+BAAe;AAAA,cACjB;AAEA,oBAAM,kBAAkB,OAAO;AAAA,gBAC7B;AAAA,cACF;AACA,kBAAI,mBAAmB,gBAAgB,CAAC,GAAG;AACzC,6BAAa,SAAS,gBAAgB,CAAC,GAAG,EAAE;AAAA,cAC9C;AAAA,YACF,QAAQ;AAAA,YAER;AAAA,UACF;AAEA,gBAAM,YAAY;AAAA,YAChB,MAAM;AAAA,YACN,MAAM,OAAO;AAAA,YACb;AAAA,UACF;AACA,cAAI,UAAU,WAAW,EAAG;AAE5B,cAAI,CAAC,gBAAgB,aAAa,SAAS,EAAG;AAE9C,cAAI,iBAAiB,QAAW;AAC9B,2BAAe,MAAM,OAAO;AAC5B,8BAAkB,MAAM,OAAO;AAC/B,+BAAmB,MAAM,OAAO;AAAA,UAClC;AAEA,iBAAO,KAAK,SAAS;AACrB,wBAAc;AAEd,cAAI,MAAM,OAAO,iBAAiB,KAAK;AACrC,mBAAO,OAAO,OAAO,MAAM,CAAC;AAAA,UAC9B;AAAA,QACF,SAAS,GAAG;AACV,eAAK,CAAC;AAAA,QACR;AAAA,MACF;AAEA,gBAAU,WAAW,MAAM;AACzB,YAAI,OAAO,SAAS,GAAG;AACrB,iBAAO,OAAO,OAAO,MAAM,CAAC;AAC5B;AAAA,QACF;AACA;AAAA,UACE,IAAI;AAAA,YACF,oEAAoE,KAAK,cAAc,SAAS,eAAe,kBAAkB;AAAA,UACnI;AAAA,QACF;AAAA,MACF,GAAG,SAAS;AAEZ,WAAK,GAAG,SAAS,OAAO;AAExB,UAAI;AACF,aAAK,SAAS,MAAM;AAAA,UAClB;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA,QAAQ;AAAA,UACR,oBAAoB;AAAA,QACtB,CAAC;AACD,aAAK,SAAS;AAAA,UACZ;AAAA,UACA,cAAc;AAAA,UACd;AAAA,UACA;AAAA,UACA,YAAY;AAAA,QACd,CAAC;AACD,aAAK,UAAU,IAAI;AAAA,MACrB,SAAS,GAAG;AACV,aAAK,CAAC;AAAA,MACR;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEA,MAAc,sBAAsB,QAWhB;AAClB,UAAM,KAAK,QAAQ;AAEnB,UAAM,UAAU,OAAO,WAAW,KAAK,KAAK,WAAW;AACvD,UAAM,YACJ,OAAO,sBACN,OAAO,WAAW,OAAO,KAAK,gBAAgB,UAAU;AAE3D,UAAM,SAAS,KAAK,WAAW;AAC/B,UAAM,QAAQ,OAAO;AAIrB,UAAM,SACJ,OAAO,iBACN,OAAO,WAAW,OAAO,yBAAyB,OAAO,IAAI;AAChE,UAAM,aAAa,OAAO,cAAc;AAExC,UAAM,eAAe,OAAO,gBAAgB;AAC5C,UAAM,gBAAgB,OAAO,WAAW,QAAQ,MAAM;AACtD,UAAM,UAAU,gBAAgB,OAAO,WAAW,YAAY,MAAM;AAEpE,UAAM,SAAS,aAAa;AAAA,MAC1B;AAAA,MACA;AAAA,MACA;AAAA,MACA,YAAY,OAAO,cAAc;AAAA,MACjC;AAAA,MACA,cAAc;AAAA,MACd;AAAA,MACA;AAAA,IACF,CAAC;AAED,UAAM,MAAM,OAAO,cAAc,KAAK;AACtC,UAAM,YAAY,KAAK,cAAc,QAAQ,YAAY,WAAW,GAAG;AACvE,UAAM,OAAO,OAAO,OAAO,CAAC,QAAQ,SAAS,CAAC;AAE9C,UAAM,YAAY,OAAO,aAAa;AACtC,UAAM,SAAmB,CAAC;AAC1B,QAAI,gBAAgB;AAEpB,UAAM,iBAAiB,CAAC,QAAwB;AAE9C,eAAS,IAAI,GAAG,IAAI,IAAI,IAAI,QAAQ,KAAK;AACvC,YAAI,IAAI,CAAC,MAAM,OAAQ,IAAI,IAAI,CAAC,MAAM,IAAM,QAAO;AAAA,MACrD;AACA,aAAO;AAAA,IACT;AAEA,UAAM,kBAAkB,CAAC,QAAyB;AAEhD,eAAS,IAAI,GAAG,IAAI,IAAI,IAAI,QAAQ,KAAK;AACvC,YAAI,IAAI,CAAC,MAAM,OAAQ,IAAI,IAAI,CAAC,MAAM,IAAM,QAAO;AAAA,MACrD;AACA,aAAO;AAAA,IACT;AAEA,WAAO,MAAM,IAAI,QAAgB,CAAC,SAAS,WAAW;AACpD,UAAI;AACJ,UAAI,OAAO;AAEX,YAAM,UAAU,MAAM;AACpB,aAAK,IAAI,SAAS,OAAO;AACzB,YAAI,QAAS,cAAa,OAAO;AAAA,MACnC;AAEA,YAAM,SAAS,CAAC,QAAgB;AAC9B,YAAI,KAAM;AACV,eAAO;AACP,gBAAQ;AACR,gBAAQ,GAAG;AAAA,MACb;AAEA,YAAM,OAAO,CAAC,MAAe;AAC3B,YAAI,KAAM;AACV,eAAO;AACP,gBAAQ;AACR,eAAO,aAAa,QAAQ,IAAI,IAAI,MAAM,OAAO,CAAC,CAAC,CAAC;AAAA,MACtD;AAEA,YAAM,UAAU,CAAC,UAAyB;AACxC,YAAI,MAAM,OAAO,UAAU,MAAO;AAIlC,YACE,MAAM,OAAO,WAAW,UACxB,MAAM,OAAO,gBAAgB,KAC7B;AACA;AAAA,YACE,IAAI;AAAA,cACF,6CAA6C,KAAK,WAAW,MAAM,iBAAiB,MAAM,OAAO,YAAY;AAAA,YAC/G;AAAA,UACF;AACA;AAAA,QACF;AAEA,YAAI;AAIF,cAAI,gBAAgB;AACpB,cAAI,MAAM,UAAU,SAAS,GAAG;AAC9B,gBAAI;AACF,oBAAM,SAAS,KAAK;AAAA,gBAClB,MAAM;AAAA,gBACN,MAAM,OAAO;AAAA,gBACb;AAAA,cACF;AACA,kBAAI,OAAO,SAAS,4BAA4B,GAAG;AACjD,gCAAgB;AAAA,cAClB;AAAA,YACF,QAAQ;AAAA,YAGR;AAAA,UACF;AAGA,gBAAM,YAAY,KAAK;AAAA,YACrB,MAAM;AAAA,YACN,MAAM,OAAO;AAAA,YACb;AAAA,UACF;AACA,cAAI,UAAU,WAAW,EAAG;AAE5B,gBAAM,OAAO,UACV,SAAS,GAAG,KAAK,IAAI,IAAI,UAAU,MAAM,CAAC,EAC1C,SAAS,MAAM;AAClB,gBAAM,eACJ,KAAK,WAAW,OAAO,KAAK,KAAK,UAAU,EAAE,WAAW,GAAG;AAC7D,cAAI,CAAC,iBAAiB,aAAc;AAEpC,cAAI,WAAW;AACf,cAAI,CAAC,eAAe;AAClB,kBAAM,MAAM,eAAe,SAAS;AACpC,gBAAI,QAAQ,IAAI;AAEd,kBAAI,CAAC,cAAe;AAEpB,yBAAW;AACX,qBAAO,KAAK,QAAQ;AACpB,oBAAMC,YAAW,OAAO,OAAO,MAAM;AACrC,kBAAI,MAAM,OAAO,iBAAiB,IAAK,QAAOA,SAAQ;AACtD;AAAA,YACF;AACA,4BAAgB;AAChB,uBAAW,UAAU,SAAS,GAAG;AAAA,UACnC;AAEA,iBAAO,KAAK,QAAQ;AACpB,gBAAM,WAAW,OAAO,OAAO,MAAM;AAGrC,cAAI,gBAAgB,QAAQ,KAAK,MAAM,OAAO,iBAAiB,KAAK;AAElE,kBAAM,SAAS,SAAS,QAAQ,OAAO,KAAK,CAAC,KAAM,GAAI,CAAC,CAAC;AACzD,gBAAI,WAAW,IAAI;AACjB,qBAAO,SAAS,SAAS,GAAG,SAAS,CAAC,CAAC;AACvC;AAAA,YACF;AACA,mBAAO,QAAQ;AAAA,UACjB;AAAA,QACF,SAAS,GAAG;AACV,eAAK,CAAC;AAAA,QACR;AAAA,MACF;AAEA,gBAAU,WAAW,MAAM;AACzB;AAAA,UACE,IAAI;AAAA,YACF,gDAAgD,KAAK,WAAW,MAAM;AAAA,UACxE;AAAA,QACF;AAAA,MACF,GAAG,SAAS;AAIZ,WAAK,GAAG,SAAS,OAAO;AAExB,UAAI;AACF,aAAK,SAAS,MAAM;AAAA,UAClB;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA,QAAQ;AAAA,UACR,UAAU;AAAA,QACZ,CAAC;AACD,aAAK,SAAS;AAAA,UACZ;AAAA,UACA,cAAc;AAAA,UACd;AAAA,UACA;AAAA,UACA,YAAY,OAAO,cAAc;AAAA,QACnC,CAAC;AACD,aAAK,UAAU,IAAI;AAAA,MACrB,SAAS,GAAG;AACV,aAAK,CAAC;AAAA,MACR;AAAA,IACF,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,uBAAuB,QAiBT;AAClB,UAAM,aAAa,OAAO,cAAc;AACxC,UAAM,eAAe,OAAO,gBAAgB;AAE5C,QAAI;AAEJ,aAAS,UAAU,GAAG,UAAU,YAAY,WAAW;AACrD,UAAI;AACF,eAAO,MAAM,KAAK,4BAA4B,MAAM;AAAA,MACtD,SAAS,GAAG;AACV,cAAM,MAAM,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC;AACrD,oBAAY,aAAa,QAAQ,IAAI,IAAI,MAAM,GAAG;AAGlD,cAAM,iBACJ,IAAI,SAAS,UAAU,MACtB,IAAI,SAAS,kBAAkB,KAAK,IAAI,SAAS,eAAe;AAEnE,YAAI,kBAAkB,UAAU,aAAa,GAAG;AAC9C,kBAAQ;AAAA,YACN,0BAA0B,UAAU,CAAC,yBAAyB,YAAY;AAAA,UAC5E;AACA,gBAAM,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,YAAY,CAAC;AAChE;AAAA,QACF;AAEA,cAAM;AAAA,MACR;AAAA,IACF;AAEA,UAAM,aAAa,IAAI,MAAM,uCAAuC;AAAA,EACtE;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,4BAA4B,QAWtB;AAClB,UAAM,KAAK,QAAQ;AAEnB,UAAM,UAAU,OAAO,WAAW,KAAK,KAAK,WAAW;AAMvD,UAAM,iBAAiB,KAAK,WAAW;AACvC,UAAM,YAAY,OAAO,qBAAqB;AAC9C,UAAM,SAAS,OAAO,kBAAkB;AAExC,UAAM,QAAQ,OAAO;AAGrB,UAAM,SAAS,OAAO,gBAAgB;AACtC,UAAM,aAAa,OAAO,cAAc;AAExC,UAAM,eAAe,OAAO,gBAAgB;AAC5C,UAAM,gBAAgB,OAAO,WAAW,QAAQ,MAAM;AACtD,UAAM,UAAU,gBAAgB,OAAO,WAAW,YAAY,MAAM;AAEpE,UAAM,SAAS,aAAa;AAAA,MAC1B;AAAA,MACA;AAAA,MACA;AAAA,MACA,YAAY,OAAO,cAAc;AAAA,MACjC;AAAA,MACA,cAAc;AAAA,MACd;AAAA,MACA;AAAA,IACF,CAAC;AAED,UAAM,MAAM,OAAO,cAAc,KAAK;AACtC,UAAM,YAAY,KAAK,cAAc,QAAQ,YAAY,WAAW,GAAG;AACvE,UAAM,OAAO,OAAO,OAAO,CAAC,QAAQ,SAAS,CAAC;AAE9C,UAAM,YAAY,OAAO,aAAa;AACtC,UAAM,SAAmB,CAAC;AAC1B,QAAI,mBAAmB;AACvB,QAAI;AACJ,QAAI;AACJ,QAAI;AACJ,QAAI;AACJ,UAAM,kBAAkB,oBAAI,IAAI,CAAC,QAAQ,MAAM,CAAC;AAEhD,UAAM,qBAAqB,OAAO,cAAc;AAEhD,UAAM,kBAAkB,CAAC,QAAyB;AAEhD,YAAM,UAAU,KAAK,IAAI,KAAK,IAAI,IAAI,SAAS,GAAG,CAAC,GAAG,KAAK,IAAI;AAC/D,eAAS,IAAI,GAAG,IAAI,SAAS,KAAK;AAChC,YAAI,IAAI,CAAC,MAAM,KAAQ,IAAI,IAAI,CAAC,MAAM,EAAM;AAC5C,YAAI,IAAI,IAAI,CAAC,MAAM,EAAM,QAAO;AAChC,YAAI,IAAI,IAAI,CAAC,MAAM,KAAQ,IAAI,IAAI,CAAC,MAAM,EAAM,QAAO;AAAA,MACzD;AACA,aAAO;AAAA,IACT;AAEA,UAAM,wBAAwB,CAC5B,SACA,gBACA,eACG;AAGH,UACE,eAAe,UACf,aAAa,KACb,aAAa,QAAQ,QACrB;AACA,cAAM,gBAAgB,QAAQ,SAAS,GAAG,UAAU;AACpD,cAAM,YAAY,QAAQ,SAAS,UAAU;AAC7C,cAAM,gBAAgB,KAAK;AAAA,UACzB;AAAA,UACA;AAAA,UACA;AAAA,QACF;AACA,eAAO,OAAO,OAAO,CAAC,eAAe,SAAS,CAAC;AAAA,MACjD;AAGA,YAAM,mBAAmB,CAAC,MAAsB;AAC9C,YAAI,EAAE,SAAS,EAAG,QAAO;AACzB,cAAM,UAAU,KAAK,IAAI,KAAK,MAAM,EAAE,SAAS,CAAC;AAChD,YAAI,QAAQ;AACZ,YAAI,QAAQ;AACZ,iBAAS,IAAI,GAAG,KAAK,SAAS,KAAK;AACjC,gBAAM,QAAQ,EAAE,aAAa,CAAC;AAC9B,gBAAM,WAAW,UAAU;AAC3B,gBAAM,WAAW,UAAU;AAC3B,gBAAM,WAAW,SAAS,cAAc,SAAS;AACjD,gBAAM,WAAW,SAAS,cAAc,SAAS;AACjD,gBAAM,QAAQ,UAAU;AACxB,gBAAM,UAAU,UAAU;AAC1B,cACE,YACA,YACA,YACA,YACA,SACA,SACA;AACA;AACA,gBAAI,QAAQ,EAAG,SAAQ;AACvB,gBAAI,QAAQ,MAAM,UAAU,EAAG;AAAA,UACjC;AAAA,QACF;AACA,eAAO,QAAQ,OAAQ,QAAQ,IAAI,MAAQ;AAAA,MAC7C;AAIA,UAAI,IAAI,SAAS,MAAM;AAGrB,cAAMD,aAAY,KAAK,iBAAiB,SAAS,gBAAgB,GAAG;AACpE,cAAM,WAAW,iBAAiB,OAAO;AACzC,cAAM,WAAW,iBAAiBA,UAAS;AAC3C,eAAO,WAAW,WAAWA,aAAY;AAAA,MAC3C;AAEA,YAAM,aAAa,CAAC,gBAAgB,WAAW,KAAK,GAAG,CAAC,EAAE;AAAA,QACxD,CAAC,GAAG,GAAG,MAAM,OAAO,SAAS,CAAC,KAAK,EAAE,QAAQ,CAAC,MAAM;AAAA,MACtD;AACA,YAAM,YAAY,WAAW;AAAA,QAAI,CAAC,QAChC,KAAK,iBAAiB,SAAS,KAAK,GAAG;AAAA,MACzC;AACA,iBAAW,KAAK,WAAW;AACzB,YAAI,EAAE,UAAU,GAAG;AACjB,gBAAM,QAAQ,EAAE,SAAS,GAAG,CAAC,EAAE,SAAS,OAAO;AAC/C,cAAI,gBAAgB,IAAI,KAAK,EAAG,QAAO;AAAA,QACzC;AAAA,MACF;AACA,aAAO,UAAU,CAAC,KAAK,OAAO,MAAM,CAAC;AAAA,IACvC;AAEA,WAAO,MAAM,IAAI,QAAgB,CAAC,SAAS,WAAW;AACpD,UAAI;AACJ,UAAI,OAAO;AAEX,YAAM,UAAU,MAAM;AACpB,aAAK,IAAI,SAAS,OAAO;AACzB,YAAI,QAAS,cAAa,OAAO;AAAA,MACnC;AAEA,YAAM,SAAS,CAAC,QAAgB;AAC9B,YAAI,KAAM;AACV,eAAO;AACP,gBAAQ;AACR,gBAAQ,GAAG;AAAA,MACb;AAEA,YAAM,OAAO,CAAC,MAAe;AAC3B,YAAI,KAAM;AACV,eAAO;AACP,gBAAQ;AACR,eAAO,aAAa,QAAQ,IAAI,IAAI,MAAM,OAAO,CAAC,CAAC,CAAC;AAAA,MACtD;AAEA,YAAM,UAAU,CAAC,UAAyB;AAKxC,cAAM,sBAAsB,oBAAI,IAAI,CAAC,KAAK,KAAK,KAAK,KAAK,GAAG,CAAC;AAC7D,cAAM,oBAAoB,oBAAoB,IAAI,KAAK;AACvD,cAAM,iBAAiB,oBACnB,oBAAI,IAAI,CAAC,OAAO,KAAK,CAAC,CAAC,IACvB,oBAAI,IAAI,CAAC,KAAK,CAAC;AAEnB,YAAI,CAAC,eAAe,IAAI,MAAM,OAAO,KAAK,GAAG;AAC3C;AAAA,QACF;AAEA,YACE,qBAAqB,UACrB,MAAM,OAAO,eAAe,kBAC5B;AACA;AAAA,QACF;AAIA,YACE,oBAAoB,UACpB,MAAM,OAAO,cAAc,iBAC3B;AACA;AAAA,QACF;AAMA,YAAI,iBAAiB,QAAW;AAC9B,gBAAM,kBAAkB,MAAM,OAAO,WAAW;AAChD,gBAAM,wBACJ,qBAAqB,MAAM,OAAO,iBAAiB;AACrD,cAAI,CAAC,mBAAmB,CAAC,uBAAuB;AAC9C;AAAA,UACF;AAAA,QACF;AAKA,cAAM,aACH,MAAM,OAAO,gBAAgB,OAC5B,MAAM,OAAO,WAAW,UACzB,qBACC,MAAM,OAAO,UAAU,KACvB,MAAM,OAAO,kBAAkB,OAC/B,MAAM,OAAO,iBAAiB;AAElC,YAAI,YAAY;AACd;AAAA,YACE,IAAI;AAAA,cACF,iDAAiD,KAAK,iBAAiB,SAAS,iBAAiB,MAAM,OAAO,SAAS,kBAAkB,kBAAkB,kBAAkB,MAAM,OAAO,UAAU,WAAW,MAAM,OAAO,MAAM,iBAAiB,MAAM,OAAO,YAAY,kBAAkB,MAAM,OAAO,aAAa;AAAA,YAC1T;AAAA,UACF;AACA;AAAA,QACF;AAEA,YAAI;AAIF,cAAI,gBAAgB;AACpB,cAAI;AACJ,cAAI,MAAM,UAAU,SAAS,GAAG;AAC9B,kBAAM,SAAS,KAAK;AAAA,cAClB,MAAM;AAAA,cACN,MAAM,OAAO;AAAA,cACb;AAAA,YACF;AACA,gBAAI,OAAO,SAAS,4BAA4B,GAAG;AACjD,8BAAgB;AAAA,YAClB;AAEA,kBAAM,kBAAkB,OAAO;AAAA,cAC7B;AAAA,YACF;AACA,gBAAI,mBAAmB,gBAAgB,CAAC,GAAG;AACzC,2BAAa,SAAS,gBAAgB,CAAC,GAAG,EAAE;AAAA,YAC9C;AAAA,UACF;AAEA,gBAAM,YAAY;AAAA,YAChB,MAAM;AAAA,YACN,MAAM,OAAO;AAAA,YACb;AAAA,UACF;AAIA,gBAAM,cACJ,MAAM,OAAO,iBAAiB,OAC9B,MAAM,OAAO,iBAAiB;AAChC,gBAAM,wBACJ,qBACA,MAAM,OAAO,UAAU,KACvB,MAAM,OAAO,kBAAkB;AAEjC,cAAI,eAAe,uBAAuB;AACxC,kBAAM,WAAW,OAAO,OAAO,MAAM;AACrC,mBAAO,QAAQ;AACf;AAAA,UACF;AAEA,cAAI,UAAU,WAAW,EAAG;AAG5B,gBAAM,OAAO,UACV,SAAS,GAAG,KAAK,IAAI,IAAI,UAAU,MAAM,CAAC,EAC1C,SAAS,MAAM;AAClB,gBAAM,eACJ,KAAK,WAAW,OAAO,KAAK,KAAK,UAAU,EAAE,WAAW,GAAG;AAC7D,cAAI,CAAC,iBAAiB,cAAc;AAGlC,kBAAM,SAAS,UAAU,SAAS,MAAM;AACxC,2BACE,OAAO,SAAS,MAAO,GAAG,OAAO,MAAM,GAAG,GAAI,CAAC,WAAM;AACvD;AAAA,UACF;AAMA,cAAI,CAAC,kBAAkB;AACrB,kBAAM,cAAc,UAAU,SAAS,GAAG,CAAC,EAAE,SAAS,OAAO;AAC7D,gBAAI,gBAAgB,IAAI,WAAW,GAAG;AACpC,gCAAkB,MAAM,OAAO;AAC/B,iCAAmB,MAAM,OAAO;AAEhC,6BAAe,oBACX,MAAM,OAAO,eACb,MAAM,OAAO;AACjB,iCAAmB;AACnB,qBAAO,KAAK,SAAS;AAAA,YACvB,WAAW,gBAAgB,SAAS,GAAG;AAGrC,gCAAkB,MAAM,OAAO;AAC/B,iCAAmB,MAAM,OAAO;AAChC,6BAAe,oBACX,MAAM,OAAO,eACb,MAAM,OAAO;AACjB,iCAAmB;AACnB,qBAAO,KAAK,SAAS;AAAA,YACvB;AAAA,UACF,OAAO;AACL,mBAAO,KAAK,SAAS;AAAA,UACvB;AAAA,QACF,SAAS,GAAG;AACV,eAAK,CAAC;AAAA,QACR;AAAA,MACF;AAEA,gBAAU,WAAW,MAAM;AAEzB,YAAI,OAAO,SAAS,GAAG;AACrB,gBAAM,WAAW,OAAO,OAAO,MAAM;AACrC,iBAAO,QAAQ;AAAA,QACjB,OAAO;AACL,gBAAM,QAAQ,eACV,kBAAkB,KAAK,UAAU,YAAY,CAAC,KAC9C;AACJ;AAAA,YACE,IAAI;AAAA,cACF,oDAAoD,KAAK,WAAW,MAAM,GAAG,KAAK;AAAA,YACpF;AAAA,UACF;AAAA,QACF;AAAA,MACF,GAAG,SAAS;AAGZ,WAAK,GAAG,SAAS,OAAO;AAExB,UAAI;AACF,aAAK,SAAS,MAAM;AAAA,UAClB;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA,QAAQ;AAAA,UACR,cAAc;AAAA,QAChB,CAAC;AACD,aAAK,SAAS;AAAA,UACZ;AAAA,UACA,cAAc;AAAA,UACd;AAAA,UACA;AAAA,UACA,YAAY,OAAO,cAAc;AAAA,QACnC,CAAC;AACD,aAAK,UAAU,IAAI;AAAA,MACrB,SAAS,GAAG;AACV,aAAK,CAAC;AAAA,MACR;AAAA,IACF,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,iBACE,KACA,WACA,WACQ;AACR,QAAI,IAAI,WAAW,EAAG,QAAO;AAE7B,UAAM,QAAQ,CAAC,QAA2C;AACxD,UAAI;AACF,YAAI,IAAI,SAAS,OAAQ,QAAO;AAChC,YAAI,IAAI,SAAS,MAAM;AACrB,iBAAO,UAAU,KAAK,SAAS;AAAA,QACjC;AACA,YAAI,IAAI,SAAS,SAAS,IAAI,SAAS,YAAY;AACjD,iBAAO,WAAW,KAAK,IAAI,GAAG;AAAA,QAChC;AACA,eAAO;AAAA,MACT,QAAQ;AACN,eAAO;AAAA,MACT;AAAA,IACF;AAGA,WACE,MAAM,SAAS,MACd,UAAU,SAAS,QACpB,KAAK,IAAI,SAAS,WACjB,KAAK,IAAI,SAAS,SAAS,KAAK,IAAI,SAAS,cAC1C,MAAM,KAAK,GAAG,IACd,UACH,UAAU,SAAS,OAAO,MAAM,EAAE,MAAM,KAAK,CAAC,IAAI,SACnD,MAAM,EAAE,MAAM,OAAO,CAAC,KACtB;AAAA,EAEJ;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,MAAM,gBAA+B,YAA2B;AACpE,QAAI,KAAK,SAAU;AAEnB,UAAM,cAAc;AACpB,QAAI;AAKJ,QAAI,yBAAwC;AAC5C,QAAI,yBAAyB,KAAK;AAElC,UAAME,SAAQ,CAAC,OACb,IAAI,QAAc,CAAC,YAAY,WAAW,SAAS,EAAE,CAAC;AAExD,aAAS,UAAU,GAAG,WAAW,aAAa,WAAW;AACvD,UAAI;AAUF,cAAM,UACJ,2BAA2B,SACvB,QACA,2BAA2B,OACzB,QACA,2BAA2B,QACzB;AAAA;AAAA,UACe;AAAA;AAEzB,cAAM,KAAK,QAAQ;AAGnB,cAAM,SAAS,KAAK,WAAW;AAC/B,cAAM,QAAQ;AACd,cAAM,YAAY;AAElB,cAAM,SAAS,aAAa;AAAA,UAC1B;AAAA,UACA,SAAS;AAAA,UACT;AAAA,UACA,YAAY;AAAA,UACZ;AAAA,UACA,cAAc;AAAA,UACd,cAAc;AAAA,QAChB,CAAC;AACD,cAAM,aAAyB,GAAG,KAAK,IAAI,MAAM;AAEjD,cAAM,eAAe,IAAI,QAAuB,CAAC,SAAS,WAAW;AACnE,gBAAM,IAAI,WAAW,MAAM;AACzB,iBAAK,QAAQ,OAAO,UAAU;AAC9B,mBAAO,IAAI,MAAM,oCAAoC,CAAC;AAAA,UACxD,GAAG,GAAM;AACT,eAAK,QAAQ,IAAI,YAAY;AAAA,YAC3B,SAAS,CAAC,MAAM;AACd,2BAAa,CAAC;AACd,sBAAQ,CAAC;AAAA,YACX;AAAA,YACA,QAAQ,CAAC,MAAM;AACb,2BAAa,CAAC;AACd,qBAAO,CAAC;AAAA,YACV;AAAA,UACF,CAAC;AAAA,QACH,CAAC;AAKD,qBAAa,MAAM,MAAM;AAAA,QAGzB,CAAC;AAED,aAAK,UAAU,MAAM;AACrB,cAAM,aAAa,MAAM;AAGzB,cAAM,OAAO,WAAW,OAAO;AAC/B,YAAI,SAAS,MAAM;AACjB,gBAAM,IAAI;AAAA,YACR,4DAA4D,KAAK,SAAS,EAAE,CAAC;AAAA,UAC/E;AACF,cAAM,UAAU,OAAO;AAGvB,cAAM,WAAW,KAAK;AAAA,UACpB,WAAW;AAAA,UACX,WAAW,OAAO;AAAA,UAClB,YAAY,IAAO,EAAE,MAAM,OAAO,IAAI,EAAE,MAAM,KAAK;AAAA,QACrD;AACA,cAAM,QAAQ,WAAW,UAAU,OAAO;AAC1C,YAAI,CAAC,MAAO,OAAM,IAAI,MAAM,wCAAwC;AACpE,aAAK,QAAQ;AAGb,YAAI,YAAY,EAAM,MAAK,MAAM,EAAE,MAAM,OAAO;AAAA,iBACvC,YAAY,EAAM,MAAK,MAAM,EAAE,MAAM,KAAK;AAAA,iBAC1C,YAAY;AACnB,eAAK,MAAM;AAAA,YACT,MAAM;AAAA,YACN,KAAK,aAAa,OAAO,KAAK,KAAK,QAAQ;AAAA,UAC7C;AAAA,iBACO,YAAY;AACnB,eAAK,MAAM;AAAA,YACT,MAAM;AAAA,YACN,KAAK,aAAa,OAAO,KAAK,KAAK,QAAQ;AAAA,UAC7C;AAAA;AAEA,gBAAM,IAAI;AAAA,YACR,qCAAqC,QAAQ,SAAS,EAAE,CAAC;AAAA,UAC3D;AAGF,cAAM,WAAW,aAAa,GAAG,KAAK,KAAK,QAAQ,GAAG,KAAK,EAAE;AAC7D,cAAM,WAAW,aAAa,GAAG,KAAK,KAAK,QAAQ,GAAG,KAAK,EAAE;AAC7D,cAAM,WAAW,cAAc,UAAU,QAAQ;AAEjD,aAAK,SAAS,cAAc;AAAA,UAC1B,UAAU,KAAK,KAAK;AAAA,UACpB;AAAA,UACA;AAAA,UACA,gBAAgB,SAAS;AAAA,UACzB,gBAAgB,SAAS;AAAA,UACzB,iBAAiB,SAAS,UAAU,GAAG,GAAG;AAAA,QAC5C,CAAC;AASD,cAAM,WACJ,YAAY,IAAO,EAAE,MAAM,OAAO,IAAI,EAAE,MAAM,KAAK;AAErD,cAAM,aAAa,MAAM,KAAK,UAAU;AAAA,UACtC,OAAO;AAAA,UACP,YAAY;AAAA,UACZ,cAAc;AAAA,UACd,cAAc;AAAA,UACd,mBAAmB;AAAA,UACnB,YAAY;AAAA,UACZ,WAAW;AAAA,QACb,CAAC;AAED,cAAM,WAAW,KAAK;AAAA,UACpB,WAAW;AAAA,UACX,WAAW,OAAO;AAAA,UAClB;AAAA,QACF;AAKA,aAAK,SAAS,eAAe;AAAA,UAC3B,aAAa,SAAS;AAAA,UACtB,cAAc,SAAS,UAAU,GAAG,GAAG;AAAA,UACvC,eAAe,SAAS,WAAW,OAAO;AAAA,QAC5C,CAAC;AAID,YAAI,SAAS,WAAW,GAAG;AACzB,gBAAM,IAAI;AAAA,YACR;AAAA,UACF;AAAA,QACF;AAEA,YAAI,CAAC,SAAS,WAAW,OAAO,GAAG;AACjC,gBAAM,UACJ,SAAS,SAAS,IAAI,SAAS,UAAU,GAAG,GAAG,IAAI;AACrD,gBAAM,IAAI;AAAA,YACR,qDAAqD,SAAS,MAAM,cAAc,OAAO;AAAA,UAC3F;AAAA,QACF;AAEA,aAAK,WAAW;AAEhB,aAAK,gBAAgB;AACrB;AAAA,MACF,SAAS,GAAG;AACV,oBAAY;AACZ,aAAK,WAAW;AAEhB,cAAM,MACJ,KAAK,OAAO,MAAM,YAAY,aAAa,IACvC,OAAQ,EAAU,OAAO,IACzB,OAAO,CAAC;AACd,cAAM,uBACJ,IAAI,SAAS,cAAc,KAAK,IAAI,SAAS,aAAa;AAC5D,cAAM,8BACJ,IAAI,SAAS,2BAA2B,KACxC,IAAI,SAAS,0BAA0B,KACvC,IAAI,SAAS,wBAAwB,KACrC,IAAI,SAAS,YAAY,KACzB,IAAI,SAAS,OAAO;AAItB,YAAI,+BAA+B,2BAA2B,KAAK;AACjE,mCAAyB;AAAA,QAC3B;AAIA,YAAI,6BAA6B;AAC/B,cAAI,2BAA2B,YAAY;AACzC,qCAAyB;AAAA,UAC3B,WAAW,2BAA2B,OAAO;AAC3C,qCAAyB;AAAA,UAC3B,WAAW,2BAA2B,MAAM;AAC1C,qCAAyB;AAAA,UAC3B;AAAA,QACF;AACA,YAAI;AACF,gBAAM,KAAK,MAAM;AAAA,YACf,QAAQ,uBACJ,oBACA,8BACE,6BACA;AAAA,UACR,CAAC;AAAA,QACH,QAAQ;AAAA,QAER;AAIA,YAAI,sBAAsB;AACxB,gBAAM;AAAA,QACR;AAEA,YAAI,UAAU,aAAa;AAEzB,gBAAMA,OAAM,IAAI;AAChB;AAAA,QACF;AACA,cAAM;AAAA,MACR;AAAA,IACF;AAGA,UAAM,qBAAqB,QAAQ,YAAY,IAAI,MAAM,OAAO,SAAS,CAAC;AAAA,EAC5E;AACF;;;ACrmIA,eAAsB,eAAe,SAAmD;AACtF,QAAM,EAAE,SAAS,SAAS,KAAK,UAAU,UAAU,cAAc,aAAa,IAAI;AAGlF,QAAM,WAAW,MAAM,IAAI,kBAAkB,OAAO;AACpD,QAAM,SAAS,SAAS,QAAQ,KAAK,CAAC,MAAM,EAAE,YAAY,OAAO;AAEjE,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI,MAAM,kBAAkB,OAAO,8BAA8B,OAAO,EAAE;AAAA,EAClF;AAGA,QAAM,UAAU,aAAa;AAAA,IAC3B,MAAM;AAAA,IACN,GAAI,aAAa,SAAY,EAAE,MAAM,SAAS,IAAI,CAAC;AAAA,IACnD,UAAU;AAAA,IACV,UAAU;AAAA,IACV;AAAA,IACA,QAAQ;AAAA,EACV,CAAC;AAGD,QAAMC,iBAAwC;AAAA,IAC5C,SAAS;AAAA,IACT,SAAS;AAAA,IACT,SAAS;AAAA,IACT,SAAS;AAAA,EACX;AAEA,QAAM,aAAaA,eAAc,OAAO,YAAY,KAAK,OAAO,aAAa,YAAY;AAEzF,QAAM,SAAsB;AAAA,IAC1B,KAAK;AAAA,IACL,WAAW;AAAA,IACX,OAAO;AAAA,MACL,OAAO;AAAA,MACP,OAAO,OAAO;AAAA,MACd,QAAQ,OAAO;AAAA,MACf,KAAK,OAAO;AAAA,IACd;AAAA,EACF;AAGA,MAAI,OAAO,UAAU,GAAG;AAEtB,WAAO,QAAQ;AAAA,MACb,OAAO;AAAA;AAAA,MACP,YAAY;AAAA;AAAA,IACd;AAAA,EACF;AAEA,SAAO;AACT;AAOA,eAAsB,iCACpB,SACA,KACA,UACA,WAAmB,KACnB,eAAuB,SACvB,eAAuB,IACgB;AAEvC,QAAM,WAAW,MAAM,IAAI,kBAAkB,OAAO;AACpD,QAAM,UAAwC,CAAC;AAG/C,QAAMA,iBAAwC;AAAA,IAC5C,SAAS;AAAA,IACT,SAAS;AAAA,IACT,SAAS;AAAA,IACT,SAAS;AAAA,EACX;AAGA,aAAW,UAAU,SAAS,SAAS;AACrC,UAAM,aAAaA,eAAc,OAAO,YAAY,KAAK,OAAO,aAAa,YAAY;AACzF,UAAM,cAAc,OAAO,QAAQ,OAAO,CAAC,EAAE,YAAY,IAAI,OAAO,QAAQ,MAAM,CAAC;AAEnF,UAAM,SAAqC;AAAA,MACzC,IAAI,OAAO;AAAA,MACX,MAAM,GAAG,WAAW;AAAA,MACpB,aAAa,GAAG,WAAW,aAAa,OAAO,KAAK,IAAI,OAAO,MAAM,MAAM,OAAO,SAAS;AAAA,MAC3F,OAAO;AAAA,QACL,OAAO;AAAA,QACP,OAAO,OAAO;AAAA,QACd,QAAQ,OAAO;AAAA,QACf,KAAK,OAAO;AAAA,MACd;AAAA,MACA,WAAW;AAAA,MACX,kBAAkB;AAAA,IACpB;AAGA,QAAI,OAAO,UAAU,GAAG;AACtB,aAAO,QAAQ;AAAA,QACb,OAAO;AAAA;AAAA,QACP,YAAY;AAAA;AAAA,MACd;AAAA,IACF;AAEA,YAAQ,KAAK,MAAM;AAAA,EACrB;AAEA,SAAO;AACT;AA0BO,IAAM,WAAN,MAAe;AAAA,EACZ;AAAA,EACA;AAAA,EACA,SAAS;AAAA,EACT;AAAA,EAER,YAAY,SAA0B;AACpC,SAAK,MAAM,QAAQ;AACnB,SAAK,UAAU,QAAQ;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,QAAuB;AAC3B,QAAI,KAAK,QAAQ;AACf,YAAM,IAAI,MAAM,iCAAiC;AAAA,IACnD;AAGA,SAAK,UAAU,MAAM,KAAK,IAAI,kBAAkB,KAAK,OAAO;AAC5D,SAAK,SAAS;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,UAAU,WAAkC;AAChD,QAAI,CAAC,KAAK,QAAQ;AAChB,YAAM,IAAI,MAAM,6BAA6B;AAAA,IAC/C;AAEA,QAAI,CAAC,KAAK,SAAS;AACjB,YAAM,IAAI,MAAM,iDAAiD;AAAA,IACnE;AACA,UAAM,KAAK,QAAQ,UAAU,SAAS;AAAA,EACxC;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,OAAsB;AAC1B,QAAI,CAAC,KAAK,OAAQ;AAElB,UAAM,KAAK,SAAS,KAAK;AACzB,SAAK,UAAU;AACf,SAAK,SAAS;AAAA,EAChB;AAAA,EAEA,WAAoB;AAClB,WAAO,KAAK;AAAA,EACd;AACF;AAMO,IAAM,uBAAN,MAA2B;AAAA,EACxB;AAAA,EACA,aAAa;AAAA,EACb;AAAA,EAER,YAAY,KAAyB;AACnC,SAAK,MAAM;AAAA,EACb;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,UAAU,SAAuD;AACrE,QAAI,KAAK,WAAY;AAGrB,UAAM,KAAK,IAAI,gBAAgB;AAG/B,SAAK,iBAAiB,CAAC,UAAwB,QAAQ,KAAK;AAC5D,SAAK,IAAI,OAAO,GAAG,SAAS,KAAK,cAAc;AAE/C,SAAK,aAAa;AAAA,EACpB;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,cAA6B;AACjC,QAAI,CAAC,KAAK,WAAY;AAEtB,UAAM,KAAK,IAAI,kBAAkB;AACjC,QAAI,KAAK,gBAAgB;AACvB,WAAK,IAAI,OAAO,eAAe,SAAS,KAAK,cAAc;AAC3D,WAAK,iBAAiB;AAAA,IACxB;AACA,SAAK,aAAa;AAAA,EACpB;AAAA,EAEA,eAAwB;AACtB,WAAO,KAAK;AAAA,EACd;AACF;AAsBA,gBAAuB,mBACrB,KACA,SACA,SACA,SAWgB;AAChB,QAAM,cAAc,IAAI,oBAAoB;AAAA,IAC1C,QAAQ,IAAI;AAAA,IACZ;AAAA,IACA;AAAA,IACA;AAAA,IACA,GAAI,SAAS,YAAY,SAAY,EAAE,SAAS,QAAQ,QAAQ,IAAI,CAAC;AAAA,IACrE,QAAQ,IAAI;AAAA,EACd,CAAC;AAED,MAAI,iBAAoE;AACxE,MAAI,aAA4B;AAChC,MAAI,kBAAiC;AACrC,MAAI,gBAAgB;AACpB,MAAI,SAAS;AAEb,QAAM,UAAU,CAAC,WAAkB;AACjC,aAAS;AAAA,EAGX;AAEA,QAAM,UAAU,MAAM;AACpB,aAAS;AAAA,EACX;AAEA,MAAI;AAEF,gBAAY,GAAG,SAAS,OAAO;AAC/B,gBAAY,GAAG,SAAS,OAAO;AAG/B,UAAM,YAAY,MAAM;AAIxB,UAAM,wBAAwB,IAAI,QAAoD,CAAC,YAAY;AACjG,YAAM,UAAU,CAAC,SAA4E;AAC3F,YAAI,KAAK,cAAc,CAAC,gBAAgB;AAItC,2BAAiB,EAAE,KAAK,MAAM,KAAK,KAAK;AACxC,sBAAY,eAAe,mBAA0B,OAAO;AAC5D,kBAAQ,cAAc;AAAA,QACxB;AAAA,MACF;AACA,kBAAY,GAAG,mBAA0B,OAAO;AAGhD,iBAAW,MAAM;AACf,YAAI,CAAC,gBAAgB;AACnB,2BAAiB,EAAE,KAAK,MAAM,KAAK,KAAK;AACxC,sBAAY,eAAe,mBAA0B,OAAO;AAC5D,kBAAQ,cAAc;AAAA,QACxB;AAAA,MACF,GAAG,GAAI;AAAA,IACT,CAAC;AAGD,UAAM,QAAQ,KAAK;AAAA,MACjB;AAAA,MACA,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,GAAI,CAAC;AAAA,IACpD,CAAC;AAGD,UAAM,aAQD,CAAC;AAIN,UAAM,kBAAkB;AAMxB,QAAI,qBAAqB;AACzB,QAAI,kBAAkB;AAEtB,QAAI,eAAoC;AAGxC,gBAAY,GAAG,mBAAmB,CAAC,SAK7B;AACJ,UAAI,OAAQ;AAEZ,UAAI,sBAAsB,CAAC,KAAK,YAAY;AAC1C;AAAA,MACF;AAEA,UAAI,sBAAsB,KAAK,YAAY;AACzC,6BAAqB;AAAA,MACvB;AAEA,iBAAW,KAAK;AAAA,QACd,OAAO;AAAA,QACP,MAAM,KAAK;AAAA,QACX,OAAO;AAAA,QACP,YAAY;AAAA,QACZ,cAAc,KAAK;AAAA,QACnB,WAAW,KAAK;AAAA,QAChB,YAAY,KAAK;AAAA,MACnB,CAAC;AAED,UAAI,WAAW,SAAS,iBAAiB;AACvC,mBAAW,SAAS;AACpB,6BAAqB;AAErB,cAAM,MAAM,KAAK,IAAI;AACrB,YAAI,MAAM,kBAAkB,KAAM;AAChC,4BAAkB;AAClB,cAAI,QAAQ;AAAA,YACV,uDAAuD,OAAO,YAAY,OAAO;AAAA,UACnF;AAAA,QACF;AAAA,MACF;AAEA,UAAI,cAAc;AAChB,qBAAa;AACb,uBAAe;AAAA,MACjB;AAAA,IACF,CAAC;AAGD,gBAAY,GAAG,cAAc,CAAC,UAAkB;AAC9C,UAAI,OAAQ;AAIZ,UAAI,CAAC,YAAY;AACf,qBAAa;AACb,0BAAkB;AAAA,MACpB;AAEA,iBAAW,KAAK;AAAA,QACd,OAAO;AAAA,QACP,MAAM;AAAA,QACN,OAAO;AAAA,QACP,YAAY;AAAA,QACZ,cAAc;AAAA,MAChB,CAAC;AAED,UAAI,WAAW,SAAS,iBAAiB;AACvC,mBAAW,OAAO,GAAG,WAAW,SAAS,eAAe;AAAA,MAC1D;AAEA,UAAI,cAAc;AAChB,qBAAa;AACb,uBAAe;AAAA,MACjB;AAAA,IACF,CAAC;AAED,oBAAgB;AAGhB,WAAO,CAAC,QAAQ;AACd,UAAI,WAAW,SAAS,GAAG;AACzB,cAAM,QAAQ,WAAW,MAAM;AAC/B,cAAM;AAAA,MACR,OAAO;AAEL,cAAM,IAAI,QAAc,CAAC,YAAY;AACnC,yBAAe;AAEf,qBAAW,MAAM;AACf,gBAAI,iBAAiB,SAAS;AAC5B,6BAAe;AACf,sBAAQ;AAAA,YACV;AAAA,UACF,GAAG,GAAI;AAAA,QACT,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF,UAAE;AAEA,aAAS;AACT,QAAI;AACF,YAAM,YAAY,KAAK;AAAA,IACzB,QAAQ;AAAA,IAER;AACA,gBAAY,eAAe,SAAS,OAAO;AAC3C,gBAAY,eAAe,SAAS,OAAO;AAAA,EAC7C;AACF;;;ACphBA,SAAS,gBAAAC,qBAAoB;AAC7B,SAAS,aAAa;AACtB,YAAYC,UAAS;AACrB,YAAYC,YAAW;AACvB,YAAY,YAAY;;;ACiBxB,SAAS,MAAM,WAA0B,WAAuC;AAC9E,SAAO,GAAG,SAAS,IAAI,cAAc,SAAS,SAAS,MAAM;AAC/D;AAEA,IAAe,WAAf,MAA4C;AAAA,EAC1B;AAAA,EACA;AAAA,EACA;AAAA,EAIR;AAAA,EAEE,YAAY,WAA0B,WAA0B;AACxE,SAAK,YAAY;AACjB,SAAK,YAAY;AACjB,SAAK,MAAM,MAAM,WAAW,SAAS;AAAA,EACvC;AAAA,EAEA,MAAM,eAAe,KAAwC;AAS3D,SAAK,cAAc;AAEnB,QAAI,IAAI,OAAO,aAAa,MAAM,MAAO;AAGzC;AAAA,EACF;AAAA,EAEA,gBAAsB;AACpB,QAAI,KAAK,gBAAgB;AACvB,oBAAc,KAAK,cAAc;AACjC,WAAK,iBAAiB;AAAA,IACxB;AAAA,EACF;AAKF;AAEA,IAAM,oBAAoB,OAAO,KAAK,CAAC,GAAM,GAAM,GAAM,CAAI,CAAC;AAE9D,SAAS,kBAAkB,MAA2C;AACpE,QAAM,UAAU,KAAK,OAAO,CAAC,MAAmB,CAAC,CAAC,KAAK,EAAE,SAAS,CAAC;AACnE,MAAI,QAAQ,WAAW,EAAG,QAAO;AACjC,QAAM,QAAkB,CAAC;AACzB,aAAW,KAAK,SAAS;AACvB,UAAM,KAAK,mBAAmB,CAAC;AAAA,EACjC;AACA,SAAO,OAAO,OAAO,KAAK;AAC5B;AAEA,IAAM,WAAN,cAAuB,SAAS;AAAA,EACd,eAAe;AAAA,EACf,WAAW;AAAA,EAEnB,MAAqB;AAAA,EACrB,MAAqB;AAAA,EAEtB,YAAY,WAA0B;AAC3C,UAAM,WAAW,MAAM;AAAA,EACzB;AAAA,EAEA,qBAAqB,kBAAgC;AACnD,UAAM,OAAO,yBAAyB,gBAAgB;AACtD,eAAW,OAAO,MAAM;AACtB,UAAI,IAAI,SAAS,EAAG;AACpB,YAAM,WAAW,IAAI,CAAC,KAAK,KAAK;AAChC,UAAI,YAAY,KAAK,CAAC,KAAK,IAAK,MAAK,MAAM;AAC3C,UAAI,YAAY,KAAK,CAAC,KAAK,IAAK,MAAK,MAAM;AAC3C,UAAI,KAAK,OAAO,KAAK,IAAK;AAAA,IAC5B;AAAA,EACF;AAAA,EAEA,UAAmD;AACjD,QAAI,OAAO;AAMX,QAAI,KAAK,OAAO,KAAK,IAAI,UAAU,GAAG;AACpC,YAAM,iBAAiB,OAAO,KAAK,CAAC,KAAK,IAAI,CAAC,GAAI,KAAK,IAAI,CAAC,GAAI,KAAK,IAAI,CAAC,CAAE,CAAC,EAAE,SAAS,KAAK;AAC7F,cAAQ,qBAAqB,cAAc;AAAA,IAC7C;AAEA,QAAI,KAAK,OAAO,KAAK,KAAK;AACxB,cAAQ,yBAAyB,KAAK,IAAI,SAAS,QAAQ,CAAC,IAAI,KAAK,IAAI,SAAS,QAAQ,CAAC;AAC3F,aAAO,EAAE,MAAM,cAAc,KAAK;AAAA,IACpC;AACA,WAAO,EAAE,MAAM,cAAc,MAAM;AAAA,EACrC;AAAA,EAEA,yBAAwC;AACtC,WAAO,eAAe,KAAK,KAAK,KAAK,GAAG;AAAA,EAC1C;AACF;AAEA,IAAM,WAAN,cAAuB,SAAS;AAAA,EACd,eAAe;AAAA,EACf,WAAW;AAAA,EAEnB,MAAqB;AAAA,EACrB,MAAqB;AAAA,EACrB,MAAqB;AAAA,EAEtB,YAAY,WAA0B;AAC3C,UAAM,WAAW,MAAM;AAAA,EACzB;AAAA,EAEA,qBAAqB,kBAAgC;AACnD,UAAM,MAAM,qBAAqB,gBAAgB;AACjD,UAAM,MAAM,qBAAqB,gBAAgB;AACjD,UAAM,MAAM,qBAAqB,gBAAgB;AACjD,QAAI,OAAO,CAAC,KAAK,IAAK,MAAK,MAAM;AACjC,QAAI,OAAO,CAAC,KAAK,IAAK,MAAK,MAAM;AACjC,QAAI,OAAO,CAAC,KAAK,IAAK,MAAK,MAAM;AAAA,EACnC;AAAA,EAEA,UAAmD;AAEjD,QAAI,OAAO;AACX,QAAI,KAAK,OAAO,KAAK,OAAO,KAAK,KAAK;AACpC,aAAO,aAAa,KAAK,IAAI,SAAS,QAAQ,CAAC,cAAc,KAAK,IAAI,SAAS,QAAQ,CAAC,cAAc,KAAK,IAAI,SAAS,QAAQ,CAAC;AACjI,aAAO,EAAE,MAAM,cAAc,KAAK;AAAA,IACpC;AACA,WAAO,EAAE,MAAM,cAAc,MAAM;AAAA,EACrC;AAAA,EAEA,yBAAwC;AACtC,WAAO,eAAe,KAAK,KAAK,KAAK,KAAK,KAAK,GAAG;AAAA,EACpD;AACF;AAEO,SAAS,eAAe,WAA0B,WAAoC;AAC3F,MAAI,cAAc,OAAQ,QAAO,IAAI,SAAS,SAAS;AACvD,SAAO,IAAI,SAAS,SAAS;AAC/B;;;ADpJA,IAAM,oBAAN,MAA2B;AAAA,EACR;AAAA,EACA,QAAa,CAAC;AAAA,EACvB;AAAA,EAKA,SAAS;AAAA,EAEjB,YAAY,UAAkB;AAC5B,SAAK,WAAW,KAAK,IAAI,GAAG,WAAW,CAAC;AAAA,EAC1C;AAAA,EAEA,KAAK,MAAe;AAClB,QAAI,KAAK,OAAQ;AACjB,QAAI,KAAK,SAAS;AAChB,YAAM,EAAE,QAAQ,IAAI,KAAK;AACzB,WAAK,UAAU;AACf,cAAQ,EAAE,OAAO,MAAM,MAAM,MAAM,CAAC;AACpC;AAAA,IACF;AACA,SAAK,MAAM,KAAK,IAAI;AACpB,QAAI,KAAK,MAAM,SAAS,KAAK,UAAU;AACrC,WAAK,MAAM,OAAO,GAAG,KAAK,MAAM,SAAS,KAAK,QAAQ;AAAA,IACxD;AAAA,EACF;AAAA,EAEA,QAAc;AACZ,QAAI,KAAK,OAAQ;AACjB,SAAK,SAAS;AACd,QAAI,KAAK,SAAS;AAChB,YAAM,EAAE,QAAQ,IAAI,KAAK;AACzB,WAAK,UAAU;AACf,cAAQ,EAAE,OAAO,QAAkB,MAAM,KAAK,CAAC;AAAA,IACjD;AAAA,EACF;AAAA,EAEA,MAAM,OAAmC;AACvC,QAAI,KAAK,OAAQ,QAAO,EAAE,OAAO,QAAkB,MAAM,KAAK;AAC9D,UAAM,OAAO,KAAK,MAAM,MAAM;AAC9B,QAAI,SAAS,OAAW,QAAO,EAAE,OAAO,MAAM,MAAM,MAAM;AAC1D,WAAO,MAAM,IAAI,QAA2B,CAAC,YAAY;AACvD,WAAK,UAAU,EAAE,QAAQ;AAAA,IAC3B,CAAC;AAAA,EACH;AACF;AASA,IAAM,qBAAN,MAA4B;AAAA,EACT;AAAA,EACA,SAAS,oBAAI,IAAkC;AAAA,EACxD,SAAkD;AAAA,EAClD,UAAU;AAAA,EACV,cAAoC;AAAA,EAE5C,YAAY,MAAwB;AAClC,SAAK,OAAO;AAAA,EACd;AAAA,EAEA,QAAc;AACZ,QAAI,KAAK,QAAS;AAClB,SAAK,UAAU;AACf,SAAK,SAAS,KAAK,KAAK,aAAa;AAErC,SAAK,eAAe,YAAY;AAC9B,UAAI;AACF,yBAAiB,SAAS,KAAK,QAAS;AACtC,cAAI;AACF,iBAAK,KAAK,UAAU,KAAK;AAAA,UAC3B,QAAQ;AAAA,UAER;AACA,qBAAW,KAAK,KAAK,OAAO,OAAO,GAAG;AACpC,cAAE,KAAK,KAAK;AAAA,UACd;AAAA,QACF;AAAA,MACF,SAAS,GAAG;AACV,aAAK,KAAK,UAAU,CAAC;AAAA,MACvB,UAAE;AACA,mBAAW,KAAK,KAAK,OAAO,OAAO,EAAG,GAAE,MAAM;AAC9C,aAAK,OAAO,MAAM;AAAA,MACpB;AAAA,IACF,GAAG;AAAA,EACL;AAAA,EAEA,UAAU,IAA8C;AACtD,UAAM,IAAI,IAAI,kBAAqB,KAAK,KAAK,aAAa;AAC1D,SAAK,OAAO,IAAI,IAAI,CAAC;AACrB,UAAM,OAAO;AACb,YAAQ,mBAAmB;AACzB,UAAI;AACF,eAAO,MAAM;AACX,gBAAM,IAAI,MAAM,EAAE,KAAK;AACvB,cAAI,EAAE,KAAM;AACZ,gBAAM,EAAE;AAAA,QACV;AAAA,MACF,UAAE;AACA,UAAE,MAAM;AACR,aAAK,OAAO,OAAO,EAAE;AAAA,MACvB;AAAA,IACF,GAAG;AAAA,EACL;AAAA,EAEA,MAAM,OAAsB;AAC1B,QAAI,CAAC,KAAK,QAAS;AACnB,SAAK,UAAU;AACf,UAAM,MAAM,KAAK;AACjB,SAAK,SAAS;AACd,eAAW,KAAK,KAAK,OAAO,OAAO,EAAG,GAAE,MAAM;AAC9C,SAAK,OAAO,MAAM;AAClB,QAAI;AACF,YAAM,KAAK,OAAO,MAAgB;AAAA,IACpC,QAAQ;AAAA,IAER;AACA,QAAI;AACF,YAAM,KAAK;AAAA,IACb,QAAQ;AAAA,IAER;AACA,SAAK,cAAc;AAAA,EACrB;AACF;AAEA,SAAS,QAAQ,OAA2B,cAAgC;AAC1E,MAAI,SAAS,KAAM,QAAO;AAC1B,QAAM,IAAI,MAAM,KAAK,EAAE,YAAY;AACnC,MAAI,MAAM,OAAO,MAAM,UAAU,MAAM,SAAS,MAAM,KAAM,QAAO;AACnE,MAAI,MAAM,OAAO,MAAM,WAAW,MAAM,QAAQ,MAAM,MAAO,QAAO;AACpE,SAAO;AACT;AAwCO,IAAM,qBAAN,MAAM,4BAA2BC,cAKrC;AAAA,EACO;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,SAAS;AAAA,EACT;AAAA;AAAA,EAGA,kBAAiE,CAAC;AAAA,EAClE;AAAA,EACA,aAAa,oBAAI,IAAkD;AAAA;AAAA,EAC1D,aAAa;AAAA,EACb,mBAAmB;AAAA;AAAA;AAAA,EAG5B,mBAAmB,oBAAI,IAAY;AAAA;AAAA,EACnC,qBAAqB;AAAA;AAAA,EACrB;AAAA;AAAA,EACA,iBAIG;AAAA;AAAA,EAEH,kBAAkB,oBAAI,IA4B5B;AAAA,EAEM,qBAA8B;AACpC,UAAM,MAAM,KAAK,IAAI,OAAO,eAAe;AAC3C,WAAO,IAAI,aAAa,QAAQ,QAAQ,IAAI,qBAAqB,KAAK;AAAA,EACxE;AAAA,EAEQ,aAAa,SAAuB;AAC1C,QAAI,CAAC,KAAK,mBAAmB,EAAG;AAChC,SAAK,OAAO,MAAM,wBAAwB,OAAO,EAAE;AAAA,EACrD;AAAA;AAAA,EAEQ,oBAA0C;AAAA,EAC1C,oBAAyC;AAAA,EACzC,qBAAqB;AAAA,EACrB,oBAA0C;AAAA,EAC1C,oBAAyC;AAAA,EACzC,qBAAqB;AAAA;AAAA,EAErB,WAAW;AAAA,EACX,YAKG;AAAA,EACH,oBAAmC;AAAA;AAAA,EAEnC,sBAWG;AAAA;AAAA,EAGH,eAOI;AAAA,EACJ;AAAA,EAER,OAAe,eAAe,GAAoB;AAEhD,WAAO,EAAE,UAAU,KAAK,EAAE,CAAC,MAAM,QAAS,EAAE,CAAC,IAAK,SAAU;AAAA,EAC9D;AAAA,EAEA,OAAe,sBACb,GACoE;AAKpE,QAAI,EAAE,SAAS,EAAG,QAAO;AACzB,QAAI,CAAC,oBAAmB,eAAe,CAAC,EAAG,QAAO;AAElD,UAAM,gBAAiB,EAAE,CAAC,KAAM,IAAK;AACrC,UAAM,cAAc;AAAA,MAClB;AAAA,MAAO;AAAA,MAAO;AAAA,MAAO;AAAA,MAAO;AAAA,MAAO;AAAA,MAAO;AAAA,MAAO;AAAA,MAAO;AAAA,MAAO;AAAA,MAC/D;AAAA,MAAO;AAAA,MAAM;AAAA,IACf;AACA,UAAM,aAAa,YAAY,aAAa,KAAK;AACjD,QAAI,CAAC,WAAY,QAAO;AAExB,UAAM,iBAAkB,EAAE,CAAC,IAAK,MAAS,IAAO,EAAE,CAAC,KAAM,IAAK;AAC9D,UAAM,WAAW,kBAAkB,IAAI,IAAI;AAG3C,UAAM,UAAW,EAAE,CAAC,KAAM,IAAK;AAC/B,UAAM,kBAAkB,UAAU;AAElC,UAAM,MACH,mBAAmB,KAAO,iBAAiB,IAAM,iBAAiB;AACrE,UAAM,YAAY,OAAO,KAAK,CAAE,OAAO,IAAK,KAAM,MAAM,GAAI,CAAC,EAAE;AAAA,MAC7D;AAAA,IACF;AACA,WAAO,EAAE,YAAY,UAAU,UAAU;AAAA,EAC3C;AAAA,EAEA,OAAe,yBACb,iBACsC;AACtC,UAAM,IAAI,gBAAgB,MAAM,sCAAsC;AACtE,QAAI,CAAC,EAAG,QAAO;AACf,UAAM,MAAM,OAAO,SAAS,EAAE,CAAC,GAAI,EAAE;AACrC,UAAM,OAAO,OAAO,SAAS,EAAE,CAAC,GAAI,EAAE;AACtC,QAAI,CAAC,OAAO,SAAS,GAAG,KAAK,CAAC,OAAO,SAAS,IAAI,EAAG,QAAO;AAC5D,WAAO,EAAE,KAAK,KAAK;AAAA,EACrB;AAAA,EAEA,OAAe,gBAAgB,MAAwB;AAErD,UAAM,OAAiB,CAAC;AACxB,UAAM,MAAM,KAAK;AACjB,UAAM,gBAAgB,CAACC,OAAsB;AAE3C,UAAIA,KAAI,KAAK,OAAO,KAAKA,EAAC,MAAM,KAAQ,KAAKA,KAAI,CAAC,MAAM,GAAM;AAC5D,YAAI,KAAKA,KAAI,CAAC,MAAM,EAAM,QAAO;AACjC,YAAIA,KAAI,KAAK,OAAO,KAAKA,KAAI,CAAC,MAAM,KAAQ,KAAKA,KAAI,CAAC,MAAM;AAC1D,iBAAO;AAAA,MACX;AACA,aAAO;AAAA,IACT;AAEA,QAAI,IAAI;AAER,WAAO,IAAI,KAAK;AACd,YAAM,KAAK,cAAc,CAAC;AAC1B,UAAI,GAAI;AACR;AAAA,IACF;AACA,WAAO,IAAI,KAAK;AACd,YAAM,KAAK,cAAc,CAAC;AAC1B,UAAI,CAAC,IAAI;AACP;AACA;AAAA,MACF;AACA,YAAM,WAAW,IAAI;AACrB,UAAI,IAAI;AACR,aAAO,IAAI,KAAK;AACd,cAAM,MAAM,cAAc,CAAC;AAC3B,YAAI,IAAK;AACT;AAAA,MACF;AACA,UAAI,WAAW,GAAG;AAChB,cAAM,MAAM,KAAK,SAAS,UAAU,CAAC;AAErC,YAAI,IAAI,SAAS,EAAG,MAAK,KAAK,GAAG;AAAA,MACnC;AACA,UAAI;AAAA,IACN;AACA,WAAO;AAAA,EACT;AAAA,EAEA,OAAe,gBAAgB,WAAkC;AAC/D,QAAI,CAAC,oBAAmB,eAAe,SAAS,EAAG,QAAO;AAC1D,QAAI,UAAU,SAAS,EAAG,QAAO;AACjC,UAAM,oBAAoB,UAAU,CAAC,IAAK,OAAU;AACpD,UAAM,YAAY,mBAAmB,IAAI;AACzC,QAAI,UAAU,UAAU,UAAW,QAAO;AAC1C,WAAO,UAAU,SAAS,SAAS;AAAA,EACrC;AAAA,EAEA,YAAY,SAAoC;AAC9C,UAAM;AACN,SAAK,MAAM,QAAQ;AACnB,SAAK,UAAU,QAAQ;AACvB,SAAK,UAAU,QAAQ;AACvB,SAAK,UAAU,QAAQ,WAAW;AAClC,SAAK,aAAa,QAAQ,cAAc;AACxC,SAAK,aAAa,QAAQ,cAAc;AACxC,SAAK,OAAO,QAAQ,QAAQ,WAAW,KAAK,OAAO;AACnD,SAAK,SAAS,QAAQ,UAAU;AAChC,SAAK,gBAAgB,QAAQ,iBAAiB;AAG9C,SAAK,kBAAkB,QAAQ,eAAe,CAAC;AAC/C,SAAK,cAAc,QAAQ,eAAe,KAAK,gBAAgB,SAAS;AAGxE,UAAM,YAAY,KAAK,IAAI,OAAO,aAAa;AAC/C,SAAK,OAAO,eAAe,WAAW,MAAM;AAAA,EAC9C;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,gBAAwB;AAC9B,WAAc,mBAAY,EAAE,EAAE,SAAS,KAAK;AAAA,EAC9C;AAAA;AAAA;AAAA;AAAA,EAKQ,kBAAkB,UAA0B;AAClD,UAAM,WAAW,KAAK,WAAW,IAAI,QAAQ;AAC7C,UAAM,MAAM,KAAK,IAAI;AAGrB,eAAW,CAAC,IAAI,IAAI,KAAK,KAAK,YAAY;AACxC,UAAI,MAAM,KAAK,YAAY,KAAK,kBAAkB;AAChD,aAAK,WAAW,OAAO,EAAE;AAAA,MAC3B;AAAA,IACF;AAEA,QAAI,YAAY,MAAM,SAAS,YAAY,KAAK,kBAAkB;AAChE,aAAO,SAAS;AAAA,IAClB;AAEA,UAAM,QAAQ,KAAK,cAAc;AACjC,SAAK,WAAW,IAAI,UAAU,EAAE,OAAO,WAAW,IAAI,CAAC;AACvD,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKQ,gBAAgB,YAAmD;AACzE,QAAI,CAAC,WAAW,YAAY,EAAE,WAAW,SAAS,EAAG,QAAO;AAE5D,UAAM,SAAiC,CAAC;AACxC,UAAM,QAAQ;AACd,QAAI;AAEJ,YAAQ,QAAQ,MAAM,KAAK,UAAU,OAAO,MAAM;AAChD,YAAM,MAAM,MAAM,CAAC,EAAG,YAAY;AAClC,YAAM,QAAQ,MAAM,CAAC,KAAK,MAAM,CAAC;AACjC,aAAO,GAAG,IAAI;AAAA,IAChB;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKQ,IAAI,MAAsB;AAChC,WAAc,kBAAW,KAAK,EAAE,OAAO,IAAI,EAAE,OAAO,KAAK;AAAA,EAC3D;AAAA;AAAA;AAAA;AAAA,EAKQ,mBACN,YACA,QACA,KACA,UACS;AACT,QAAI,KAAK,gBAAgB,WAAW,EAAG,QAAO;AAE9C,UAAM,SAAS,KAAK,gBAAgB,UAAU;AAC9C,QAAI,CAAC,OAAQ,QAAO;AAEpB,UAAM,EAAE,UAAU,OAAO,OAAO,KAAK,SAAS,SAAS,IAAI;AAG3D,QAAI,CAAC,YAAY,CAAC,SAAS,CAAC,SAAS,CAAC,SAAU,QAAO;AAGvD,UAAM,kBAAkB,KAAK,WAAW,IAAI,QAAQ;AACpD,QAAI,CAAC,mBAAmB,gBAAgB,UAAU,OAAO;AACvD,WAAK,aAAa,0CAA0C,QAAQ,EAAE;AACtE,aAAO;AAAA,IACT;AAGA,eAAW,QAAQ,KAAK,iBAAiB;AACvC,UAAI,aAAa,KAAK,SAAU;AAIhC,YAAM,MAAM,KAAK,IAAI,GAAG,KAAK,QAAQ,IAAI,KAAK,IAAI,KAAK,QAAQ,EAAE;AAEjE,YAAM,MAAM,KAAK,IAAI,GAAG,MAAM,IAAI,WAAW,GAAG,EAAE;AAElD,YAAM,mBAAmB,KAAK,IAAI,GAAG,GAAG,IAAI,KAAK,IAAI,GAAG,EAAE;AAE1D,UAAI,aAAa,kBAAkB;AACjC,aAAK;AAAA,UACH,8BAA8B,QAAQ,cAAc,QAAQ;AAAA,QAC9D;AACA,eAAO;AAAA,MACT;AAAA,IACF;AAEA,SAAK;AAAA,MACH,iDAAiD,QAAQ;AAAA,IAC3D;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKQ,8BAA8B,UAA0B;AAC9D,UAAM,QAAQ,KAAK,kBAAkB,QAAQ;AAC7C,WAAO,iBAAiB,KAAK,UAAU,aAAa,KAAK;AAAA,EAC3D;AAAA;AAAA,EAIQ,6BAAmC;AACzC,QAAI,KAAK,uBAAuB;AAC9B,mBAAa,KAAK,qBAAqB;AACvC,WAAK,wBAAwB;AAAA,IAC/B;AAAA,EACF;AAAA,EAEQ,iBAAiB,WAA0B,QAAsB;AACvE,QAAI,KAAK,KAAK,cAAc,UAAW;AACvC,UAAM,YAAY,KAAK,IAAI,OAAO,aAAa;AAC/C,SAAK,KAAK,cAAc;AACxB,SAAK,OAAO,eAAe,WAAW,SAAS;AAC/C,SAAK,aAAa,mBAAmB,KAAK,KAAK,GAAG,KAAK,MAAM,GAAG;AAAA,EAClE;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,QAAuB;AAC3B,QAAI,KAAK,QAAQ;AACf,YAAM,IAAI,MAAM,+BAA+B;AAAA,IACjD;AAGA,QAAI;AACF,YAAM,WAAW,MAAM,KAAK,IAAI,kBAAkB,KAAK,OAAO;AAC9D,YAAM,SAAS,SAAS,QAAQ,KAAK,CAAC,MAAM,EAAE,YAAY,KAAK,OAAO;AACtE,UAAI,QAAQ;AACV,aAAK,iBAAiB;AAAA,UACpB,WAAW,OAAO,aAAa;AAAA,UAC/B,OAAO,OAAO;AAAA,UACd,QAAQ,OAAO;AAAA,QACjB;AAEA,cAAM,MAAM,OAAO,OAAO,gBAAgB,EAAE,EACzC,KAAK,EACL,YAAY;AACf,cAAM,gBACJ,IAAI,SAAS,KAAK,KAAK,IAAI,SAAS,MAAM,IAAI,SAAS;AACzD,aAAK,iBAAiB,eAAe,UAAU;AAAA,MACjD;AAAA,IACF,SAAS,OAAO;AACd,WAAK,OAAO;AAAA,QACV,uDAAuD,KAAK;AAAA,MAC9D;AACA,WAAK,iBAAiB,EAAE,WAAW,IAAI,OAAO,MAAM,QAAQ,KAAK;AACjE,WAAK,iBAAiB,QAAQ,sBAAsB;AAAA,IACtD;AAGA,SAAK,yBAA6B,kBAAa,CAAC,WAAW;AACzD,WAAK,qBAAqB,MAAM;AAAA,IAClC,CAAC;AAGD,UAAM,IAAI,QAAc,CAAC,SAAS,WAAW;AAC3C,WAAK,uBAAwB;AAAA,QAC3B,KAAK;AAAA,QACL,KAAK;AAAA,QACL,MAAM;AAEJ,gBAAM,UAAU,KAAK,uBAAwB,QAAQ;AACrD,cAAI,WAAW,OAAO,YAAY,YAAY,UAAU,SAAS;AAC/D,iBAAK,aAAa,QAAQ;AAAA,UAC5B;AACA,kBAAQ;AAAA,QACV;AAAA,MACF;AACA,WAAK,uBAAwB,GAAG,SAAS,CAAC,UAAU;AAClD,eAAO,KAAK;AAAA,MACd,CAAC;AAAA,IACH,CAAC;AAED,SAAK,SAAS;AACd,SAAK,OAAO;AAAA,MACV,+CAA+C,KAAK,UAAU,IAAI,KAAK,UAAU,WAAW,KAAK,IAAI;AAAA,IACvG;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,qBAAqB,QAA0B;AACrD,UAAM,WAAW,GAAG,OAAO,aAAa,IAAI,OAAO,UAAU;AAC7D,SAAK,OAAO,KAAK,+CAA+C,QAAQ,EAAE;AAE1E,QAAI,YAAY;AAChB,QAAI,SAAS,OAAO,MAAM,CAAC;AAC3B,QAAI;AACJ,QAAI,oBAAoB;AACxB,QAAI,kBAAuC;AAC3C,QAAI,uBAA4C;AAEhD,UAAM,UAAU,MAAM;AACpB,WAAK,aAAa,QAAQ;AAG1B,WAAK,WAAW,OAAO,QAAQ;AAG/B,YAAM,YAAY,KAAK,gBAAgB,IAAI,QAAQ;AACnD,UAAI,WAAW;AAEb,YAAI,UAAU,QAAQ;AACpB,cAAI;AACF,sBAAU,OAAO,OAAO,IAAI;AAC5B,sBAAU,OAAO,KAAK,SAAS;AAC/B,uBAAW,MAAM;AACf,kBAAI;AACF,0BAAU,QAAQ,KAAK,SAAS;AAAA,cAClC,QAAQ;AAAA,cAAC;AAAA,YACX,GAAG,GAAI;AAAA,UACT,QAAQ;AAAA,UAAC;AAAA,QACX;AAGA,YAAI,UAAU,WAAW;AACvB,cAAI;AACF,sBAAU,UAAU,MAAM;AAAA,UAC5B,QAAQ;AAAA,UAAC;AAAA,QACX;AACA,YAAK,UAAkB,gBAAgB;AACrC,cAAI;AACF,YAAE,UAAkB,eAAgC,MAAM;AAAA,UAC5D,QAAQ;AAAA,UAAC;AAAA,QACX;AAGA,YAAI,UAAU,cAAc,CAAC,UAAU,WAAW,WAAW;AAC3D,cAAI;AACF,sBAAU,WAAW,QAAQ;AAAA,UAC/B,QAAQ;AAAA,UAAC;AAAA,QACX;AAEA,aAAK,gBAAgB,OAAO,QAAQ;AAAA,MACtC;AAGA,UAAI,cAAc;AAChB,YAAI;AACF,uBAAa,OAAO,IAAI;AACxB,uBAAa,KAAK,SAAS;AAC3B,qBAAW,MAAM;AACf,gBAAI;AACF,4BAAc,KAAK,SAAS;AAAA,YAC9B,QAAQ;AAAA,YAAC;AAAA,UACX,GAAG,GAAI;AAAA,QACT,QAAQ;AAAA,QAAC;AACT,uBAAe;AAAA,MACjB;AAEA,UAAI,iBAAiB;AACnB,YAAI;AACF,0BAAgB,MAAM;AAAA,QACxB,QAAQ;AAAA,QAAC;AACT,0BAAkB;AAAA,MACpB;AAEA,UAAI,sBAAsB;AACxB,YAAI;AACF,+BAAqB,MAAM;AAAA,QAC7B,QAAQ;AAAA,QAAC;AACT,+BAAuB;AAAA,MACzB;AAAA,IACF;AAEA,WAAO,GAAG,SAAS,OAAO;AAC1B,WAAO,GAAG,SAAS,CAAC,UAAU;AAC5B,UACE,SACA,OAAO,UAAU,YACjB,UAAU,SACV,MAAM,SAAS,SACf;AACA,aAAK,OAAO,MAAM,2CAA2C,KAAK;AAAA,MACpE;AACA,cAAQ;AAAA,IACV,CAAC;AAED,WAAO,GAAG,QAAQ,OAAO,SAAiB;AACxC,eAAS,OAAO,OAAO,CAAC,QAAQ,IAAI,CAAC;AAErC,aAAO,OAAO,SAAS,UAAU,GAAG;AAClC,cAAM,WAAW,OAAO,QAAQ,UAAU;AAC1C,cAAM,cAAc,OAAO,SAAS,GAAG,QAAQ,EAAE,SAAS;AAC1D,iBAAS,OAAO,SAAS,WAAW,CAAC;AAErC,YAAI,CAAC,YAAY,KAAK,EAAG;AAEzB,cAAM,QAAQ,YAAY,MAAM,MAAM;AACtC,cAAM,cAAc,MAAM,CAAC,GAAG,MAAM,GAAG;AACvC,YAAI,CAAC,eAAe,YAAY,SAAS,EAAG;AAE5C,cAAM,SAAS,YAAY,CAAC;AAC5B,cAAM,MAAM,YAAY,CAAC;AACzB,cAAM,UAAU,YAAY,CAAC;AAE7B,cAAM,YAAY,YAAY,MAAM,gBAAgB;AACpD,cAAM,OAAO,YAAY,SAAS,UAAU,CAAC,KAAK,KAAK,EAAE,IAAI;AAE7D,cAAM,eAAe,CACnB,YACA,YACA,UAAkC,CAAC,GACnC,SACG;AACH,cAAI,WAAW,GAAG,OAAO,IAAI,UAAU,IAAI,UAAU;AAAA;AACrD,sBAAY,SAAS,IAAI;AAAA;AACzB,qBAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,OAAO,GAAG;AAClD,wBAAY,GAAG,GAAG,KAAK,KAAK;AAAA;AAAA,UAC9B;AACA,cAAI,MAAM;AACR,wBAAY,mBAAmB,OAAO,WAAW,MAAM,MAAM,CAAC;AAAA;AAAA,UAChE;AACA,sBAAY;AACZ,cAAI,MAAM;AACR,wBAAY;AAAA,UACd;AACA,iBAAO,MAAM,QAAQ;AAAA,QACvB;AAEA,aAAK,aAAa,QAAQ,MAAM,IAAI,GAAG,EAAE;AAGzC,YAAI,KAAK,aAAa;AACpB,gBAAM,YAAY,YAAY,MAAM,8BAA8B;AAClE,gBAAM,aAAa,YAAY,CAAC,KAAK;AAGrC,cAAI,WAAW,WAAW;AACxB,gBAAI,CAAC,YAAY;AAEf,mBAAK;AAAA,gBACH,2CAA2C,QAAQ;AAAA,cACrD;AACA,2BAAa,KAAK,gBAAgB;AAAA,gBAChC,oBACE,KAAK,8BAA8B,QAAQ;AAAA,cAC/C,CAAC;AACD;AAAA,YACF;AAGA,gBACE,CAAC,KAAK;AAAA,cACJ;AAAA,cACA,UAAU;AAAA,cACV,OAAO;AAAA,cACP;AAAA,YACF,GACA;AAEA,mBAAK,aAAa,mBAAmB,QAAQ,eAAe;AAE5D,mBAAK,WAAW,OAAO,QAAQ;AAC/B,2BAAa,KAAK,gBAAgB;AAAA,gBAChC,oBACE,KAAK,8BAA8B,QAAQ;AAAA,cAC/C,CAAC;AACD;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAGA,YAAI,WAAW,WAAW;AACxB,uBAAa,KAAK,MAAM;AAAA,YACtB,QAAQ;AAAA,UACV,CAAC;AAAA,QACH,WAAW,WAAW,YAAY;AAGhC,cAAI,CAAC,KAAK,sBAAsB,KAAK,iBAAiB,SAAS,GAAG;AAChE,gBAAI;AACF,kBAAI,CAAC,KAAK,oBAAoB;AAC5B,sBAAM,KAAK,kBAAkB;AAAA,cAC/B;AAAA,YACF,SAAS,OAAO;AACd,mBAAK,OAAO;AAAA,gBACV,uEAAuE,KAAK;AAAA,cAC9E;AAAA,YACF;AAEA,kBAAM,EAAE,aAAa,IAAI,KAAK,KAAK,QAAQ;AAC3C,gBAAI,CAAC,cAAc;AAGjB,oBAAM,YACJ,KAAK,IAAI,OAAO,aAAa,MAAM,QAAQ,MAAO;AACpD,kBAAI;AACF,sBAAM,QAAQ,KAAK;AAAA,kBACjB,KAAK,qBAAqB,QAAQ,QAAQ;AAAA,kBAC1C,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,SAAS,CAAC;AAAA,gBACzD,CAAC;AAAA,cACH,QAAQ;AAAA,cAER;AAAA,YACF;AAAA,UACF;AAGA;AACE,kBAAM,EAAE,MAAM,aAAa,IAAI,KAAK,KAAK,QAAQ;AACjD,kBAAM,cACJ,KAAK,SAAS,MAAM,GAAG,KAAK,MAAM,GAAG,GAAG,CAAC,QAAQ;AACnD,iBAAK,OAAO;AAAA,cACV,yCAAyC,QAAQ,SAAS,KAAK,IAAI,UAAU,KAAK,KAAK,QAAQ,iBAAiB,YAAY,SAAS,WAAW;AAAA,YAClJ;AACA,gBAAI,CAAC,cAAc;AACjB,mBAAK;AAAA,gBACH,0DAA0D,QAAQ,UAAU,KAAK,IAAI,UAAU,KAAK,KAAK,GAAG;AAAA,cAC9G;AAAA,YACF;AAAA,UACF;AACA,gBAAM,MAAM,KAAK,YAAY;AAC7B;AAAA,YACE;AAAA,YACA;AAAA,YACA;AAAA,cACE,gBAAgB;AAAA,cAChB,gBAAgB,UAAU,KAAK,UAAU,IAAI,KAAK,UAAU,GAAG,KAAK,IAAI;AAAA,YAC1E;AAAA,YACA;AAAA,UACF;AAAA,QACF,WAAW,WAAW,SAAS;AAC7B,gBAAM,WAAW,KAAK,SAAS,QAAQ;AACvC,gBAAM,WAAW,KAAK,SAAS,QAAQ;AACvC,cAAI,CAAC,YAAY,CAAC,UAAU;AAC1B,yBAAa,KAAK,aAAa;AAAA,cAC7B,SACE,aACA,WAAW,KAAK,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,UAAU,CAAC,CAAC;AAAA,YACpE,CAAC;AACD;AAAA,UACF;AAGA,cAAI,YAAY,CAAC,KAAK,UAAU;AAC9B,yBAAa,KAAK,aAAa;AAAA,cAC7B,SACE,aACA,WAAW,KAAK,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,UAAU,CAAC,CAAC;AAAA,YACpE,CAAC;AACD;AAAA,UACF;AAGA,eAAK,iBAAiB,IAAI,QAAQ;AAClC,eAAK,KAAK,UAAU,QAAQ;AAC5B,eAAK,2BAA2B;AAGhC,cAAI,KAAK,iBAAiB,SAAS,KAAK,CAAC,KAAK,oBAAoB;AAChE,kBAAM,KAAK,kBAAkB;AAAA,UAC/B;AAGA,gBAAM,iBAAiB,YAAY,MAAM,0BAA0B;AACnE,gBAAM,aAAa,iBAAiB,CAAC,KAAK,IAAI,KAAK;AACnD,8BAAoB,YAChB,UAAU,SAAS,KAAK,KAAK,UAAU,SAAS,KAAK,IACrD;AAGJ,cAAI,CAAC,WAAW;AACd,wBAAY,WAAW,KAAK,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,UAAU,CAAC,CAAC;AAAA,UAC9E;AAGA,gBAAM,WAAW,KAAK,gBAAgB,IAAI,QAAQ;AAClD,cAAI,CAAC,UAAU;AACb,iBAAK,gBAAgB,IAAI,UAAU;AAAA,cACjC,QAAQ;AAAA,cACR,WAAW;AAAA,cACX,gBAAgB;AAAA,cAChB,YAAY;AAAA,cACZ,iBAAiB;AAAA,cACjB,wBAAwB;AAAA,cACxB,aAAa;AAAA,cACb,aAAa;AAAA,cACb,WAAW;AAAA,YACb,CAAC;AAAA,UACH,OAAO;AAEL,qBAAS,aAAa;AAAA,UACxB;AAGA,cAAI,mBAAmB;AACrB,kBAAM,YAAY,KAAK,gBAAgB,IAAI,QAAQ;AACnD,kBAAM,YACJ,oBAAmB,yBAAyB,SAAS;AACvD,gBAAI,WAAW;AACb,kBAAI,UAAU;AACZ,sBAAM,KAAK,aAAa,EAAE,KAAK,GAAG,MAAM,EAAE;AAC1C,0BAAU,mBAAmB,GAAG;AAChC,0BAAU,oBAAoB,GAAG;AAAA,cACnC,OAAO;AACL,sBAAM,KAAK,aAAa,EAAE,KAAK,GAAG,MAAM,EAAE;AAC1C,0BAAU,mBAAmB,GAAG;AAChC,0BAAU,oBAAoB,GAAG;AAAA,cACnC;AAAA,YACF;AAAA,UACF;AAIA;AACE,kBAAM,YAAY,KAAK,gBAAgB,IAAI,QAAQ;AACnD,gBAAI,aAAa,CAAC,UAAU,iBAAiB;AAC3C,wBAAU,kBAAkB;AAC5B,oBAAM,KAAK;AAAA,gBACT;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA,CAAC,MAAM,SAAS,iBAAiB;AAC/B,iCAAe;AACf,oCAAkB;AAClB,yCAAuB;AACvB,wBAAM,IAAI,KAAK,gBAAgB,IAAI,QAAQ;AAC3C,sBAAI,GAAG;AACL,sBAAE,SAAS;AACX,sBAAE,YAAY;AACd,sBAAE,iBAAiB;AAAA,kBACrB;AAAA,gBACF;AAAA,cACF;AAAA,YACF;AAAA,UACF;AAGA;AACE,kBAAM,YAAY,KAAK,gBAAgB,IAAI,QAAQ;AACnD,gBAAI,WAAW;AACb,kBAAI,SAAU,WAAU,cAAc;AAAA,kBACjC,WAAU,cAAc;AAC7B,mBAAK;AAAA,gBACH,kBAAkB,QAAQ,YAAY,CAAC,CAAC,UAAU,WAAW,WAAW,CAAC,CAAC,UAAU,WAAW,YAAY,CAAC,CAAC,UAAU,SAAS;AAAA,cAClI;AAAA,YACF;AAAA,UACF;AAEA,cAAI,mBAAmB;AACrB,kBAAM,YAAY,KAAK,gBAAgB,IAAI,QAAQ;AACnD,kBAAM,WAAW,WACb,EAAE,KAAK,GAAG,MAAM,EAAE,IAClB,EAAE,KAAK,GAAG,MAAM,EAAE;AACtB,kBAAM,MAAM,WACP,WAAW,oBAAoB,SAAS,MACxC,WAAW,oBAAoB,SAAS;AAC7C,kBAAM,OAAO,WACR,WAAW,qBAAqB,SAAS,OACzC,WAAW,qBAAqB,SAAS;AAC9C,kBAAM,cAAc,GAAG,GAAG,IAAI,IAAI;AAClC,yBAAa,KAAK,MAAM;AAAA,cACtB,WAAW,mCAAmC,WAAW;AAAA,cACzD,SAAS;AAAA,YACX,CAAC;AAAA,UACH,OAAO;AAEL,yBAAa,KAAK,MAAM;AAAA,cACtB,WAAW;AAAA,cACX,SAAS;AAAA,YACX,CAAC;AAAA,UACH;AAAA,QACF,WAAW,WAAW,QAAQ;AAC5B;AACE,kBAAM,YAAY,KAAK,gBAAgB,IAAI,QAAQ;AACnD,gBAAI,WAAW;AACb,wBAAU,YAAY;AACtB,mBAAK;AAAA,gBACH,YAAY,QAAQ,YAAY,CAAC,CAAC,UAAU,WAAW,WAAW,CAAC,CAAC,UAAU,WAAW,YAAY,CAAC,CAAC,UAAU,SAAS;AAAA,cAC5H;AAAA,YACF;AAAA,UACF;AACA,uBAAa,KAAK,MAAM;AAAA,YACtB,SAAS;AAAA,YACT,OAAO;AAAA,UACT,CAAC;AAAA,QACH,WAAW,WAAW,YAAY;AAChC,kBAAQ;AACR,uBAAa,KAAK,MAAM;AAAA,YACtB,SAAS;AAAA,UACX,CAAC;AACD,iBAAO,IAAI;AAAA,QACb,OAAO;AACL,uBAAa,KAAK,iBAAiB;AAAA,QACrC;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKQ,cAAsB;AAC5B,UAAM,QAAQ,KAAK,KAAK;AACxB,UAAM,mBAAmB;AACzB,UAAM,mBAAmB;AAEzB,QAAI,MAAM;AACV,WAAO,OAAO,KAAK,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,WAAW,KAAK,UAAU;AAAA;AAChE,WAAO;AACP,WAAO,YAAY,KAAK,UAAU;AAAA;AAClC,WAAO;AAGP,WAAO,qBAAqB,gBAAgB;AAAA;AAC5C,WAAO,YAAY,gBAAgB,IAAI,KAAK;AAAA;AAC5C,QAAI,KAAK,gBAAgB,WAAW;AAClC,aAAO,eAAe,KAAK,eAAe,SAAS;AAAA;AAAA,IACrD;AACA,QAAI,KAAK,gBAAgB,SAAS,KAAK,gBAAgB,QAAQ;AAC7D,aAAO,eAAe,gBAAgB,IAAI,KAAK,eAAe,KAAK,IAAI,KAAK,eAAe,MAAM;AAAA;AAAA,IACnG;AACA,WAAO;AAAA;AAEP,UAAM,EAAE,MAAM,aAAa,IAAI,KAAK,KAAK,QAAQ;AACjD,QAAI,CAAC,cAAc;AACjB,WAAK,OAAO;AAAA,QACV,4DAA4D,KAAK,KAAK,GAAG;AAAA,MAC3E;AAAA,IACF;AAEA,QAAI,MAAM;AACR,aAAO,UAAU,gBAAgB,IAAI,IAAI;AAAA;AAAA,IAC3C;AAIA,QAAI,KAAK,UAAU;AACjB,aAAO,qBAAqB,gBAAgB;AAAA;AAC5C,YAAM,IAAI,KAAK;AACf,YAAM,OAAO,GAAG,cAAc;AAC9B,YAAM,KAAK,GAAG,YAAY;AAC1B,YAAM,MAAM,GAAG,aAAa;AAC5B,aAAO,YAAY,gBAAgB,kBAAkB,IAAI,IAAI,EAAE;AAAA;AAC/D,UAAI,KAAK;AACP,eAAO,UAAU,gBAAgB,4DAA4D,GAAG;AAAA;AAAA,MAClG;AACA,aAAO;AAAA;AAAA,IACT;AAEA,WAAO;AAAA;AACP,WAAO;AAAA;AAEP,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,kBACZ,UACA,YACA,mBACA,WAKe;AAEf,QAAI,iBAAiB,KAAK;AAC1B,QAAI,CAAC,kBAAkB,CAAC,eAAe,WAAW;AAChD,UAAI;AACF,cAAM,WAAW,MAAM,KAAK,IAAI,kBAAkB,KAAK,OAAO;AAC9D,cAAM,SAAS,SAAS,QAAQ,KAAK,CAAC,MAAM,EAAE,YAAY,KAAK,OAAO;AACtE,YAAI,QAAQ;AACV,2BAAiB;AAAA,YACf,WAAW,OAAO,aAAa;AAAA,YAC/B,OAAO,OAAO;AAAA,YACd,QAAQ,OAAO;AAAA,UACjB;AACA,eAAK;AAAA,YACH,gCAAgC,KAAK,OAAO,KAAK,eAAe,SAAS;AAAA,UAC3E;AAAA,QACF;AAAA,MACF,SAAS,OAAO;AACd,aAAK,OAAO;AAAA,UACV,yDAAyD,KAAK;AAAA,QAChE;AACA,yBAAiB,EAAE,WAAW,IAAI,OAAO,MAAM,QAAQ,KAAK;AAAA,MAC9D;AAAA,IACF;AAEA,UAAM,eAAe,KAAK,KAAK;AAM/B,QAAI,eAAe;AACnB,QAAI,oBAAoB;AACxB,QAAI,YAAiC;AACrC,QAAI,iBAAsC;AAE1C,UAAM,eAAe;AAErB,UAAM,kBAAkB,CAAC,SAAiB,cAA8B;AAGtE,YAAM,UAAU,oBACZ,qBACA,KAAK;AAET,UAAI,YAAY,WAAW;AACzB,cAAMC,KAAI,OAAO,MAAM,CAAC;AACxB,QAAAA,GAAE,cAAc,UAAU,SAAS,OAAQ,CAAC;AAC5C,eAAO,OAAO,OAAO,CAACA,IAAG,SAAS,CAAC;AAAA,MACrC;AACA,YAAM,IAAI,OAAO,MAAM,CAAC;AACxB,QAAE,CAAC,IAAI;AACP,QAAE,CAAC,IAAI,UAAU;AACjB,QAAE,CAAC,IAAK,UAAU,UAAU,IAAK;AACjC,QAAE,CAAC,IAAI,UAAU,SAAS;AAC1B,aAAO,OAAO,OAAO,CAAC,GAAG,SAAS,CAAC;AAAA,IACrC;AAEA,QAAI,qBAAqB,CAAC,cAAc;AACtC,qBAAe,MAAQ,KAAK,MAAM,KAAK,OAAO,IAAI,GAAK;AACvD,kBAAkB,oBAAa,MAAM;AAErC,YAAM,IAAI,QAAc,CAAC,SAAS,WAAW;AAC3C,kBAAW,KAAK,aAAa,MAAM,QAAQ,CAAC;AAC5C,kBAAW,KAAK,SAAS,MAAM;AAC/B,kBAAW,KAAK,cAAc,WAAW;AAAA,MAC3C,CAAC;AAED,YAAMC,mBAAkB,CAAC,SAAiB,QAAyB;AACjE,YAAI,CAAC,cAAc,WAAW,aAAa,CAAC,WAAW;AACrD,iBAAO;AACT,YAAI,IAAI,SAAS,GAAI,QAAO;AAE5B,cAAM,UAAW,IAAI,CAAC,KAAM,IAAK;AACjC,YAAI,YAAY,EAAG,QAAO;AAG1B,cAAMC,aAAY,KAAK,gBAAgB,IAAI,QAAQ;AACnD,YAAI,CAACA,YAAW,UAAW,QAAO;AAClC,cAAM,kBAAkBA,YAAW,oBAAoB;AACvD,cAAM,kBAAkBA,YAAW,oBAAoB;AACvD,YAAI,YAAY,mBAAmB,CAACA,YAAW;AAC7C,iBAAO;AACT,YAAI,YAAY,mBAAmB,CAACA,YAAW;AAC7C,iBAAO;AAET,YAAI;AACF,iBAAO,WAAW,MAAM,gBAAgB,SAAS,GAAG,CAAC;AAAA,QACvD,SAAS,OAAO;AACd,cACE,SACA,OAAO,UAAU,YACjB,UAAU,SACT,MAAc,SAAS;AAExB,mBAAO;AAAA,QACX;AAEA,eAAO;AAAA,MACT;AAEA,UAAI,WAAW;AACb,cAAMA,aAAY,KAAK,gBAAgB,IAAI,QAAQ;AACnD,cAAM,kBAAkBA,YAAW,oBAAoB;AACvD,YAAI,iBAAiB;AACrB,YAAI,YAAY;AAChB,YAAI,iBAAiB;AACrB,kBAAU,GAAG,WAAW,CAAC,QAAgB;AACvC,cAAI,CAAC,WAAW;AACd,wBAAY;AACZ,iBAAK;AAAA,cACH,0DAA0D,QAAQ,SAAS,IAAI,MAAM;AAAA,YACvF;AAAA,UACF;AAEA,gBAAM,YAAYD,iBAAgB,iBAAiB,GAAG;AACtD,cAAI,aAAa,CAAC,gBAAgB;AAChC,6BAAiB;AACjB,iBAAK;AAAA,cACH,mEAAmE,QAAQ;AAAA,YAC7E;AAAA,UACF;AACA;AACA,cAAI,iBAAiB,QAAS,GAAG;AAC/B,iBAAK;AAAA,cACH,aAAa,cAAc,0BAA0B,QAAQ;AAAA,YAC/D;AAAA,UACF;AAAA,QACF,CAAC;AAAA,MACH;AAEA,UAAI,KAAK,UAAU;AACjB,4BACE,eAAe,IAAI,KAAK,MAAM,KAAK,OAAO,IAAI,GAAI,IAAI;AACxD,yBAAuB,oBAAa,MAAM;AAC1C,cAAM,IAAI,QAAc,CAAC,SAAS,WAAW;AAC3C,yBAAgB,KAAK,aAAa,MAAM,QAAQ,CAAC;AACjD,yBAAgB,KAAK,SAAS,MAAM;AACpC,yBAAgB,KAAK,mBAAmB,WAAW;AAAA,QACrD,CAAC;AACD,YAAI,mBAAmB;AACvB,YAAI,iBAAiB;AACrB,YAAI,sBAAsB;AAC1B,uBAAe,GAAG,WAAW,CAAC,QAAgB;AAC5C,gBAAMC,aAAY,KAAK,gBAAgB,IAAI,QAAQ;AACnD,gBAAM,kBAAkBA,YAAW,oBAAoB;AACvD;AACA,cAAI,CAAC,gBAAgB;AACnB,6BAAiB;AACjB,iBAAK;AAAA,cACH,0DAA0D,QAAQ,SAAS,IAAI,MAAM;AAAA,YACvF;AAAA,UACF;AACA,gBAAM,YAAYD,iBAAgB,iBAAiB,GAAG;AACtD,cAAI,aAAa,CAAC,qBAAqB;AACrC,kCAAsB;AACtB,iBAAK;AAAA,cACH,mEAAmE,QAAQ;AAAA,YAC7E;AAAA,UACF;AACA,cAAI,mBAAmB,QAAS,GAAG;AACjC,iBAAK;AAAA,cACH,aAAa,gBAAgB,gCAAgC,QAAQ;AAAA,YACvE;AAAA,UACF;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF;AAEA,UAAM,YAAY,KAAK,gBAAgB,IAAI,QAAQ;AACnD,UAAM,YAAY,KAAK,mBAAmB;AAC1C,UAAM,eAAe,CAAC,YAAoB,KAAK,aAAa,OAAO;AAEnE,UAAM,kBAAkB,CAAC,SAAiB,QAAyB;AACjE,UAAI,CAAC,cAAc,WAAW,aAAa,CAAC,WAAW;AACrD,eAAO;AACT,UAAI;AACF,eAAO,WAAW,MAAM,gBAAgB,SAAS,GAAG,CAAC;AAAA,MACvD,SAAS,OAAO;AACd,YACE,SACA,OAAO,UAAU,YACjB,UAAU,SACT,MAAc,SAAS;AAExB,iBAAO;AAAA,MACX;AACA,aAAO;AAAA,IACT;AAEA,UAAM,kBAAkB,MAAc,WAAW,oBAAoB;AACrE,UAAM,kBAAkB,MAAc,WAAW,oBAAoB;AAErE,UAAM,iBAAiB,CACrB,aACA,QACA,KACA,WACA,SACW;AACX,YAAM,IAAI,OAAO,MAAM,EAAE;AACzB,QAAE,CAAC,IAAI;AACP,QAAE,CAAC,KAAK,SAAS,MAAO,KAAS,cAAc;AAC/C,QAAE,cAAc,MAAM,OAAQ,CAAC;AAC/B,QAAE,cAAc,cAAc,GAAG,CAAC;AAClC,QAAE,cAAc,SAAS,GAAG,CAAC;AAC7B,aAAO;AAAA,IACT;AAEA,UAAM,gBAAgB,CACpB,SACA,SACA,WACG;AACH,YAAM,KAAK,UAAU,KAAK;AAC1B,UAAI,CAAC,WAAW,UAAW;AAC3B,UAAI,WAAW,CAAC,WAAW,YAAa;AACxC,UAAI,CAAC,WAAW,CAAC,WAAW,YAAa;AAEzC,UAAI,CAAC,SAAS;AACZ,YAAI,UAAU,gBAAgB;AAC5B,oBAAU,cAAc,KAAK,MAAM,KAAK,OAAO,IAAI,KAAO;AAE5D,YAAI,UAAU,sBAAsB;AAClC,oBAAU,oBAAoB;AAChC,YAAI,UAAU,iBAAiB;AAC7B,oBAAU,eAAgB,KAAK,OAAO,IAAI,eAAgB;AAC5D,cAAM,IAAI;AAAA,UACR;AAAA,UACA;AAAA,UACA,UAAU;AAAA,UACV,UAAU;AAAA,UACV,UAAU;AAAA,QACZ;AACA,kBAAU,cAAe,UAAU,cAAc,IAAK;AACtD,wBAAgB,gBAAgB,GAAG,OAAO,OAAO,CAAC,GAAG,OAAO,CAAC,CAAC;AAAA,MAChE,OAAO;AACL,YAAI,UAAU,gBAAgB;AAC5B,oBAAU,cAAc,KAAK,MAAM,KAAK,OAAO,IAAI,KAAO;AAE5D,YAAI,UAAU,sBAAsB;AAClC,oBAAU,oBAAoB;AAChC,YAAI,UAAU,iBAAiB;AAC7B,oBAAU,eAAgB,KAAK,OAAO,IAAI,eAAgB;AAC5D,cAAM,IAAI;AAAA,UACR;AAAA,UACA;AAAA,UACA,UAAU;AAAA,UACV,UAAU;AAAA,UACV,UAAU;AAAA,QACZ;AACA,kBAAU,cAAe,UAAU,cAAc,IAAK;AACtD,wBAAgB,gBAAgB,GAAG,OAAO,OAAO,CAAC,GAAG,OAAO,CAAC,CAAC;AAAA,MAChE;AAAA,IACF;AAEA,UAAM,gBAAgB;AAEtB,UAAM,sBAAsB,CAAC,WAA4B;AACvD,YAAM,OAAO,oBAAmB,gBAAgB,MAAM;AACtD,iBAAW,OAAO,MAAM;AACtB,YAAI,IAAI,SAAS,EAAG;AACpB,cAAM,KAAK,IAAI,CAAC,KAAK,KAAK;AAC1B,YAAI,MAAM,EAAG,QAAO;AAAA,MACtB;AACA,aAAO;AAAA,IACT;AAEA,UAAM,uBAAuB,CAAC,KAAa,iBAA0B;AACnE,UAAI,IAAI,UAAU,eAAe;AAC/B,sBAAc,OAAO,KAAK,YAAY;AACtC;AAAA,MACF;AACA,YAAM,YAAY,IAAI,CAAC;AACvB,YAAM,UAAU,YAAY;AAE5B,YAAM,cAAe,YAAY,MAAQ;AACzC,YAAM,OAAO,IAAI,SAAS,CAAC;AAC3B,UAAI,SAAS;AACb,aAAO,SAAS,KAAK,QAAQ;AAC3B,cAAM,YAAY,KAAK,SAAS;AAChC,cAAM,WAAW,KAAK,IAAI,WAAW,gBAAgB,CAAC;AACtD,cAAM,QAAQ,WAAW;AACzB,cAAM,MAAM,SAAS,YAAY,KAAK;AACtC,cAAM,YAAY,QAAQ,MAAO,MAAS,MAAM,KAAO,KAAQ;AAC/D,cAAM,QAAQ,KAAK,SAAS,QAAQ,SAAS,QAAQ;AACrD,cAAM,UAAU,OAAO,OAAO;AAAA,UAC5B,OAAO,KAAK,CAAC,aAAa,QAAQ,CAAC;AAAA,UACnC;AAAA,QACF,CAAC;AACD,sBAAc,OAAO,SAAS,gBAAgB,GAAG;AACjD,kBAAU;AAAA,MACZ;AAAA,IACF;AAEA,UAAM,uBAAuB,CAAC,KAAa,iBAA0B;AACnE,UAAI,IAAI,UAAU,eAAe;AAC/B,sBAAc,OAAO,KAAK,YAAY;AACtC;AAAA,MACF;AACA,UAAI,IAAI,SAAS,EAAG;AACpB,YAAM,aAAa,IAAI,CAAC;AACxB,YAAM,aAAa,IAAI,CAAC;AACxB,YAAM,UAAW,cAAc,IAAK;AACpC,YAAM,eAAgB,aAAa,MAAS,MAAM;AAClD,YAAM,eAAe;AACrB,YAAM,OAAO,IAAI,SAAS,CAAC;AAC3B,UAAI,SAAS;AACb,aAAO,SAAS,KAAK,QAAQ;AAC3B,cAAM,YAAY,KAAK,SAAS;AAChC,cAAM,WAAW,KAAK,IAAI,WAAW,gBAAgB,CAAC;AACtD,cAAM,QAAQ,WAAW;AACzB,cAAM,MAAM,SAAS,YAAY,KAAK;AACtC,cAAM,YACH,QAAQ,MAAO,MAAS,MAAM,KAAO,KAAS,UAAU;AAC3D,cAAM,QAAQ,KAAK,SAAS,QAAQ,SAAS,QAAQ;AACrD,cAAM,UAAU,OAAO,OAAO;AAAA,UAC5B,OAAO,KAAK,CAAC,cAAc,cAAc,QAAQ,CAAC;AAAA,UAClD;AAAA,QACF,CAAC;AACD,sBAAc,OAAO,SAAS,gBAAgB,GAAG;AACjD,kBAAU;AAAA,MACZ;AAAA,IACF;AAEA,UAAM,iBAAiB;AACvB,UAAM,WACJ,gBAAgB,aAAa,eAAe,YAAY,IACpD,eAAe,YACf;AACN,UAAM,0BAA0B,KAAK;AAAA,MACnC;AAAA,MACA,KAAK,MAAM,iBAAiB,QAAQ;AAAA,IACtC;AAEA,UAAM,oCAAoC,CACxC,sBACG;AACH,UAAI,CAAC,UAAW;AAChB,UAAI,sBAAsB,QAAQ,sBAAsB,OAAW;AACnE,UAAI,CAAC,OAAO,SAAS,iBAAiB,EAAG;AAEzC,UAAI,UAAU,sBAAsB;AAClC,kBAAU,oBAAoB;AAChC,UAAI,UAAU,0BAA0B;AACtC,kBAAU,wBAAwB,UAAU;AAE9C,UAAI,UAAU,6BAA6B,QAAW;AACpD,kBAAU,2BAA2B,sBAAsB;AAC3D,kBAAU,wBAAwB,UAAU;AAC5C;AAAA,MACF;AAEA,YAAM,SAAS,UAAU,6BAA6B;AACtD,YAAM,QAAQ,sBAAsB;AACpC,YAAM,UAAW,QAAQ,WAAY;AACrC,YAAM,UAAU,UAAU,yBAAyB,OAAO;AAC1D,UAAI,KACD,SAAS,KAAK,MAAO,UAAU,iBAAkB,GAAS,MAAO;AAEpE,YAAM,OAAO,UAAU;AACvB,UAAI,SAAS,UAAa,MAAM,SAAS,GAAG;AAC1C,cAAO,SAAS,KAAK,MAAO;AAAA,MAC9B;AAEA,gBAAU,oBAAoB;AAC9B,gBAAU,wBAAwB;AAAA,IACpC;AAEA,UAAM,sBAAsB,CAC1B,WACA,kBACA,mBAAmB,SAChB;AACH,YAAM,OAAO,oBAAmB,gBAAgB,gBAAgB;AAChE,UAAI,KAAK,WAAW,EAAG;AACvB,eAAS,MAAM,GAAG,MAAM,KAAK,QAAQ,OAAO;AAC1C,cAAM,MAAM,KAAK,GAAG;AACpB,cAAM,YAAY,QAAQ,KAAK,SAAS;AACxC,YAAI,cAAc,OAAQ,sBAAqB,KAAK,SAAS;AAAA,YACxD,sBAAqB,KAAK,SAAS;AAAA,MAC1C;AAGA,UACE,oBACA,WAAW,sBAAsB,UACjC,WAAW,6BAA6B,QACxC;AACA,kBAAU,oBACP,UAAU,oBAAoB,4BAA6B;AAAA,MAChE;AAAA,IACF;AAEA,UAAM,qBAAqB,CAAC,SAAiB;AAC3C,YAAM,MAAM,oBAAmB,gBAAgB,IAAI;AACnD,UAAI,CAAC,IAAK;AAEV,YAAM,kBAAkB,OAAO,KAAK,CAAC,GAAM,EAAI,CAAC;AAChD,YAAM,SAAS,IAAI,SAAS;AAC5B,YAAM,WAAW,OAAO,MAAM,CAAC;AAE/B,eAAS,CAAC,IAAK,UAAU,IAAK;AAC9B,eAAS,CAAC,KAAK,SAAS,OAAS;AACjC,YAAM,UAAU,OAAO,OAAO,CAAC,iBAAiB,UAAU,GAAG,CAAC;AAC9D,oBAAc,MAAM,SAAS,IAAI;AAGjC,UAAI,WAAW,sBAAsB,QAAW;AAC9C,kBAAU,oBACP,UAAU,oBAAoB,SAAU;AAAA,MAC7C;AAAA,IACF;AAEA,UAAM,uBAAuB,CAAC,WAA4B;AACxD,YAAM,OAAOE,0BAAyB,MAAM;AAC5C,iBAAW,OAAO,MAAM;AACtB,YAAI,IAAI,SAAS,EAAG;AACpB,cAAM,KAAK,IAAI,CAAC;AAChB,YAAI,OAAO,OAAW;AACtB,aAAK,KAAK,SAAU,EAAG;AACvB,cAAM,UAAW,MAAM,IAAK;AAC5B,YAAI,WAAW,OAAO,EAAG,QAAO;AAAA,MAClC;AACA,aAAO;AAAA,IACT;AAEA,QAAI,cAAc;AAChB,gBAAU,QAAW,MAAM,IAAI;AAAA,IACjC;AAEA,QAAI;AACJ,QAAI;AACJ,QAAI,CAAC,cAAc;AACjB,YAAM,aAAa;AAAA,QACjB;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA;AAAA,MACF;AAIA,YAAM,YAAY,gBAAgB,aAAa;AAC/C,UAAI,YAAY,GAAG;AACjB,mBAAW,KAAK,MAAM,UAAU,SAAS,CAAC;AAC1C,aAAK;AAAA,UACH,oBAAoB,SAAS,mBAAmB,QAAQ;AAAA,QAC1D;AAAA,MACF;AAEA,iBAAW,KAAK,MAAM,cAAc,MAAM,QAAQ;AAIlD,UAAI,KAAK,UAAU;AACjB,mBAAW,KAAK,MAAM,OAAO,MAAM,QAAQ;AAAA,MAC7C;AAOA,UAAI,mBAAmB;AAErB,mBAAW;AAAA,UACT;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA;AAAA,UACA;AAAA,UACA;AAAA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA,mBAAmB,YAAY;AAAA,QACjC;AAEA,YAAI,KAAK,YAAY,kBAAkB,mBAAmB;AACxD,gBAAM,IAAI,KAAK;AACf,gBAAM,OAAO,GAAG,cAAc;AAC9B,gBAAM,KAAK,GAAG,YAAY;AAC1B,qBAAW;AAAA,YACT;AAAA,YACA;AAAA;AAAA,YAEA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA,OAAO,IAAI;AAAA,YACX;AAAA,YACA,OAAO,EAAE;AAAA,YACT;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA,mBAAmB,iBAAiB;AAAA,UACtC;AAAA,QACF;AAAA,MACF,OAAO;AACL,mBAAW;AAAA,UACT;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAAA,MACF;AAEA,YAAM,QAAe,CAAC,QAAQ,UAAU,MAAM;AAC9C,UAAI,KAAK,SAAU,OAAM,KAAK,MAAM;AACpC,WAAK;AAAA,QACH,8BAA8B,QAAQ,YAAY,WAAW,KAAK,GAAG,CAAC;AAAA,MACxE;AACA,eAAS,MAAM,UAAU,YAAY;AAAA,QACnC;AAAA,MACF,CAAC;AAID,UAAI;AACF,cAAM,YAAY,KAAK,KAAK,uBAAuB;AACnD,YAAI,aAAa,UAAU,SAAS,KAAK,QAAQ,OAAO;AACtD,iBAAO,MAAM,MAAM,SAAS;AAC5B,eAAK;AAAA,YACH,yDAAyD,QAAQ,SAAS,UAAU,MAAM;AAAA,UAC5F;AAAA,QACF;AAAA,MACF,SAAS,GAAG;AACV,aAAK,OAAO;AAAA,UACV,kFAAkF,QAAQ,KAAK,CAAC;AAAA,QAClG;AAAA,MACF;AACA,aAAO,GAAG,SAAS,CAAC,UAAU;AAC5B,aAAK,OAAO;AAAA,UACV,0DAA0D,QAAQ;AAAA,UAClE;AAAA,QACF;AAAA,MACF,CAAC;AAED,aAAO,GAAG,SAAS,CAAC,MAAM,WAAW;AACnC,aAAK;AAAA,UACH,4BAA4B,QAAQ,UAAU,IAAI,YAAY,MAAM;AAAA,QACtE;AAAA,MACF,CAAC;AAED,gBAAU,QAAQ,WAAW,cAAc;AAI3C,aAAO,OAAO,GAAG,SAAS,CAAC,UAAiC;AAC1D,cAAM,OAAQ,OAAe;AAC7B,YAAI,SAAS,WAAW,SAAS,8BAA8B;AAC7D,eAAK;AAAA,YACH,uBAAuB,IAAI,gBAAgB,QAAQ;AAAA,UACrD;AACA;AAAA,QACF;AACA,aAAK,OAAO;AAAA,UACV,sDAAsD,QAAQ;AAAA,UAC9D;AAAA,QACF;AAAA,MACF,CAAC;AAED,kBAAY,KAAK,WACZ,OAAO,QAAQ,CAAC,IACjB;AACJ,MAAC,WAAmB,KAAK,SAAS,CAAC,UAAiC;AAClE,cAAM,OAAQ,OAAe;AAC7B,YAAI,SAAS,WAAW,SAAS,8BAA8B;AAC7D,eAAK;AAAA,YACH,4BAA4B,IAAI,gBAAgB,QAAQ;AAAA,UAC1D;AACA;AAAA,QACF;AACA,aAAK,OAAO;AAAA,UACV,2DAA2D,QAAQ;AAAA,UACnE;AAAA,QACF;AAAA,MACF,CAAC;AAID,UAAI,aAAa,KAAK,mBAAmB;AACvC,YAAI;AACF,oBAAU,MAAM,KAAK,iBAAiB;AAAA,QACxC,QAAQ;AAAA,QAAC;AAAA,MACX;AAAA,IACF;AAIA,SAAK,aAAa,8CAA8C,QAAQ,EAAE;AAC1E,UAAM,kBAAkB,KAAK,eACzB,KAAK,aAAa,UAAU,QAAQ,IACpC,mBAAmB,KAAK,KAAK,KAAK,SAAS,KAAK,SAAS;AAAA,MACvD,SAAS,KAAK;AAAA,IAChB,CAAC;AAEL,SAAK,sBAAsB;AAG3B,QAAI,aAAa;AACjB,QAAI,gBAAgB,KAAK,IAAI;AAC7B,UAAM,sBACJ,kBAAkB,eAAe,YAAY,IACzC,MAAO,eAAe,YACtB;AAEN,UAAM,aAAa,YAAY;AAC7B,UAAI;AACF,aAAK;AAAA,UACH,qCAAqC,QAAQ,iBAAiB,gBAAgB,aAAa,EAAE,eAAe,mBAAmB;AAAA,QACjI;AACA,YAAI,kBAAkB;AACtB,YAAI,wBAAwB;AAC5B,YAAI,wBAAwB;AAC5B,YAAI,4BAA4B;AAChC,YAAI,0BAA0B;AAC9B,YAAI,qBAAqB;AACzB,yBAAiB,SAAS,iBAAiB;AAEzC,cAAI,CAAC,KAAK,iBAAiB,IAAI,QAAQ,GAAG;AACxC,iBAAK;AAAA,cACH,UAAU,QAAQ;AAAA,YACpB;AACA;AAAA,UACF;AAEA,gBAAM,QAAQ,QAAQ;AACtB,cAAI,CAAC,cAAc;AACjB,gBACE,CAAC,SACD,MAAM,aACN,MAAM,iBACN,MAAM,kBACN;AACA,mBAAK,aAAa,kCAAkC,QAAQ,EAAE;AAC9D;AAAA,YACF;AAAA,UACF;AAEA,cAAI,MAAM,KAAK,WAAW,EAAG;AAE7B,cAAI,CAAC,MAAM,SAAS,CAAC,2BAA2B;AAC9C,wCAA4B;AAC5B,gBAAI,WAAW;AACb,oBAAM,UAAU,MAAM,KAAK,SAAS,GAAG,EAAE,EAAE,SAAS,KAAK;AACzD;AAAA,gBACE,wDAAwD,QAAQ,SAAS,MAAM,KAAK,MAAM,eAAe;AAAA,kBACtG,MAAc,aAAa,KAAK,KAAK;AAAA,gBACxC,CAAC,UAAU,OAAO;AAAA,cACpB;AAAA,YACF;AAAA,UACF;AAGA,cAAI,MAAM,OAAO;AACf;AACA,gBAAI,oBAAoB,GAAG;AACzB,mBAAK;AAAA,gBACH,iCAAiC,MAAM,SAAS,SAAS,iBAAiB,MAAM,cAAc,SAAS;AAAA,cACzG;AAAA,YACF;AACA,gBAAI,kBAAkB,QAAQ,GAAG;AAC/B,mBAAK;AAAA,gBACH,YAAY,eAAe;AAAA,cAC7B;AAAA,YACF;AAEA,gBAAI,cAAc;AAEhB,kBAAI,CAAC,WAAW,wBAAwB;AACtC;AAAA,cACF;AACA,kBACE,KAAK,YACL,oBAAmB,eAAe,MAAM,IAAI,GAC5C;AACA,oBAAI,aAAa,CAAC,uBAAuB;AACvC,0CAAwB;AACxB,wBAAM,UAAU,MAAM,KAAK,SAAS,GAAG,EAAE,EAAE,SAAS,KAAK;AACzD;AAAA,oBACE,uDAAuD,QAAQ,SAAS,MAAM,KAAK,MAAM,UAAU,OAAO;AAAA,kBAC5G;AAAA,gBACF;AACA,mCAAmB,MAAM,IAAI;AAAA,cAC/B;AACA;AAAA,YACF,OAAO;AACL,oBAAM,cACJ,KAAK,YACL,aACA,CAAE,UAAkB,iBACpB,CAAE,UAAkB;AACtB,kBAAI,aAAa;AACf,sBAAM,KAAK;AACX,oBAAI,CAAC,GAAI;AAET,oBAAI,oBAAmB,eAAe,MAAM,IAAI,GAAG;AACjD,sBAAI;AACF,wBAAI,CAAC,uBAAuB;AAC1B,8CAAwB;AACxB,4BAAM,UAAU,MAAM,KACnB,SAAS,GAAG,EAAE,EACd,SAAS,KAAK;AACjB,2BAAK;AAAA,wBACH,uDAAuD,QAAQ,SAAS,MAAM,KAAK,MAAM,UAAU,OAAO;AAAA,sBAC5G;AAAA,oBACF;AACA,0BAAM,UAAU,GAAG,MAAM,MAAM,IAAI;AACnC,wBAAI,CAAC,SAAS;AACZ,4BAAM,IAAI;AAAA,wBAAc,CAAC,YACvB,GAAG,KAAK,SAAS,MAAM,QAAQ,CAAC;AAAA,sBAClC;AAAA,oBACF;AAAA,kBACF,QAAQ;AAAA,kBAAC;AAAA,gBACX;AAAA,cACF;AACA;AAAA,YACF;AACA;AAAA,UACF;AAIA,cAAI,MAAM,cAAc,UAAU,MAAM,cAAc,QAAQ;AAC5D,kBAAM,sBACJ,MAAM,cAAc,SAChB,gBAAoB,MAAM,IAAI,IAC9BC,iBAAoB,MAAM,IAAI;AACpC,gBAAI,eAAe,GAAG;AACpB,mBAAK,iBAAiB,MAAM,WAAW,mBAAmB;AAAA,YAC5D;AAEA,kBAAM,SAAS,KAAK,KAAK,QAAQ;AACjC,gBAAI,CAAC,OAAO,cAAc;AACxB,mBAAK,KAAK,qBAAqB,mBAAmB;AAClD,oBAAM,QAAQ,KAAK,KAAK,QAAQ;AAChC,kBAAI,MAAM,cAAc;AACtB,qBAAK,uBAAuB;AAAA,cAC9B;AAAA,YACF,WAAW,CAAC,KAAK,oBAAoB;AACnC,mBAAK,uBAAuB;AAAA,YAC9B;AAAA,UACF;AAEA;AACA,cAAI,aAAa,QAAQ,GAAG;AAC1B,iBAAK;AAAA,cACH,QAAQ,UAAU,qBAAqB,QAAQ,iBAAiB,MAAM,KAAK,MAAM;AAAA,YACnF;AAAA,UACF;AAIA,gBAAM,MAAM,KAAK,IAAI;AACrB,gBAAM,qBAAqB,MAAM;AACjC,gBAAM,WAAW,sBAAsB;AACvC,cAAI,WAAW,GAAG;AAEhB,kBAAM,IAAI;AAAA,cAAQ,CAAC,YACjB,WAAW,SAAS,KAAK,IAAI,UAAU,sBAAsB,CAAC,CAAC;AAAA,YACjE;AAAA,UACF;AACA,0BAAgB,KAAK,IAAI;AAEzB,cAAI,cAAc;AAChB,kBAAM,YAAa,MAAM,aAAa,KAAK,KAAK;AAGhD,kBAAM,sBACJ,cAAc,SACV,gBAAoB,MAAM,IAAI,IAC9BA,iBAAoB,MAAM,IAAI;AAMpC,gBAAI,CAAC,WAAW,wBAAwB;AACtC,kBAAI,cAAc,QAAQ;AACxB,sBAAM,EAAE,aAAa,IAAI,KAAK,KAAK,QAAQ;AAC3C,oBAAI,CAAC,cAAc;AACjB,sBAAI,aAAa,CAAC,yBAAyB;AACzC,8CAA0B;AAC1B;AAAA,sBACE,qEAAqE,QAAQ;AAAA,oBAC/E;AAAA,kBACF;AACA;AAAA,gBACF;AAGA,oBAAI,CAAC,WAAW,oBAAoB;AAClC,wBAAM,YAAY,KAAK,KAAK,uBAAuB;AACnD,sBAAI,aAAa,UAAU,SAAS,GAAG;AACrC,wCAAoB,WAAW,WAAW,KAAK;AAC/C,8BAAU,qBAAqB;AAAA,kBACjC;AAAA,gBACF;AAEA,oBAAI,CAAC,UAAU;AACb,4BAAU,kBAAkB,KAAK,IAAI;AACvC,sBAAM,SAAS,qBAAqB,mBAAmB;AACvD,sBAAM,WACJ,KAAK,IAAI,IAAK,UAAU;AAC1B,oBAAI,CAAC,UAAU,WAAW,KAAM;AAC9B,sBAAI,aAAa,CAAC,oBAAoB;AACpC,yCAAqB;AACrB;AAAA,sBACE,yDAAyD,QAAQ;AAAA,oBACnE;AAAA,kBACF;AACA;AAAA,gBACF;AAEA,0BAAU,yBAAyB;AAAA,cACrC,OAAO;AAIL,sBAAM,EAAE,aAAa,IAAI,KAAK,KAAK,QAAQ;AAC3C,oBAAI,CAAC,cAAc;AACjB,sBAAI,aAAa,CAAC,yBAAyB;AAEzC,8CAA0B;AAC1B;AAAA,sBACE,iEAAiE,QAAQ;AAAA,oBAC3E;AAAA,kBACF;AACA;AAAA,gBACF;AAGA,oBAAI,CAAC,WAAW,oBAAoB;AAClC,wBAAM,YAAY,KAAK,KAAK,uBAAuB;AACnD,sBAAI,aAAa,UAAU,SAAS,GAAG;AACrC,wCAAoB,WAAW,WAAW,KAAK;AAC/C,8BAAU,qBAAqB;AAAA,kBACjC;AAAA,gBACF;AAEA,sBAAM,QAAQ,oBAAoB,mBAAmB;AACrD,oBAAI,CAAC,OAAO;AACV;AAAA,gBACF;AAEA,0BAAU,yBAAyB;AAAA,cACrC;AAAA,YACF;AAIA,kBAAM,oBAAqB,MAAc;AAIzC,8CAAkC,iBAAiB;AAEnD,gBAAI,CAAC,WAAW,oBAAoB;AAClC,oBAAM,YAAY,KAAK,KAAK,uBAAuB;AACnD,kBAAI,aAAa,UAAU,SAAS,GAAG;AAErC,oCAAoB,WAAW,WAAW,KAAK;AAC/C,0BAAU,qBAAqB;AAAA,cACjC;AAAA,YACF;AACA,gBAAI,CAAC,uBAAuB;AAC1B,sCAAwB;AACxB,kBAAI,WAAW;AACb,sBAAM,UAAU,MAAM,KAAK,SAAS,GAAG,EAAE,EAAE,SAAS,KAAK;AACzD;AAAA,kBACE,wDAAwD,QAAQ,SAAS,MAAM,KAAK,MAAM,UAAU,OAAO;AAAA,gBAC7G;AAAA,cACF;AAAA,YACF;AAEA,gCAAoB,WAAW,qBAAqB,IAAI;AAAA,UAC1D,OAAO;AACL,gBAAI;AACF,kBACE,SACA,CAAC,MAAM,aACP,CAAC,MAAM,iBACP,CAAC,MAAM,kBACP;AACA,oBAAI,CAAC,uBAAuB;AAC1B,0CAAwB;AACxB,wBAAM,UAAU,MAAM,KAAK,SAAS,GAAG,EAAE,EAAE,SAAS,KAAK;AACzD,uBAAK;AAAA,oBACH,wDAAwD,QAAQ,SAAS,MAAM,KAAK,MAAM,UAAU,OAAO;AAAA,kBAC7G;AAAA,gBACF;AACA,sBAAM,UAAU,MAAM;AAAA,kBACpB,MAAM,cAAc,SAChB,gBAAoB,MAAM,IAAI,IAC9B,MAAM;AAAA,gBACZ;AACA,oBAAI,CAAC,SAAS;AACZ,wBAAM,IAAI,QAAc,CAAC,YAAY;AACnC,wBAAI,OAAO;AACT,4BAAM,KAAK,SAAS,MAAM,QAAQ,CAAC;AAAA,oBACrC,OAAO;AACL,8BAAQ;AAAA,oBACV;AAAA,kBACF,CAAC;AAAA,gBACH;AAAA,cACF;AAAA,YACF,SAAS,OAAO;AACd,oBAAM,OAAQ,OAAe;AAC7B,kBAAI,SAAS,WAAW,SAAS,8BAA8B;AAC7D,qBAAK;AAAA,kBACH,sCAAsC,QAAQ;AAAA,gBAChD;AACA;AAAA,cACF;AACA,mBAAK,OAAO;AAAA,gBACV,iEAAiE,QAAQ;AAAA,gBACzE;AAAA,cACF;AAAA,YACF;AAAA,UACF;AAAA,QACF;AACA,aAAK;AAAA,UACH,qCAAqC,QAAQ,YAAY,UAAU;AAAA,QACrE;AAAA,MACF,SAAS,OAAO;AACd,aAAK,OAAO;AAAA,UACV,uDAAuD,QAAQ;AAAA,UAC/D;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,eAAW,EAAE,MAAM,CAAC,UAAU;AAC5B,WAAK,OAAO;AAAA,QACV,uDAAuD,QAAQ;AAAA,QAC/D;AAAA,MACF;AAAA,IACF,CAAC;AAGD,YAAQ,QAAQ,GAAG,QAAQ,CAAC,SAAiB;AAC3C,YAAM,SAAS,KAAK,SAAS;AAC7B,UAAI,OAAO,SAAS,OAAO,KAAK,OAAO,SAAS,OAAO,GAAG;AACxD,aAAK,OAAO;AAAA,UACV,gDAAgD,QAAQ,KAAK,MAAM;AAAA,QACrE;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAc,oBAAmC;AAC/C,QAAI,KAAK,oBAAoB;AAC3B;AAAA,IACF;AAEA,SAAK,qBAAqB;AAC1B,SAAK,qBAAqB;AAC1B,SAAK,qBAAqB;AAC1B,SAAK,WAAW;AAChB,SAAK,YAAY;AACjB,SAAK,oBAAoB;AAGzB,SAAK,oBAAoB,IAAI,QAAc,CAAC,YAAY;AACtD,WAAK,oBAAoB;AAAA,IAC3B,CAAC;AAED,SAAK,oBAAoB,IAAI,QAAc,CAAC,YAAY;AACtD,WAAK,oBAAoB;AAAA,IAC3B,CAAC;AAED,SAAK;AAAA,MACH,sCAAsC,KAAK,OAAO;AAAA,IACpD;AAGA,UAAM,KAAK,KAAK,eAAe,KAAK,GAAG;AAIvC,SAAK,eAAe,IAAI,mBAAmB;AAAA,MACzC,eAAe;AAAA,MACf,cAAc,MACZ,mBAAmB,KAAK,KAAK,KAAK,SAAS,KAAK,SAAS;AAAA,QACvD,SAAS,KAAK;AAAA,MAChB,CAAC;AAAA,MACH,SAAS,CAAC,UAAU;AAClB,YAAI,MAAM,OAAO;AAEf,cACE,CAAC,KAAK,YACN,KAAK,IAAI,OAAO,aAAa,MAAM,SACnC,oBAAmB,eAAe,MAAM,IAAI,GAC5C;AACA,kBAAM,OAAO,oBAAmB,sBAAsB,MAAM,IAAI;AAChE,gBAAI,MAAM;AACR,mBAAK,WAAW;AAChB,mBAAK,YAAY;AAAA,gBACf,OAAO;AAAA,gBACP,YAAY,KAAK;AAAA,gBACjB,UAAU,KAAK;AAAA,gBACf,WAAW,KAAK;AAAA,cAClB;AACA,mBAAK,oBAAoB,OAAO,KAAK,MAAM,IAAI;AAC/C,mBAAK,uBAAuB;AAC5B,mBAAK;AAAA,gBACH,4BAA4B,KAAK,UAAU,SAAS,KAAK,QAAQ;AAAA,cACnE;AAAA,YACF;AAAA,UACF;AACA;AAAA,QACF;AAEA,YAAI,MAAM,KAAK,WAAW,EAAG;AAC7B,YAAI,MAAM,cAAc,UAAU,MAAM,cAAc,QAAQ;AAC5D,eAAK,iBAAiB,MAAM,WAAW,eAAe;AAAA,QACxD;AAGA,aAAK,KAAK,qBAAqB,MAAM,IAAI;AACzC,cAAM,EAAE,aAAa,IAAI,KAAK,KAAK,QAAQ;AAC3C,YAAI,cAAc;AAChB,eAAK,uBAAuB;AAAA,QAC9B;AAAA,MACF;AAAA,MACA,SAAS,CAAC,UAAU;AAClB,aAAK,OAAO;AAAA,UACV,oDAAoD,KAAK;AAAA,QAC3D;AAAA,MACF;AAAA,IACF,CAAC;AACD,SAAK,aAAa,MAAM;AAIxB,SAAK,2BAA2B;AAChC,SAAK,wBAAwB,WAAW,MAAM;AAC5C,UAAI,KAAK,iBAAiB,SAAS,GAAG;AACpC,aAAK;AAAA,UACH;AAAA,QACF;AACA,aAAK,KAAK,iBAAiB;AAAA,MAC7B;AAAA,IACF,GAAG,IAAM;AAET,IAAC,KAAK,uBAA+B,QAAQ;AAAA,EAC/C;AAAA,EAEQ,yBAA+B;AACrC,QAAI,CAAC,KAAK,sBAAsB,KAAK,mBAAmB;AACtD,WAAK,qBAAqB;AAC1B,WAAK;AAAA,QACH,gDAAgD,KAAK,OAAO;AAAA,MAC9D;AACA,WAAK,kBAAkB;AACvB,WAAK,oBAAoB;AAAA,IAC3B;AAAA,EACF;AAAA,EAEQ,yBAA+B;AACrC,QAAI,CAAC,KAAK,sBAAsB,KAAK,mBAAmB;AACtD,WAAK,qBAAqB;AAC1B,WAAK,kBAAkB;AACvB,WAAK,oBAAoB;AAAA,IAC3B;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,mBAAkC;AAC9C,QAAI,CAAC,KAAK,oBAAoB;AAC5B;AAAA,IACF;AAEA,SAAK,aAAa,wBAAwB;AAE1C,SAAK,KAAK,cAAc;AAExB,SAAK,2BAA2B;AAEhC,SAAK,qBAAqB;AAC1B,SAAK,qBAAqB;AAC1B,SAAK,oBAAoB;AACzB,QAAI,KAAK,mBAAmB;AAE1B,WAAK,oBAAoB;AAAA,IAC3B;AACA,SAAK,qBAAqB;AAC1B,SAAK,oBAAoB;AACzB,QAAI,KAAK,mBAAmB;AAC1B,WAAK,oBAAoB;AAAA,IAC3B;AAEA,QAAI,KAAK,cAAc;AACrB,YAAM,SAAS,KAAK;AACpB,WAAK,eAAe;AACpB,YAAM,OAAO,KAAK;AAAA,IACpB;AAGA,QAAI,KAAK,qBAAqB;AAC5B,UAAI;AACF,cAAM,KAAK,oBAAoB,OAAO,MAAgB;AAAA,MACxD,QAAQ;AAAA,MAAC;AACT,WAAK,sBAAsB;AAAA,IAC7B;AAAA,EAGF;AAAA;AAAA;AAAA;AAAA,EAKQ,aAAa,UAAwB;AAC3C,QAAI,KAAK,iBAAiB,IAAI,QAAQ,GAAG;AACvC,WAAK,iBAAiB,OAAO,QAAQ;AACrC,WAAK,KAAK,sBAAsB,QAAQ;AACxC,WAAK,OAAO;AAAA,QACV,kDAAkD,QAAQ;AAAA,MAC5D;AAGA,UAAI,KAAK,iBAAiB,SAAS,GAAG;AACpC,aAAK,KAAK,iBAAiB;AAAA,MAC7B;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,eAAe,YAAoB,KAAsB;AAC7D,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAI,CAAC,KAAK,wBAAwB;AAChC,eAAO,IAAI,MAAM,yBAAyB,CAAC;AAC3C;AAAA,MACF;AAEA,YAAM,UAAU,WAAW,MAAM;AAC/B;AAAA,UACE,IAAI;AAAA,YACF,gDAAgD,SAAS;AAAA,UAC3D;AAAA,QACF;AAAA,MACF,GAAG,SAAS;AAGZ,YAAM,YAAY,MAAM;AACtB,cAAM,SAAS,IAAQ,YAAO;AAC9B,eAAO,WAAW,GAAI;AACtB,eAAO,GAAG,WAAW,MAAM;AACzB,iBAAO,QAAQ;AAEf,cAAI,KAAK,sBAAsB,KAAK,mBAAmB;AACrD,iBAAK;AAAA,cACH;AAAA,YACF;AACA,iBAAK,kBACF,KAAK,MAAM;AACV,2BAAa,OAAO;AACpB,sBAAQ;AAAA,YACV,CAAC,EACA,MAAM,CAAC,UAAU;AAChB,2BAAa,OAAO;AACpB,qBAAO,KAAK;AAAA,YACd,CAAC;AAAA,UACL,OAAO;AAEL,yBAAa,OAAO;AACpB,oBAAQ;AAAA,UACV;AAAA,QACF,CAAC;AACD,eAAO,GAAG,WAAW,MAAM;AACzB,iBAAO,QAAQ;AACf,qBAAW,WAAW,GAAG;AAAA,QAC3B,CAAC;AACD,eAAO,GAAG,SAAS,MAAM;AACvB,iBAAO,QAAQ;AACf,qBAAW,WAAW,GAAG;AAAA,QAC3B,CAAC;AACD,eAAO,QAAQ,KAAK,YAAY,KAAK,UAAU;AAAA,MACjD;AAGA,gBAAU;AAAA,IACZ,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,OAAsB;AAC1B,QAAI,CAAC,KAAK,QAAQ;AAChB;AAAA,IACF;AAEA,SAAK,OAAO;AAAA,MACV,gDAAgD,KAAK,UAAU,IAAI,KAAK,UAAU;AAAA,IACpF;AAGA,UAAM,KAAK,iBAAiB;AAG5B,UAAM,YAAY,MAAM,KAAK,KAAK,gBAAgB;AAClD,eAAW,YAAY,WAAW;AAChC,YAAM,YAAY,KAAK,gBAAgB,IAAI,QAAQ;AACnD,UAAI,WAAW;AAEb,YAAI,UAAU,QAAQ;AACpB,cAAI;AACF,sBAAU,OAAO,OAAO,IAAI;AAC5B,sBAAU,OAAO,KAAK,SAAS;AAC/B,uBAAW,MAAM;AACf,kBAAI;AACF,0BAAU,QAAQ,KAAK,SAAS;AAAA,cAClC,QAAQ;AAAA,cAAC;AAAA,YACX,GAAG,GAAI;AAAA,UACT,QAAQ;AAAA,UAAC;AAAA,QACX;AAGA,YAAI,UAAU,WAAW;AACvB,cAAI;AACF,sBAAU,UAAU,MAAM;AAAA,UAC5B,QAAQ;AAAA,UAAC;AAAA,QACX;AAGA,YAAI,UAAU,cAAc,CAAC,UAAU,WAAW,WAAW;AAC3D,cAAI;AACF,sBAAU,WAAW,QAAQ;AAAA,UAC/B,QAAQ;AAAA,UAAC;AAAA,QACX;AAAA,MACF;AAAA,IACF;AACA,SAAK,gBAAgB,MAAM;AAG3B,QAAI,KAAK,wBAAwB;AAC/B,YAAM,IAAI,QAAc,CAAC,YAAY;AACnC,aAAK,wBAAwB,MAAM,MAAM;AACvC,kBAAQ;AAAA,QACV,CAAC;AAAA,MACH,CAAC;AACD,WAAK,yBAAyB;AAAA,IAChC;AAEA,SAAK,SAAS;AACd,SAAK,iBAAiB,MAAM;AAC5B,SAAK,KAAK,OAAO;AACjB,SAAK,OAAO,KAAK,0CAA0C;AAAA,EAC7D;AAAA;AAAA;AAAA;AAAA,EAKA,aAAqB;AACnB,WAAO,UAAU,KAAK,UAAU,IAAI,KAAK,UAAU,GAAG,KAAK,IAAI;AAAA,EACjE;AAAA;AAAA;AAAA;AAAA,EAKA,WAAoB;AAClB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,iBAAyB;AACvB,WAAO,KAAK,iBAAiB;AAAA,EAC/B;AACF;;;AEn3EA,SAAS,oBAAoB,OAA+C;AAC1E,MAAI,SAAS,KAAM,QAAO;AAC1B,QAAM,IAAI,OAAO,KAAK;AACtB,SAAO,OAAO,SAAS,CAAC,IAAI,IAAI;AAClC;AAEA,SAAS,mBAAmB,OAAyB;AACnD,MAAI,OAAO,UAAU,SAAU,QAAO,QAAQ;AAC9C,MAAI,OAAO,UAAU,UAAU;AAC7B,UAAM,IAAI,OAAO,KAAK;AACtB,QAAI,OAAO,SAAS,CAAC,EAAG,QAAO,IAAI;AACnC,WAAO,MAAM,SAAS,KAAK,UAAU;AAAA,EACvC;AACA,SAAO,QAAQ,KAAK;AACtB;AAEO,SAAS,2BACd,WACA,SACyB;AACzB,MAAI,CAAC,aAAa,OAAO,cAAc,SAAU,QAAO;AAIxD,QAAM,eAAe;AACrB,QAAM,mBAAmB,aAAa,OAAO;AAC7C,QAAM,OAAO,aAAa;AAE1B,QAAM,SAAsB;AAAA,IAC1B,GAAI,QAAQ,OAAO,SAAS,WAAW,OAAO,CAAC;AAAA,IAC/C,GAAI,oBAAoB,OAAO,qBAAqB,WAChD,mBACA,CAAC;AAAA,EACP;AAEA,SAAO,OAAO,KAAK,MAAM,EAAE,SAAS,SAAS;AAC/C;AAEO,SAAS,gBACd,WACA,IACS;AACT,MAAI,CAAC,UAAW,QAAO;AACvB,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,SAAS,GAAG;AACpD,QAAI,CAAC,GAAG,KAAK,GAAG,EAAG;AACnB,QAAI,mBAAmB,KAAK,EAAG,QAAO;AAAA,EACxC;AACA,SAAO;AACT;AAEO,SAAS,gBAAgB,KAAsC;AACpE,MAAI,CAAC,IAAK,QAAO;AAEjB,QAAM,QAAQ,IAAI,MAAM,sCAAsC;AAC9D,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,aAAa,MAAM,CAAC,KAAK;AAE/B,QAAM,UAAU,WAAW,MAAM,8BAA8B,IAAI,CAAC;AAEpE,QAAM,cAAc,CAAC,QAAoC;AACvD,UAAM,IAAI,WAAW,MAAM,IAAI,OAAO,IAAI,GAAG,eAAe,GAAG,KAAK,GAAG,CAAC;AACxE,WAAO,oBAAoB,IAAI,CAAC,CAAC;AAAA,EACnC;AAEA,QAAM,iBAAiB,CAAC,QAAoC;AAC1D,UAAM,IAAI,WAAW,MAAM,IAAI,OAAO,IAAI,GAAG,eAAe,GAAG,KAAK,GAAG,CAAC;AACxE,WAAO,IAAI,CAAC;AAAA,EACd;AAEA,QAAM,QAAuB,CAAC;AAC9B,aAAW,aAAa,WAAW;AAAA,IACjC;AAAA,EACF,GAAG;AACD,UAAM,UAAU,UAAU,CAAC,KAAK;AAChC,UAAM,OAAoB;AAAA,MACxB,OACE,oBAAoB,QAAQ,MAAM,0BAA0B,IAAI,CAAC,CAAC,KAClE;AAAA,IACJ;AAEA,eAAW,YAAY,QAAQ;AAAA,MAC7B;AAAA,IACF,GAAG;AACD,YAAM,MAAM,SAAS,CAAC;AACtB,YAAM,QAAQ,SAAS,CAAC;AACxB,UAAI,CAAC,IAAK;AACV,UAAI,QAAQ,QAAS;AACrB,YAAM,IAAI,oBAAoB,KAAK;AACnC,MAAC,KAAa,GAAG,IAAI,KAAK;AAAA,IAC5B;AAEA,UAAM,KAAK,IAAI;AAAA,EACjB;AAEA,QAAM,UAAuB,EAAE,MAAM;AACrC,MAAI,YAAY,OAAW,SAAQ,UAAU;AAE7C,QAAM,YAAY,CAAC,KAAwB,QAAgB;AACzD,UAAM,IAAI,YAAY,GAAG;AACzB,QAAI,MAAM,OAAW,CAAC,QAAgB,GAAG,IAAI;AAAA,EAC/C;AACA,YAAU,kBAAkB,gBAAgB;AAC5C,YAAU,mBAAmB,iBAAiB;AAC9C,YAAU,WAAW,SAAS;AAC9B,YAAU,cAAc,YAAY;AACpC,YAAU,YAAY,UAAU;AAChC,YAAU,UAAU,QAAQ;AAC5B,YAAU,QAAQ,MAAM;AACxB,YAAU,cAAc,YAAY;AACpC,YAAU,aAAa,WAAW;AAClC,YAAU,OAAO,KAAK;AACtB,YAAU,WAAW,SAAS;AAC9B,YAAU,SAAS,OAAO;AAC1B,YAAU,QAAQ,MAAM;AACxB,YAAU,UAAU,QAAQ;AAC5B,YAAU,YAAY,UAAU;AAChC,YAAU,QAAQ,MAAM;AACxB,YAAU,SAAS,OAAO;AAC1B,YAAU,aAAa,WAAW;AAElC,QAAM,aAAa,eAAe,YAAY;AAC9C,MAAI,eAAe,OAAW,CAAC,QAAgB,aAAa;AAE5D,SAAO;AACT;AAEA,SAAS,yBACP,SACA,SACyB;AACzB,MAAI,CAAC,SAAS,OAAO,OAAQ,QAAO;AAQpC,QAAM,mBAAmB,CAAC,SAA8B;AACtD,UAAM,UAAU;AAChB,QAAI,QAAQ;AAGZ,QAAI,QAAQ,QAAQ,KAAM,UAAS;AAGnC,UAAM,iBAAiB;AAAA,MACrB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AACA,eAAW,KAAK,gBAAgB;AAC9B,UAAI,QAAQ,CAAC,MAAM,OAAW,UAAS;AAAA,IACzC;AAGA,aAAS,KAAK,IAAI,IAAI,KAAK,IAAI,GAAG,OAAO,KAAK,OAAO,EAAE,SAAS,CAAC,CAAC;AAClE,WAAO;AAAA,EACT;AAEA,QAAM,WAAW,CAAC,UAA2C;AAC3D,UAAM,aAAa,QAAQ,MAAM,OAAO,CAAC,MAAM,EAAE,UAAU,KAAK;AAChE,QAAI,CAAC,WAAW,OAAQ,QAAO;AAC/B,WAAO,WACJ,MAAM,EACN,KAAK,CAAC,GAAG,MAAM,iBAAiB,CAAC,IAAI,iBAAiB,CAAC,CAAC,EAAE,CAAC;AAAA,EAChE;AAEA,SAAO,SAAS,OAAO;AACzB;AAEO,SAAS,0BAA0B,QAMnB;AACrB,QAAM,EAAE,QAAQ,IAAI;AACpB,QAAM,OAAO,2BAA2B,OAAO,WAAW,OAAO;AACjE,QAAM,cAAc,yBAAyB,OAAO,SAAS,OAAO;AAEpE,QAAM,aAAa,OAAO,SAAS;AACnC,QAAM,UACJ,OAAO,eAAe,WAAW,WAAW,YAAY,IAAI;AAK9D,QAAM,aAAa,cAAe,YAAoB,UAAU;AAChE,QAAM,gBAAgB,cACjB,YAAoB,aACrB;AACJ,QAAM,8BACJ,eAAe,UAAa,kBAAkB;AAChD,QAAM,qCACJ,+BACA,CAAC,mBAAmB,UAAU,KAC9B,CAAC,mBAAmB,aAAa;AACnC,QAAM,wBACJ,mBAAmB,UAAU,KAC7B,mBAAmB,aAAa,KAChC,mBAAoB,aAAqB,SAAS;AAKpD,QAAM,wBACH,YAAY,UAAU,YAAY,QAAQ,CAAC;AAE9C,QAAM,wBAAwB,cAC1B,mBAAoB,YAAoB,OAAO,IAC/C;AAGJ,QAAM,eAAe,cAAe,YAAoB,YAAY;AAGpE,QAAM,kCAAkC,iBAAiB;AACzD,QAAM,wBAAwB,kCAC1B,mBAAmB,YAAY,IAC/B;AACJ,QAAM,qCACJ,mCAAmC,CAAC,mBAAmB,YAAY;AACrE,QAAM,qBAAqB,cACtB,YAAoB,kBACrB;AACJ,QAAM,wBAAwB,OAAO,SAAS,OAAO,kBAAkB,CAAC,IACpE,OAAO,kBAAkB,IAAI,IAC7B,mBAAmB,kBAAkB;AACzC,QAAM,sBACJ,OAAO,OAAO,UAAU,YAAY,YAAY,KAAK,OAAO,KAAK;AAQnE,QAAM,eAAe,cAAe,YAAoB,YAAY;AACpE,QAAM,YACJ,OAAO,iBAAiB,WACpB,eACA,OAAO,iBAAiB,WACtB,OAAO,YAAY,IACnB;AAER,QAAM,oBACJ,0BACC,UAAU,YAAY,UAAU,YAAY,MAAM;AACrD,QAAM,wBAAwB,UAC1B,QAAQ,SAAS,IAAI,KAAK,YAAY,QAAQ,YAAY,QAC1D;AACJ,QAAM,qBAAqB,UAAU,QAAQ,SAAS,GAAG,IAAI;AAE7D,QAAM,0BAA0B,gBAAgB,MAAM,UAAU;AAChE,QAAM,6BAA6B;AAAA,IACjC;AAAA,IACA;AAAA,EACF;AACA,QAAM,wBAAwB;AAAA,IAC5B;AAAA,IACA;AAAA,EACF;AAMA,QAAM,yBACJ,mBAAoB,OAAO,SAAiB,SAAS,MACpD,cACG,mBAAoB,YAAoB,YAAY,IACpD;AAIN,QAAM,0BAA0B,qCAC5B,QACA,gBAAgB,MAAM,MAAM;AAChC,QAAM,uBAAuB,qCACzB,QACA,gBAAgB,MAAM,gCAAgC;AAC1D,QAAM,0BAA0B,gBAAgB,MAAM,SAAS;AAM/D,QAAM,sBACJ,gBAAgB,MAAM,WAAW,KACjC,gBAAgB,MAAM,UAAU,KAChC,gBAAgB,MAAM,YAAY;AAQpC,QAAM,oBAAoB,cACtB,mBAAoB,YAAoB,KAAK,KAC7C,mBAAoB,YAAoB,QAAQ,KAChD,mBAAoB,YAAoB,SAAS,KACjD,mBAAoB,YAAoB,OAAO,IAC/C;AAYJ,QAAM,6BAA6B,cAC/B,mBAAoB,YAAoB,MAAM,KAC9C,mBAAoB,YAAoB,OAAO,IAC/C;AAGJ,QAAM,+BAA+B,gBAAgB,MAAM,aAAa;AAExE,QAAM,SAAS,yBAAyB;AACxC,QAAM,UAAU,yBAAyB;AACzC,QAAM,UAAU,sBAAsB;AACtC,QAAM,aAAa,qCACf,QACA,yBAAyB;AAE7B,QAAM,cAAc,uBAAuB,QAAQ;AACnD,QAAM,eAAe,uBAAuB,QAAQ;AACpD,QAAM,eAAe,uBAAuB,QAAQ;AACpD,QAAM,kBAAkB,uBAAuB,QAAQ;AAEvD,QAAM,SAA6B;AAAA,IACjC;AAAA,IACA,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,QAAQ,uBACJ,QACA,qBACA,eACA,gBACA,gBACA;AAAA,IACJ,YAAY,yBAAyB;AAAA,IACrC,aAAa;AAAA,IACb,UAAU;AAAA;AAAA,IAEV,eAAe,OAAO,SAAS,SAAmB,IAC7C,aAAwB,IACzB;AAAA,IACJ,QAAQ,uBAAuB;AAAA,IAC/B,YAAY,yBAAyB;AAAA,IACrC,iBAAiB,8BAA8B;AAAA,EACjD;AAEA,MAAI,YAAY,OAAW,QAAO,UAAU;AAC5C,SAAO;AACT;;;AC5XA,SAAS,SAAAC,cAAa;AACtB,SAAS,aAAa;AACtB,SAAS,eAAe;AACxB,SAAS,mBAAkC;;;ACa3C,IAAM,iBAAiB;AACvB,IAAM,eAAe;AAGrB,IAAM,UAAU;AAChB,IAAM,UAAU;AAChB,IAAM,YAAY;AAGlB,IAAM,mBAAmB;AACzB,IAAM,mBAAmB;AAGzB,IAAI,QAAQ;AACZ,IAAI,QAAQ;AACZ,IAAI,UAAU;AAKd,SAAS,YAAoB;AAC3B,QAAM,SAAS,OAAO,MAAM,gBAAgB,GAAI;AAGhD,SAAO,CAAC,IAAI;AACZ,SAAO,CAAC,IAAI,KAAS,WAAW,IAAK;AACrC,SAAO,CAAC,IAAI,UAAU;AACtB,SAAO,CAAC,IAAI,KAAQ,QAAQ;AAC5B,UAAS,QAAQ,IAAK;AAGtB,SAAO,CAAC,IAAI;AAGZ,MAAI,MAAM;AACV,SAAO,KAAK,IAAI;AAChB,SAAO,KAAK,IAAI;AAChB,SAAO,KAAK,IAAI;AAChB,SAAO,KAAK,IAAI;AAChB,SAAO,KAAK,IAAI;AAChB,SAAO,KAAK,IAAI;AAChB,SAAO,KAAK,IAAI;AAChB,SAAO,KAAK,IAAI;AAGhB,SAAO,KAAK,IAAI;AAChB,SAAO,KAAK,IAAI;AAChB,SAAO,KAAK,IAAI,MAAS,WAAW,IAAK;AACzC,SAAO,KAAK,IAAI,UAAU;AAG1B,QAAM,MAAM,UAAU,OAAO,SAAS,GAAG,GAAG,CAAC;AAC7C,SAAO,cAAc,KAAK,GAAG;AAE7B,SAAO;AACT;AAKA,SAAS,UAAU,YAA4B;AAC7C,QAAM,SAAS,OAAO,MAAM,gBAAgB,GAAI;AAGhD,SAAO,CAAC,IAAI;AACZ,SAAO,CAAC,IAAI,KAAS,WAAW,IAAK;AACrC,SAAO,CAAC,IAAI,UAAU;AACtB,SAAO,CAAC,IAAI,KAAQ,QAAQ;AAC5B,UAAS,QAAQ,IAAK;AAGtB,SAAO,CAAC,IAAI;AAGZ,MAAI,MAAM;AACV,SAAO,KAAK,IAAI;AAChB,SAAO,KAAK,IAAI;AAChB,SAAO,KAAK,IAAI;AAChB,SAAO,KAAK,IAAI;AAChB,SAAO,KAAK,IAAI;AAChB,SAAO,KAAK,IAAI;AAChB,SAAO,KAAK,IAAI;AAChB,SAAO,KAAK,IAAI;AAChB,SAAO,KAAK,IAAI,MAAS,aAAa,IAAK;AAC3C,SAAO,KAAK,IAAI,YAAY;AAC5B,SAAO,KAAK,IAAI;AAChB,SAAO,KAAK,IAAI;AAGhB,SAAO,KAAK,IAAI;AAChB,SAAO,KAAK,IAAI,MAAS,aAAa,IAAK;AAC3C,SAAO,KAAK,IAAI,YAAY;AAC5B,SAAO,KAAK,IAAI;AAChB,SAAO,KAAK,IAAI;AAGhB,QAAM,MAAM,UAAU,OAAO,SAAS,GAAG,GAAG,CAAC;AAC7C,SAAO,cAAc,KAAK,GAAG;AAE7B,SAAO;AACT;AASA,SAAS,eACP,MACA,KACA,YACU;AACV,QAAM,UAAoB,CAAC;AAG3B,QAAM,SAAS,KAAK,MAAO,MAAM,MAAS,GAAS;AAInD,QAAM,eAAe;AACrB,QAAM,YAAY,OAAO,MAAM,YAAY;AAE3C,MAAI,MAAM;AAEV,YAAU,KAAK,IAAI;AACnB,YAAU,KAAK,IAAI;AACnB,YAAU,KAAK,IAAI;AAEnB,YAAU,KAAK,IAAI;AAEnB,YAAU,KAAK,IAAI;AACnB,YAAU,KAAK,IAAI;AAEnB,YAAU,KAAK,IAAI;AACnB,YAAU,KAAK,IAAI;AAEnB,YAAU,KAAK,IAAI;AAInB,YAAU,KAAK,IAAI,KAAS,UAAU,KAAM;AAC5C,YAAU,KAAK,IAAK,UAAU,KAAM;AACpC,YAAU,KAAK,IAAI,IAAS,UAAU,KAAM;AAC5C,YAAU,KAAK,IAAK,UAAU,IAAK;AACnC,YAAU,KAAK,IAAI,IAAS,UAAU,IAAK;AAG3C,QAAM,UAAU,OAAO,OAAO,CAAC,WAAW,IAAI,CAAC;AAC/C,MAAI,YAAY;AAGhB,MAAI,UAAU;AACd,SAAO,YAAY,QAAQ,QAAQ;AACjC,UAAM,SAAS,OAAO,MAAM,gBAAgB,GAAI;AAChD,QAAI,SAAS;AAGb,WAAO,QAAQ,IAAI;AACnB,WAAO,QAAQ,KAAK,UAAU,KAAO,KAAU,aAAa,IAAK;AACjE,WAAO,QAAQ,IAAI,YAAY;AAE/B,UAAM,YAAY,QAAQ,SAAS;AACnC,UAAM,aAAa,iBAAiB;AAEpC,QAAI,aAAa,YAAY;AAE3B,aAAO,QAAQ,IAAI,KAAQ,UAAU;AACrC,gBAAW,UAAU,IAAK;AAC1B,cAAQ,KAAK,QAAQ,QAAQ,WAAW,YAAY,UAAU;AAC9D,mBAAa;AAAA,IACf,OAAO;AAEL,YAAM,WAAW,aAAa,YAAY;AAE1C,UAAI,WAAW,GAAG;AAEhB,eAAO,QAAQ,IAAI,KAAQ,UAAU;AACrC,kBAAW,UAAU,IAAK;AAC1B,eAAO,QAAQ,IAAI,iBAAiB,IAAI,IAAI;AAC5C,YAAI,WAAW,YAAY;AACzB,iBAAO,QAAQ,IAAI;AAEnB,mBAAS,IAAI,QAAQ,IAAI,iBAAiB,WAAW,KAAK;AACxD,mBAAO,CAAC,IAAI;AAAA,UACd;AAAA,QACF,OAAO;AACL,iBAAO,QAAQ,IAAI;AACnB,mBAAS,IAAI,QAAQ,IAAI,iBAAiB,WAAW,KAAK;AACxD,mBAAO,CAAC,IAAI;AAAA,UACd;AAAA,QACF;AACA,gBAAQ,KAAK,QAAQ,iBAAiB,WAAW,SAAS;AAC1D,qBAAa;AAAA,MACf,OAAO;AAEL,eAAO,QAAQ,IAAI,KAAQ,UAAU;AACrC,kBAAW,UAAU,IAAK;AAE1B,YAAI,aAAa,GAAG;AAClB,iBAAO,QAAQ,IAAI;AAAA,QACrB,OAAO;AACL,iBAAO,QAAQ,IAAI;AACnB,cAAI,WAAW,YAAY;AACzB,mBAAO,QAAQ,IAAI;AAAA,UACrB,OAAO;AACL,mBAAO,QAAQ,IAAI;AAAA,UACrB;AAEA,mBAAS,IAAI,GAAG,IAAI,WAAW,GAAG,KAAK;AACrC,mBAAO,QAAQ,IAAI;AAAA,UACrB;AAAA,QACF;AAEA,gBAAQ,KAAK,QAAQ,QAAQ,WAAW,YAAY,SAAS;AAC7D,qBAAa;AAAA,MACf;AAAA,IACF;AAEA,YAAQ,KAAK,MAAM;AACnB,cAAU;AAAA,EACZ;AAEA,SAAO;AACT;AAKA,SAAS,UAAU,MAAsB;AACvC,MAAI,MAAM;AACV,WAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AACpC,WAAO,KAAK,CAAC,KAAM;AACnB,aAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AAC1B,UAAI,MAAM,YAAY;AACpB,eAAQ,OAAO,IAAK,cAAgB;AAAA,MACtC,OAAO;AACL,cAAO,OAAO,MAAO;AAAA,MACvB;AAAA,IACF;AAAA,EACF;AACA,SAAO,QAAQ;AACjB;AAUO,IAAM,cAAN,MAAkB;AAAA,EACN;AAAA,EACT,UAAU;AAAA,EACV,UAAU;AAAA,EACV,iBAAiB;AAAA,EACR,oBAAoB;AAAA;AAAA,EAErC,YAAY,SAA6B;AACvC,SAAK,aACH,QAAQ,cAAc,SAAS,mBAAmB;AAAA,EACtD;AAAA;AAAA;AAAA;AAAA,EAKA,OAAO,gBAAsB;AAC3B,YAAQ;AACR,YAAQ;AACR,cAAU;AAAA,EACZ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,IAAI,MAAc,cAAsB,YAA6B;AACnE,UAAM,UAAoB,CAAC;AAG3B,QACE,CAAC,KAAK,WACN,CAAC,KAAK,WACN,cACA,KAAK,kBAAkB,KAAK,mBAC5B;AACA,cAAQ,KAAK,UAAU,CAAC;AACxB,cAAQ,KAAK,UAAU,KAAK,UAAU,CAAC;AACvC,WAAK,UAAU;AACf,WAAK,UAAU;AACf,WAAK,iBAAiB;AAAA,IACxB;AACA,SAAK;AAGL,UAAM,eAAe,eAAe,MAAM,cAAc,UAAU;AAClE,YAAQ,KAAK,GAAG,YAAY;AAE5B,WAAO,OAAO,OAAO,OAAO;AAAA,EAC9B;AACF;;;AClUA,SAAS,iBAAiB;AAM1B,IAAM,SAAS,IAAI,UAAU;AAAA,EAC3B,kBAAkB;AAAA,EAClB,qBAAqB;AAAA,EACrB,cAAc;AAAA,EACd,YAAY;AAAA,EACZ,eAAe;AAAA,EACf,qBAAqB;AAAA,EACrB,wBAAwB;AAC1B,CAAC;AAEM,SAAS,eACd,KACG;AAEH,SAAO,OAAO,MAAM,GAAG;AACzB;AAEA,SAAS,oBAAoB,KAAqB;AAChD,SAAO,OAAO,OAAO,EAAE,EACpB,QAAQ,0BAA0B,EAAE,EACpC,KAAK;AACV;AAMO,SAAS,uBAAyC,KAAgB;AACvE,QAAM,UAAU,oBAAoB,GAAG;AACvC,MAAI,CAAC,QAAS,QAAO,CAAC;AAEtB,QAAM,UAAU,SAAS,OAAO;AAChC,QAAM,SAAS,OAAO,MAAM,OAAO;AACnC,SAAO,OAAO;AAChB;;;AF2LA,SAAS,MAAM,gBAAgB;;;AG/NxB,IAAM,sBAAsB,CAAC,QAAmC;AACrE,QAAM,YAA+B,CAAC;AAEtC,QAAM,gBAAgB;AAAA,IACpB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAEA,aAAW,gBAAgB,eAAe;AACxC,UAAM,eAAe,IAAI,OAAO,IAAI,YAAY,yBAAyB,YAAY,KAAK,GAAG;AAC7F,UAAM,eAAe,IAAI,MAAM,YAAY;AAE3C,QAAI,CAAC,aAAc;AAEnB,UAAM,aAAa,aAAa,CAAC,KAAK;AACtC,UAAM,mBAAmB,WAAW,SAAS,0CAA0C;AAEvF,eAAW,SAAS,kBAAkB;AACpC,YAAM,eAAe,MAAM,CAAC,KAAK;AACjC,YAAM,gBAAgB,WAAW,cAAc,WAAW,KAAK,WAAW,cAAc,OAAO;AAC/F,YAAM,eAAe,WAAW,cAAc,cAAc;AAE5D,UAAI,CAAC,aAAc;AAEnB,YAAM,aAA8B,gBAAgB,OAAO,aAAa,IAAI;AAC5E,UAAI,CAAC,UAAU,UAAU,EAAG,WAAU,UAAU,IAAI,CAAC;AAErD,YAAM,eAAe,aAClB,MAAM,GAAG,EACT,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EACnB,OAAO,OAAO;AAEjB,iBAAW,cAAc,cAAc;AACrC,kBAAU,UAAU,EAAG,UAAU,IAAI;AAAA,MACvC;AAAA,IACF;AAAA,EACF;AAEA,QAAM,mBAAmB,IAAI,MAAM,6CAA6C;AAChF,MAAI,kBAAkB;AACpB,UAAM,iBAAiB,iBAAiB,CAAC,KAAK;AAC9C,UAAM,iBAAiB,eAAe,SAAS,kCAAkC;AAEjF,QAAI,CAAC,UAAU,KAAM,WAAU,OAAO,CAAC;AAEvC,eAAW,cAAc,gBAAgB;AACvC,YAAM,UAAU,WAAW,CAAC;AAC5B,YAAM,YAAY,WAAW,CAAC;AAE9B,UAAI,CAAC,WAAW,cAAc,OAAW;AACzC,UAAI,CAAC,SAAS,SAAS,WAAW,EAAE,SAAS,OAAO,EAAG;AACvD,UAAI,YAAY,eAAe,YAAY,eAAgB;AAE3D,YAAM,WAAW,OAAO,SAAS;AACjC,gBAAU,KAAM,OAAO,IAAI,OAAO,MAAM,QAAQ,IAAI,YAAY;AAAA,IAClE;AAAA,EACF;AAEA,MAAI,UAAU,QAAQ,OAAO,KAAK,UAAU,IAAI,EAAE,WAAW,GAAG;AAC9D,WAAO,UAAU;AAAA,EACnB;AAEA,SAAO;AACT;;;AC3EO,IAAM,oBAAoB,CAAC,MAAuB;AACvD,MAAI,aAAa,OAAO;AACtB,UAAM,YAAY,QAAQ,IAAI,GAAG,MAAM;AACvC,UAAM,OAAO,OAAO,cAAc,YAAY,OAAO,cAAc,WAAW,SAAS,OAAO,SAAS,CAAC,KAAK;AAC7G,WAAO,GAAG,EAAE,IAAI,KAAK,EAAE,OAAO,GAAG,IAAI;AAAA,EACvC;AACA,MAAI,OAAO,MAAM,SAAU,QAAO;AAClC,MAAI,KAAK,OAAO,MAAM,UAAU;AAG9B,QAAI;AACF,YAAM,WAAW,QAAQ,IAAI,GAAa,SAAS;AACnD,YAAM,YAAY,QAAQ,IAAI,GAAa,MAAM;AACjD,YAAM,YAAY,QAAQ,IAAI,GAAa,MAAM;AAEjD,YAAM,MAAM,OAAO,aAAa,WAAW,WAAW;AACtD,YAAM,OAAO,OAAO,cAAc,WAAW,YAAY;AACzD,YAAM,OAAO,OAAO,cAAc,YAAY,OAAO,cAAc,WAAW,OAAO,SAAS,IAAI;AAElG,UAAI,OAAO,QAAQ,MAAM;AACvB,cAAM,OAAO,OAAO,GAAG,IAAI,OAAO;AAClC,cAAM,OAAO,OAAO,SAAS,IAAI,KAAK;AACtC,eAAO,GAAG,IAAI,GAAG,OAAO,cAAc,GAAG,IAAI;AAAA,MAC/C;AAAA,IACF,QAAQ;AAAA,IAER;AAEA,QAAI;AACF,YAAM,OAAO,OAAO,KAAK,CAAC;AAC1B,YAAM,OAAO,KAAK,UAAU,CAAC;AAC7B,aAAO,KAAK,SAAS,gBAAgB,KAAK,KAAK,GAAG,CAAC,MAAM,IAAI,KAAK,WAAW,IAAI;AAAA,IACnF,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO,OAAO,CAAC;AACjB;AAEO,IAAM,uBAAuB,CAAC,QAA6C;AAChF,MAAI;AACF,UAAM,IAAI,IAAI;AACd,QAAI,CAAC,EAAG,QAAO;AAEf,UAAM,YAAY,EAAE,eAAe,KAAK;AACxC,UAAM,YAAY,EAAE,oBAAoB,KAAK;AAC7C,UAAM,WAAW,EAAE,aAAa;AAChC,UAAM,SAAS,EAAE,gBAAgB;AACjC,UAAM,SAAS,EAAE,gBAAgB;AAEjC,UAAM,QAAkB;AAAA,MACtB,aAAa,SAAS;AAAA,MACtB,aAAa,SAAS;AAAA,MACtB,YAAY,QAAQ;AAAA,MACpB,QAAQ,SAAS,OAAO,eAAe,OAAO,KAAK,KAAK;AAAA,MACxD,QAAQ,gBAAgB,OAAO,cAAc,OAAO,YAAY,KAAK;AAAA,MACrE,QAAQ,SAAS,OAAO,eAAe,OAAO,KAAK,KAAK;AAAA,MACxD,QAAQ,gBAAgB,OAAO,cAAc,OAAO,YAAY,KAAK;AAAA,IACvE,EAAE,OAAO,OAAO;AAEhB,WAAO,MAAM,SAAS,KAAK,MAAM,KAAK,GAAG,CAAC,MAAM;AAAA,EAClD,QAAQ;AACN,WAAO;AAAA,EACT;AACF;;;ACnDA,IAAM,0BAA0B,CAAC,MAAwB;AACvD,QAAM,MAAM,kBAAkB,CAAC;AAC/B,SACE,IAAI,SAAS,YAAY,KACzB,IAAI,SAAS,OAAO,KACpB,IAAI,SAAS,gBAAgB,KAC7B,IAAI,SAAS,wBAAwB,KACrC,IAAI,SAAS,SAAS;AAE1B;AAEO,IAAM,0BAA0B,OAAO,WAItB;AACtB,QAAM,QAAQ;AACd,QAAM,KAAK,OAAO;AAElB,QAAM,wBAAwB;AAAA,IAC5B;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACA,QAAM,iBACJ,OAAO,kBAAkB,OAAO,eAAe,SAAS,IACpD,OAAO,iBACP,CAAC,GAAG,qBAAqB;AAC/B,MAAI;AAEJ,QAAM,UAAU,OACd,MACA,sBACoB;AACpB,UAAM,aACJ,gFAGQ,EAAE,eACD,UAAU,IAAI,CAAC;AAI1B,WAAO,MAAM,OAAO;AAAA,MAClB;AAAA,QACE;AAAA,QACA,SAAS;AAAA,QACT;AAAA,QACA,GAAI,qBAAqB,OAAO,EAAE,kBAAkB,IAAI,CAAC;AAAA,MAC3D;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAEA,aAAW,QAAQ,gBAAgB;AACjC,QAAI;AACF,YAAM,MAAM,MAAM,QAAQ,MAAM,EAAE;AAClC,UAAI,KAAK;AACP,eAAO;AAAA,UACL,SAAS;AAAA,UACT,aAAa,OAAO,WAAW,KAAK,aAAa,KAAK,GAAG;AAAA,UACzD,SAAS,OAAO,WAAW,KAAK,SAAS,KAAK,GAAG;AAAA,QACnD;AAAA,MACF;AAAA,IACF,SAAS,GAAG;AACV,UAAI,wBAAwB,CAAC,EAAG,OAAM;AACtC,gBAAU;AAAA,IACZ;AAAA,EACF;AAEA,aAAW,QAAQ,gBAAgB;AACjC,QAAI;AACF,YAAM,MAAM,MAAM,QAAQ,MAAM,MAAS;AACzC,UAAI,KAAK;AACP,eAAO;AAAA,UACL,SAAS;AAAA,UACT,aAAa,OAAO,WAAW,KAAK,aAAa,KAAK,GAAG;AAAA,UACzD,SAAS,OAAO,WAAW,KAAK,SAAS,KAAK,GAAG;AAAA,QACnD;AAAA,MACF;AAAA,IACF,SAAS,GAAG;AACV,UAAI,wBAAwB,CAAC,EAAG,OAAM;AACtC,gBAAU;AAAA,IACZ;AAAA,EACF;AAEA,QAAM,mBAAmB,QACrB,UACA,IAAI,MAAM,OAAO,WAAW,mBAAmB,CAAC;AACtD;;;ACtFA,IAAM,mBAAmB,CAAC,UAAkD;AAC1E,QAAM,KAAK,SAAS,IAAI,KAAK;AAC7B,MAAI,CAAC,EAAG,QAAO;AACf,QAAM,IAAI,OAAO,SAAS,GAAG,EAAE;AAC/B,SAAO,OAAO,SAAS,CAAC,IAAI,IAAI;AAClC;AAEA,IAAM,uBAAuB,CAAC,UAAmD;AAC/E,QAAM,KAAK,SAAS,IAAI,KAAK;AAC7B,MAAI,MAAM,GAAI,QAAO;AACrB,QAAM,IAAI,OAAO,CAAC;AAClB,MAAI,OAAO,SAAS,CAAC,EAAG,QAAO,IAAI;AACnC,SAAO,MAAM;AACf;AAEO,IAAM,6BAA6B,CAAC,WAA6C;AACtF,QAAM,MAA8B,CAAC;AAErC,aAAW,SAAS,QAAQ;AAC1B,UAAM,cAAc,WAAW,OAAO,WAAW,KAAK,WAAW,OAAO,SAAS,KAAK,WAAW,OAAO,OAAO;AAC/G,QAAI,CAAC,YAAa;AAElB,UAAM,UAAU,OAAO,SAAS,aAAa,EAAE;AAC/C,QAAI,CAAC,OAAO,SAAS,OAAO,EAAG;AAE/B,UAAM,QAAQ,WAAW,OAAO,SAAS,KAAK,IAAI,KAAK;AACvD,UAAM,OAAO,WAAW,OAAO,KAAK,KAAK,IAAI,KAAK;AAClD,UAAM,SAAS,WAAW,OAAO,OAAO,KAAK,IAAI,KAAK;AACtD,UAAM,aAAa,MAAM,KAAK,EAAE,YAAY;AAE5C,UAAM,QAAQ,iBAAiB,WAAW,OAAO,OAAO,CAAC;AACzD,UAAM,aAAa,WAAW,OAAO,WAAW,KAAK,IAAI,KAAK,KAAK;AACnE,UAAM,kBAAkB,WAAW,OAAO,gBAAgB,KAAK,IAAI,KAAK,KAAK;AAC7E,UAAM,UAAU,qBAAqB,WAAW,OAAO,SAAS,CAAC;AACjE,UAAM,iBAAiB,qBAAqB,WAAW,OAAO,gBAAgB,CAAC;AAE/E,UAAM,cAAc,WAAW,OAAO,YAAY,KAAK,IAAI,KAAK,EAAE,YAAY,KAAK;AACnF,UAAM,qBAAqB,WAAW,OAAO,eAAe,KAAK,IAAI,KAAK,EAAE,YAAY;AACxF,UAAM,gBAAgB,oBAClB,kBACG,MAAM,GAAG,EACT,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EACnB,OAAO,OAAO,IACjB;AAEJ,UAAM,aAAa,WAAW,OAAO,OAAO,KAAK,IAAI,KAAK;AAC1D,UAAM,oBACJ,eAAe,UACf,CAAC,QACD,CAAC,OACD,CAAC,eACA,sBAAsB,UAAU,sBAAsB,QACtD,cAAc,OAAO,cAAc;AAEtC,UAAM,iBACJ,eAAe,YAAY,OAAO,eAAe,UAAU,eAAe,eAAe,QAAQ;AAEnG,UAAM,gBACJ,aACI,eAAe,YACf,eAAe,YACb,QACA,eAAe,UAAU,eAAe,eACtC,OACA;AAEV,QAAI,KAAK;AAAA,MACP;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,GAAI,UAAU,SAAY,EAAE,MAAM,IAAI,CAAC;AAAA,MACvC,GAAI,eAAe,SAAS,EAAE,cAAc,IAAI,CAAC;AAAA,MACjD,GAAI,YAAY,EAAE,UAAU,IAAI,CAAC;AAAA,MACjC,GAAI,iBAAiB,EAAE,eAAe,IAAI,CAAC;AAAA,MAC3C,GAAI,OAAO,YAAY,YAAY,EAAE,QAAQ,IAAI,CAAC;AAAA,MAClD,GAAI,OAAO,mBAAmB,YAAY,EAAE,eAAe,IAAI,CAAC;AAAA,MAChE,GAAI,aAAa,EAAE,WAAW,IAAI,CAAC;AAAA,MACnC,GAAI,mBAAmB,SAAY,EAAE,eAAe,IAAI,CAAC;AAAA,MACzD,GAAI,kBAAkB,SAAY,EAAE,cAAc,IAAI,CAAC;AAAA,MACvD;AAAA,IACF,CAAC;AAAA,EACH;AAEA,SAAO;AACT;;;ACtFA,IAAM,cAAc,CAAC,MAA2B,SAAmD;AACjG,MAAI,OAAO,SAAS,UAAW,QAAO;AACtC,MAAI,OAAO,SAAS,UAAW,QAAO;AACtC,SAAO;AACT;AAEA,IAAM,qBAAqB,CAAC,MAA0B,SAAiD;AACrG,QAAM,KAAK,QAAQ,IAAI,KAAK;AAC5B,MAAI,EAAG,QAAO;AACd,QAAM,KAAK,QAAQ,IAAI,KAAK;AAC5B,MAAI,EAAG,QAAO;AACd,SAAO;AACT;AAEA,IAAM,oBAAoB,CAAI,MAAuB,SAA2C;AAC9F,MAAI,MAAM,QAAQ,IAAI,KAAK,KAAK,SAAS,EAAG,QAAO;AACnD,MAAI,MAAM,QAAQ,IAAI,KAAK,KAAK,SAAS,EAAG,QAAO;AACnD,SAAO;AACT;AAEO,IAAM,oCAAoC,CAAC,WAImC;AACnF,QAAM,EAAE,OAAO,UAAU,MAAM,IAAI;AACnC,QAAM,SAA+B,CAAC;AAEtC,MAAI,MAAM,mBAAmB;AAC3B,UAAM,sBAAsB,UAAU,cAAc,UAAU,SAAS,IAAI,YAAY;AACvF,QAAI,uBAAuB,UAAW,QAAO,EAAE,YAAY,MAAM,OAAO;AACxE,QAAI,CAAC,SAAU,QAAO,EAAE,YAAY,MAAM,OAAO;AAAA,EACnD;AAEA,QAAM,aAAa,YAAY,MAAM,gBAAgB,UAAU,MAAM;AACrE,MAAI,OAAO,MAAM,mBAAmB,aAAa,UAAU,WAAW,MAAM,gBAAgB;AAC1F,WAAO,KAAK;AAAA,MACV,MAAM,MAAM,iBAAiB,WAAW;AAAA,MACxC,SAAS,MAAM;AAAA,MACf,WAAW;AAAA,IACb,CAAC;AAAA,EACH;AAEA,QAAM,eAAe,YAAY,MAAM,eAAe,UAAU,QAAQ;AACxE,MAAI,OAAO,MAAM,kBAAkB,aAAa,UAAU,aAAa,MAAM,eAAe;AAC1F,WAAO,KAAK;AAAA,MACV,MAAM,MAAM,gBAAgB,aAAa;AAAA,MACzC,SAAS,MAAM;AAAA,MACf,WAAW;AAAA,IACb,CAAC;AAAA,EACH;AAEA,QAAM,OAAO,MAAM,QAAQ,UAAU,QAAQ;AAC7C,QAAM,MAAM,MAAM,OAAO,UAAU,OAAO;AAC1C,QAAM,QAAQ,MAAM,SAAS,UAAU,SAAS;AAEhD,QAAM,gBAAgB,kBAAkB,MAAM,eAAe,UAAU,aAAa;AACpF,QAAM,YAAY,mBAAmB,MAAM,WAAW,UAAU,SAAS;AACzE,QAAM,iBAAiB,mBAAmB,MAAM,gBAAgB,UAAU,cAAc;AACxF,QAAM,aAAa,mBAAmB,MAAM,YAAY,UAAU,UAAU;AAC5E,QAAM,aAAa,mBAAmB,MAAM,YAAY,UAAU,UAAU;AAE5E,QAAM,OAAwB;AAAA,IAC5B;AAAA,IACA;AAAA,IACA;AAAA,IACA,aAAa;AAAA,IACb,GAAI,OAAO,MAAM,UAAU,WACvB,EAAE,OAAO,MAAM,MAAM,IACrB,UAAU,UAAU,SAClB,EAAE,OAAO,SAAS,MAAM,IACxB,CAAC;AAAA,IACP,GAAI,iBAAiB,OAAO,EAAE,cAAc,IAAI,CAAC;AAAA,IACjD,GAAI,aAAa,OAAO,EAAE,UAAU,IAAI,CAAC;AAAA,IACzC,GAAI,kBAAkB,OAAO,EAAE,eAAe,IAAI,CAAC;AAAA,IACnD,GAAI,OAAO,MAAM,YAAY,YACzB,EAAE,SAAS,MAAM,QAAQ,IACzB,UAAU,YAAY,SACpB,EAAE,SAAS,SAAS,QAAQ,IAC5B,CAAC;AAAA,IACP,GAAI,OAAO,MAAM,mBAAmB,YAChC,EAAE,gBAAgB,MAAM,eAAe,IACvC,UAAU,mBAAmB,SAC3B,EAAE,gBAAgB,SAAS,eAAe,IAC1C,CAAC;AAAA,IACP,GAAI,cAAc,OAAO,EAAE,WAAW,IAAI,CAAC;AAAA,IAC3C,GAAI,eAAe,SAAY,EAAE,QAAQ,WAAW,IAAI,CAAC;AAAA,IACzD,GAAI,iBAAiB,SAAY,EAAE,UAAU,aAAa,IAAI,CAAC;AAAA,IAC/D,GAAI,cAAc,OAAO,EAAE,WAAW,IAAI,CAAC;AAAA,EAC7C;AAEA,SAAO,EAAE,YAAY,OAAO,MAAM,OAAO;AAC3C;AAEO,IAAM,kCAAkC,CAAC,oBAG3C;AACH,QAAM,YAA0E,CAAC;AACjF,aAAW,CAAC,SAAS,IAAI,KAAK,gBAAgB,QAAQ,GAAG;AACvD,SAAK,KAAK,cAAc,KAAK,OAAO,YAAY,MAAM,OAAQ;AAC9D,cAAU,OAAO,IAAI,EAAE,MAAM,KAAK,MAAM,KAAK,KAAK,KAAK,OAAO,KAAK,MAAM;AAAA,EAC3E;AACA,SAAO,EAAE,QAAQ,WAAW,gBAAgB,OAAO,KAAK,SAAS,EAAE;AACrE;;;AC1HO,IAAM,mBAAmB,CAAC,UAAmD;AAClF,QAAM,YAAY,MAAM,aAAa,KAAK,IAAI;AAE9C,MAAI,MAAM,SAAS,UAAU;AAC3B,WAAO,EAAE,MAAM,UAAU,SAAS,MAAM,SAAS,UAAU;AAAA,EAC7D;AAEA,MAAI,MAAM,SAAS,WAAW;AAC5B,WAAO,EAAE,MAAM,YAAY,SAAS,MAAM,SAAS,UAAU;AAAA,EAC/D;AAEA,MAAI,MAAM,SAAS,YAAY;AAC7B,WAAO,EAAE,MAAM,YAAY,SAAS,MAAM,SAAS,UAAU;AAAA,EAC/D;AAEA,MAAI,MAAM,SAAS,MAAM;AACvB,UAAM,SAAS,MAAM,IAAI;AAEzB,UAAM,MAAoE;AAAA,MACxE,QAAQ;AAAA,MACR,SAAS;AAAA,MACT,SAAS;AAAA,MACT,MAAM;AAAA,MACN,SAAS;AAAA,MACT,OAAO;AAAA,IACT;AAEA,WAAO;AAAA,MACL,MAAM,SAAS,IAAI,MAAM,IAAI;AAAA,MAC7B,SAAS,MAAM;AAAA,MACf;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;;;ACrCO,SAAS,YAAY,GAA2C;AACrE,MAAI,KAAK,KAAM,QAAO;AACtB,QAAM,IAAI,OAAO,CAAC;AAClB,SAAO,OAAO,SAAS,CAAC,IAAI,IAAI;AAClC;AAEO,SAAS,eAAe,GAA4C;AACzE,MAAI,KAAK,KAAM,QAAO;AACtB,QAAM,IAAI,EAAE,KAAK;AACjB,MAAI,MAAM,OAAO,EAAE,YAAY,MAAM,OAAQ,QAAO;AACpD,MAAI,MAAM,OAAO,EAAE,YAAY,MAAM,QAAS,QAAO;AACrD,SAAO;AACT;;;ACVO,IAAM,qBAAqB,CAChC,WACA,YACA,WACA,YAAY,OACD;AACX,QAAM,IAAI,OAAO,SAAS;AAC1B,MAAI,cAAc,UAAa,cAAc,KAAM,QAAO;AAC1D,MAAI,CAAC,OAAO,SAAS,CAAC,KAAK,IAAI,EAAG,QAAO;AAGzC,MAAI,IAAI,EAAG,QAAO,KAAK,MAAM,CAAC;AAG9B,QAAM,OAAO,KAAK,IAAI,KAAK,IAAI,GAAG,KAAK,MAAM,SAAS,CAAC,GAAG,KAAK,IAAI,GAAG,KAAK,MAAM,UAAU,CAAC,CAAC;AAC7F,SAAO,KAAK,IAAI,GAAG,KAAK,MAAM,OAAO,CAAC,CAAC;AACzC;AAEO,IAAM,8BAA8B,CAAC,WAOP;AACnC,QAAM,OAAO,KAAK,IAAI,GAAG,KAAK,MAAM,OAAO,QAAQ,CAAC;AACpD,QAAM,OAAO,KAAK,IAAI,GAAG,KAAK,MAAM,OAAO,SAAS,CAAC;AACrD,QAAM,SAAS,KAAK,IAAI,GAAG,KAAK,MAAM,OAAO,MAAM,CAAC;AACpD,QAAM,QAAQ,KAAK,IAAI,GAAG,KAAK,MAAM,OAAO,SAAS,CAAC;AACtD,QAAM,QAAQ,KAAK,IAAI,GAAG,KAAK,MAAM,OAAO,UAAU,CAAC;AAEvD,QAAM,QAAQ,CAAC,GAAW,KAAa,QAAgB,KAAK,IAAI,KAAK,IAAI,GAAG,GAAG,GAAG,GAAG;AACrF,QAAM,OAAO,KAAK,IAAI,GAAG,QAAQ,IAAI;AACrC,QAAM,OAAO,KAAK,IAAI,GAAG,QAAQ,IAAI;AAErC,MAAI,OAAO;AACX,MAAI,MAAM;AAEV,UAAQ,OAAO,UAAU;AAAA,IACvB,KAAK;AACH,aAAO;AACP,YAAM;AACN;AAAA,IACF,KAAK;AACH,aAAO,QAAQ,OAAO;AACtB,YAAM;AACN;AAAA,IACF,KAAK;AACH,aAAO;AACP,YAAM,QAAQ,OAAO;AACrB;AAAA,IACF,KAAK;AACH,aAAO,QAAQ,OAAO;AACtB,YAAM,QAAQ,OAAO;AACrB;AAAA,IACF,KAAK;AACH,aAAO,KAAK,OAAO,QAAQ,QAAQ,CAAC;AACpC,YAAM,KAAK,OAAO,QAAQ,QAAQ,CAAC;AACnC;AAAA,IACF,KAAK;AACH,aAAO,KAAK,OAAO,QAAQ,QAAQ,CAAC;AACpC,YAAM;AACN;AAAA,IACF,KAAK;AACH,aAAO,KAAK,OAAO,QAAQ,QAAQ,CAAC;AACpC,YAAM,QAAQ,OAAO;AACrB;AAAA,IACF,KAAK;AACH,aAAO;AACP,YAAM,KAAK,OAAO,QAAQ,QAAQ,CAAC;AACnC;AAAA,IACF,KAAK;AACH,aAAO,QAAQ,OAAO;AACtB,YAAM,KAAK,OAAO,QAAQ,QAAQ,CAAC;AACnC;AAAA,EACJ;AAEA,SAAO;AAAA,IACL,MAAM,MAAM,MAAM,GAAG,IAAI;AAAA,IACzB,KAAK,MAAM,KAAK,GAAG,IAAI;AAAA,EACzB;AACF;;;AC9EO,IAAM,sBAAsB,CAAC,YAAsC;AACxE,MAAI,QAAQ,WAAW,OAAQ,QAAO;AAEtC,QAAM,SAAwD;AAAA,IAC5D,MAAM;AAAA,IACN,OAAO;AAAA,IACP,IAAI;AAAA,IACJ,MAAM;AAAA,EACR;AAEA,QAAM,SAAS,OAAO,QAAQ,OAAO;AACrC,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI,MAAM,mDAAmD,QAAQ,OAAO,EAAE;AAAA,EACtF;AAEA,SAAO;AACT;AAEO,IAAM,kBAAkB,CAAC,WAAyB,aAAyC;AAChG,MAAI,cAAc,OAAQ,QAAO;AAEjC,MAAI,aAAa,OAAW,QAAO;AAEnC,MAAI,WAAW,KAAK,YAAY,GAAG;AACjC,WAAO,KAAK,IAAI,GAAG,WAAW,EAAE;AAAA,EAClC;AAEA,SAAO;AACT;AAEO,IAAM,2BAA2B,CAAC,WAI3B;AACZ,MAAI,OAAO,MAAM,KAAK,WAAW,EAAG,QAAO;AAE3C,MAAI;AACF,UAAM,gBAAiB,OAAO,OAAiF;AAC/G,QAAI,CAAC,cAAe,QAAO;AAE3B,UAAM,WAAW,cAAc,KAAK,OAAO,QAAQ,OAAO,MAAM,MAAM,OAAO,MAAM,OAAO,WAAY,OAAO,OAAe,GAAG;AAC/H,QAAI,CAAC,SAAU,QAAO;AAEtB,UAAM,WAAW,OAAO,YAAY;AACpC,WAAO,qBAAqB,SAAS,UAAU,GAAG,QAAQ,CAAC;AAAA,EAC7D,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,IAAM,+BAA+B,CAAC,WAIO;AAClD,SAAO;AAAA,IACL;AAAA,MACE,OAAO;AAAA,MACP,YAAY,oBAAoB,OAAO,WAAW,OAAO,UAAU,QAAQ;AAAA,IAC7E;AAAA,IACA;AAAA,MACE,OAAO;AAAA,MACP,YAAY,oBAAoB,OAAO,WAAW,OAAO,UAAU,UAAU,EAAE,QAAQ,EAAE,CAAC;AAAA,IAC5F;AAAA,IACA;AAAA,MACE,OAAO;AAAA,MACP,YAAY,oBAAoB,OAAO,WAAW,OAAO,UAAU,UAAU,EAAE,MAAM,GAAG,CAAC;AAAA,IAC3F;AAAA,IACA;AAAA,MACE,OAAO;AAAA,MACP,YAAY,oBAAoB,OAAO,WAAW,OAAO,UAAU,UAAU,EAAE,MAAM,IAAI,QAAQ,EAAE,CAAC;AAAA,IACtG;AAAA,IACA,GAAI,OAAO,cACP;AAAA,MACE;AAAA,QACE,OAAO;AAAA,QACP,YAAY,oBAAoB,OAAO,WAAW,OAAO,UAAU,UAAU;AAAA,UAC3E,MAAM,OAAO;AAAA,UACb,QAAQ;AAAA,QACV,CAAC;AAAA,MACH;AAAA,IACF,IACA,CAAC;AAAA,IACL;AAAA,MACE,OAAO;AAAA,MACP,YAAY,oBAAoB,OAAO,WAAW,OAAO,UAAU,UAAU;AAAA,QAC3E,MAAM,UAAU,OAAO,QAAQ;AAAA,QAC/B,QAAQ;AAAA,MACV,CAAC;AAAA,IACH;AAAA,EACF;AACF;AAEO,IAAM,6BAA6B,CAAC,WAAsE;AAC/G,MAAI,CAAC,OAAQ,QAAO;AAEpB,QAAM,YAAY,OAAO,SAAS,UAAa,OAAO,OAAO,IAAI,EAAE,KAAK,MAAM;AAC9E,QAAM,WAAW,OAAO,WAAW,UAAa,OAAO,OAAO,MAAM,EAAE,KAAK,MAAM;AAEjF,SAAO,aAAa;AACtB;AAEO,IAAM,6BAA6B,OAAO,WAI0B;AACzE,MAAI;AACJ,MAAI,SAAS;AACb,MAAI,WAAW;AAEf,aAAW,KAAK,OAAO,UAAU;AAC/B,QAAI;AACF,YAAM,MAAM,MAAM,OAAO,KAAK,EAAE,YAAY,EAAE,KAAK;AAEnD,UAAI,IAAI,iBAAiB,KAAK;AAC5B,cAAM,IAAI,MAAM,6CAA6C,IAAI,YAAY,GAAG;AAAA,MAClF;AAEA,eAAS;AAET,UAAI,OAAO,QAAQ;AACjB,YAAI;AACF,cAAI,MAAM,OAAO,OAAO,GAAG;AACzB,uBAAW;AACX,mBAAO,EAAE,QAAQ,UAAU,UAAU;AAAA,UACvC;AAAA,QACF,SAAS,GAAG;AAEV,sBAAY;AAAA,QACd;AAAA,MACF;AAAA,IACF,SAAS,GAAG;AACV,kBAAY;AAAA,IACd;AAAA,EACF;AAEA,SAAO,EAAE,QAAQ,UAAU,UAAU;AACvC;;;ACvIA,IAAM,wBAAwB,CAC5B,QACA,eAC8B;AAC9B,QAAM,UAAqC,CAAC;AAC5C,QAAM,QAAQ,QAAQ;AAGtB,MAAI,OAAO;AACT,QAAI,MAAM,SAAU,SAAQ,KAAK,QAAQ;AACzC,QAAI,MAAM,UAAW,SAAQ,KAAK,SAAS;AAC3C,QAAI,MAAM,SAAU,SAAQ,KAAK,QAAQ;AACzC,QAAI,MAAM,OAAQ,SAAQ,KAAK,MAAM;AACrC,QAAI,MAAM,OAAQ,SAAQ,KAAK,QAAQ;AACvC,QAAI,MAAM,SAAU,SAAQ,KAAK,UAAU;AAC3C,QAAI,MAAM,SAAU,SAAQ,KAAK,UAAU;AAC3C,QAAI,MAAM,QAAS,SAAQ,KAAK,SAAS;AACzC,QAAI,MAAM,GAAI,SAAQ,KAAK,IAAI;AAC/B,QAAI,MAAM,QAAS,SAAQ,KAAK,OAAO;AAAA,EACzC;AAGA,MAAI,QAAQ,WAAW,KAAK,YAAY;AACtC,UAAM,KAAK,WAAW,YAAY;AAClC,QAAI,GAAG,SAAS,QAAQ,KAAK,GAAG,SAAS,QAAQ,EAAG,SAAQ,KAAK,QAAQ;AACzE,QAAI,GAAG,SAAS,SAAS,EAAG,SAAQ,KAAK,SAAS;AAClD,QAAI,GAAG,SAAS,SAAS,KAAK,GAAG,SAAS,QAAQ,EAAG,SAAQ,KAAK,QAAQ;AAC1E,QAAI,GAAG,SAAS,MAAM,EAAG,SAAQ,KAAK,MAAM;AAC5C,QAAI,GAAG,SAAS,IAAI,KAAK,GAAG,SAAS,QAAQ,EAAG,SAAQ,KAAK,QAAQ;AACrE,QAAI,GAAG,SAAS,OAAO,EAAG,SAAQ,KAAK,UAAU;AACjD,QAAI,GAAG,SAAS,SAAS,KAAK,GAAG,SAAS,UAAU;AAClD,cAAQ,KAAK,UAAU;AACzB,QAAI,GAAG,SAAS,SAAS,EAAG,SAAQ,KAAK,SAAS;AAClD,QAAI,GAAG,SAAS,IAAI,EAAG,SAAQ,KAAK,IAAI;AAAA,EAC1C;AAGA,MAAI,QAAQ,WAAW,GAAG;AACxB,YAAQ,KAAK,QAAQ;AAAA,EACvB;AAEA,SAAO;AACT;AAEO,IAAM,cAAc,CACzB,KACA,SAC+B;AAC/B,QAAM,MAAkC,CAAC;AACzC,aAAW,OAAO,MAAM;AACtB,UAAM,IAAI,WAAW,KAAK,GAAG;AAC7B,QAAI,MAAM,OAAW,KAAI,GAAG,IAAI;AAAA,EAClC;AACA,SAAO;AACT;AAEO,IAAM,eAAe,CAAC,KAAa,YAA8B;AACtE,QAAM,KAAK,IAAI,OAAO,IAAI,OAAO,4BAA4B,OAAO,KAAK,GAAG;AAC5E,QAAM,MAAgB,CAAC;AACvB,MAAI;AAEJ,SAAQ,QAAQ,GAAG,KAAK,GAAG,GAAI;AAC7B,QAAI,KAAK,MAAM,CAAC,KAAK,EAAE;AAAA,EACzB;AACA,SAAO;AACT;AAEO,IAAM,wBAAwB,CAAC,UAAoC;AACxE,QAAM,OAAO,OAAO,SAAS,WAAW,OAAO,MAAM,KAAK,IAAI,EAAE;AAChE,QAAM,QAAQ,OAAO,SAAS,WAAW,OAAO,OAAO,KAAK,IAAI,EAAE;AAClE,QAAM,MAAM,OAAO,SAAS,WAAW,OAAO,KAAK,KAAK,IAAI,EAAE;AAC9D,QAAM,OAAO,OAAO,SAAS,WAAW,OAAO,MAAM,KAAK,IAAI,EAAE;AAChE,QAAM,SAAS,OAAO,SAAS,WAAW,OAAO,QAAQ,KAAK,IAAI,EAAE;AACpE,QAAM,SAAS,OAAO,SAAS,WAAW,OAAO,QAAQ,KAAK,IAAI,EAAE;AAEpE,MAAI,CAAC,MAAM,OAAO,KAAK,MAAM,QAAQ,MAAM,EAAE,MAAM,OAAO,QAAQ,GAAG;AAKnE,WAAO,IAAI,KAAK,MAAM,QAAQ,GAAG,KAAK,MAAM,QAAQ,MAAM;AAAA,EAC5D;AAGA,QAAM,OAAO,MAAM,QAAQ,YAAY,EAAE,EAAE,KAAK;AAChD,QAAM,IAAI,KAAK;AAAA,IACb;AAAA,EACF;AACA,MAAI,CAAC,EAAG,QAAO;AACf,QAAM,IAAI,OAAO,SAAS,EAAE,CAAC,KAAK,IAAI,EAAE;AACxC,QAAM,KAAK,OAAO,SAAS,EAAE,CAAC,KAAK,IAAI,EAAE;AACzC,QAAM,KAAK,OAAO,SAAS,EAAE,CAAC,KAAK,IAAI,EAAE;AACzC,QAAM,KAAK,OAAO,SAAS,EAAE,CAAC,KAAK,KAAK,EAAE;AAC1C,QAAM,KAAK,OAAO,SAAS,EAAE,CAAC,KAAK,KAAK,EAAE;AAC1C,QAAM,KAAK,OAAO,SAAS,EAAE,CAAC,KAAK,KAAK,EAAE;AAC1C,MAAI,CAAC,CAAC,GAAG,IAAI,IAAI,IAAI,IAAI,EAAE,EAAE,MAAM,OAAO,QAAQ,EAAG,QAAO;AAC5D,SAAO,IAAI,KAAK,GAAG,KAAK,GAAG,IAAI,IAAI,IAAI,EAAE;AAC3C;AAEO,IAAM,6BAA6B,CAAC,QAAiC;AAC1E,QAAM,MAAuB,CAAC;AAG9B,QAAM,iBAAiB,aAAa,KAAK,UAAU;AACnD,aAAW,KAAK,gBAAgB;AAC9B,UAAM,KACJ,WAAW,GAAG,IAAI,KAAK,WAAW,GAAG,IAAI,KAAK,WAAW,GAAG,IAAI;AAClE,UAAM,OAAO,WAAW,GAAG,MAAM,KAAK,WAAW,GAAG,UAAU;AAC9D,UAAM,UAAU,MAAM,OAAO,KAAK;AAClC,QAAI,CAAC,OAAQ;AAEb,UAAM,OAAsB,EAAE,UAAU,OAAO;AAC/C,QAAI,QAAQ,QAAQ,KAAK,KAAK,EAAG,MAAK,OAAO,KAAK,KAAK;AACvD,QAAI,MAAM,QAAQ,GAAG,KAAK,EAAG,MAAK,KAAK,GAAG,KAAK;AAE/C,UAAM,aACJ,WAAW,GAAG,MAAM,KACpB,WAAW,GAAG,YAAY,KAC1B,WAAW,GAAG,WAAW;AAC3B,QAAI,cAAc,KAAM,MAAK,aAAa;AAE1C,UAAM,WAAW,WAAW,GAAG,MAAM,KAAK,WAAW,GAAG,UAAU;AAClE,UAAM,YAAY,WAAW,OAAO,SAAS,UAAU,EAAE,IAAI;AAC7D,QAAI,aAAa,QAAQ,OAAO,SAAS,SAAS;AAChD,WAAK,YAAY;AAEnB,UAAM,QAAQ,aAAa,GAAG,WAAW,EAAE,CAAC;AAC5C,UAAM,MAAM,aAAa,GAAG,SAAS,EAAE,CAAC;AACxC,UAAM,UAAU,QAAQ,sBAAsB,KAAK,IAAI;AACvD,UAAM,QAAQ,MAAM,sBAAsB,GAAG,IAAI;AACjD,QAAI,QAAS,MAAK,YAAY;AAC9B,QAAI,MAAO,MAAK,UAAU;AAE1B,UAAM,SAAS,uBAAuB,KAAK,QAAQ,KAAK,QAAQ;AAChE,QAAI,QAAQ;AACV,WAAK,iBAAiB;AACtB,UAAI,CAAC,KAAK,UAAW,MAAK,YAAY,OAAO;AAC7C,UAAI,CAAC,KAAK,QAAS,MAAK,UAAU,OAAO;AAAA,IAC3C;AAEA,SAAK,mBAAmB,sBAAsB,QAAQ,KAAK,UAAU;AAErE,QAAI,KAAK,IAAI;AAAA,EACf;AAGA,QAAM,aAAa,aAAa,KAAK,MAAM;AAC3C,aAAW,KAAK,YAAY;AAC1B,UAAM,YACJ,WAAW,GAAG,UAAU,KAAK,WAAW,GAAG,MAAM,IAChD,KAAK;AACR,QAAI,CAAC,SAAU;AAEf,UAAM,WAAW,WAAW,GAAG,MAAM,KAAK,WAAW,GAAG,UAAU;AAClE,UAAM,YAAY,WAAW,OAAO,SAAS,UAAU,EAAE,IAAI;AAC7D,UAAM,aACJ,WAAW,GAAG,MAAM,KACpB,WAAW,GAAG,YAAY,KAC1B,WAAW,GAAG,WAAW;AAE3B,UAAM,QAAQ,aAAa,GAAG,WAAW,EAAE,CAAC;AAC5C,UAAM,MAAM,aAAa,GAAG,SAAS,EAAE,CAAC;AAExC,UAAM,OAAsB,EAAE,SAAS;AACvC,QAAI,aAAa,QAAQ,OAAO,SAAS,SAAS;AAChD,WAAK,YAAY;AACnB,QAAI,cAAc,KAAM,MAAK,aAAa;AAE1C,UAAM,UAAU,QAAQ,sBAAsB,KAAK,IAAI;AACvD,UAAM,QAAQ,MAAM,sBAAsB,GAAG,IAAI;AACjD,QAAI,QAAS,MAAK,YAAY;AAC9B,QAAI,MAAO,MAAK,UAAU;AAE1B,UAAM,SAAS,uBAAuB,KAAK,QAAQ;AACnD,QAAI,QAAQ;AACV,WAAK,iBAAiB;AACtB,UAAI,CAAC,KAAK,UAAW,MAAK,YAAY,OAAO;AAC7C,UAAI,CAAC,KAAK,QAAS,MAAK,UAAU,OAAO;AAAA,IAC3C;AAEA,SAAK,mBAAmB,sBAAsB,QAAQ,KAAK,UAAU;AAErE,QAAI,KAAK,IAAI;AAAA,EACf;AAGA,MAAI,IAAI,WAAW,GAAG;AACpB,UAAM,KAAK;AACX,UAAM,YAAY,oBAAI,IAAY;AAClC,QAAI;AAEJ,WAAQ,QAAQ,GAAG,KAAK,GAAG,GAAI;AAC7B,YAAM,YAAY,MAAM,CAAC,KAAK,IAAI,KAAK;AACvC,UAAI,CAAC,SAAU;AACf,UAAI,UAAU,IAAI,QAAQ,EAAG;AAC7B,gBAAU,IAAI,QAAQ;AAEtB,YAAM,OAAsB,EAAE,SAAS;AACvC,YAAM,SAAS,uBAAuB,QAAQ;AAC9C,UAAI,QAAQ;AACV,aAAK,iBAAiB;AACtB,aAAK,YAAY,OAAO;AACxB,aAAK,UAAU,OAAO;AAAA,MACxB;AACA,WAAK,mBAAmB,sBAAsB,QAAQ,MAAS;AAC/D,UAAI,KAAK,IAAI;AAAA,IACf;AAAA,EACF;AAGA,QAAM,cAAc,aAAa,KAAK,YAAY;AAClD,MAAI,YAAY,SAAS,GAAG;AAC1B,UAAM,SAAS,oBAAI,IAA2B;AAC9C,eAAW,YAAY,KAAK;AAC1B,YAAM,MAAM,SAAS,SAAS,KAAK;AACnC,UAAI,CAAC,IAAK;AACV,UAAI,CAAC,OAAO,IAAI,GAAG,EAAG,QAAO,IAAI,KAAK,QAAQ;AAAA,IAChD;AAEA,eAAW,KAAK,aAAa;AAC3B,YAAM,cAAc,WAAW,GAAG,UAAU,KAAK,WAAW,GAAG,MAAM;AACrE,YAAM,WAAW,aAAa,KAAK;AACnC,UAAI,CAAC,SAAU;AAEf,YAAM,cACJ,WAAW,GAAG,QAAQ,KACtB,WAAW,GAAG,WAAW,KACzB,WAAW,GAAG,aAAa,KAC3B,WAAW,GAAG,SAAS,KACvB,WAAW,GAAG,YAAY,KAC1B,WAAW,GAAG,IAAI,KAClB,WAAW,GAAG,IAAI,KAClB,WAAW,GAAG,IAAI;AACpB,YAAM,WAAW,eAAe,IAAI,KAAK;AAEzC,YAAM,YAAY,WAAW,GAAG,WAAW,GAAG,KAAK;AACnD,YAAM,QAAQ,aAAa,GAAG,WAAW,EAAE,CAAC;AAC5C,YAAM,MAAM,aAAa,GAAG,SAAS,EAAE,CAAC;AACxC,YAAM,UAAU,QAAQ,sBAAsB,KAAK,IAAI;AACvD,YAAM,QAAQ,MAAM,sBAAsB,GAAG,IAAI;AAEjD,YAAM,SAAS,OAAO,IAAI,QAAQ,KAAK,EAAE,SAAS;AAClD,UAAI,WAAW,CAAC,OAAO,GAAI,QAAO,KAAK;AACvC,UAAI,UAAW,QAAO,aAAa;AACnC,UAAI,QAAS,QAAO,YAAY;AAChC,UAAI,MAAO,QAAO,UAAU;AAE5B,UAAI,CAAC,OAAO,gBAAgB;AAC1B,cAAM,SAAS,uBAAuB,OAAO,QAAQ;AACrD,YAAI,QAAQ;AACV,iBAAO,iBAAiB;AACxB,cAAI,CAAC,OAAO,UAAW,QAAO,YAAY,OAAO;AACjD,cAAI,CAAC,OAAO,QAAS,QAAO,UAAU,OAAO;AAAA,QAC/C;AAAA,MACF;AAEA,UAAI,CAAC,OAAO,IAAI,QAAQ,GAAG;AACzB,YAAI,KAAK,MAAM;AACf,eAAO,IAAI,UAAU,MAAM;AAAA,MAC7B;AAAA,IACF;AAAA,EACF;AAGA,QAAM,OAAO,oBAAI,IAAY;AAC7B,SAAO,IAAI,OAAO,CAAC,MAAM;AACvB,UAAM,MAAM,EAAE,SAAS,KAAK;AAC5B,QAAI,KAAK,IAAI,GAAG,EAAG,QAAO;AAC1B,SAAK,IAAI,GAAG;AACZ,WAAO;AAAA,EACT,CAAC;AACH;AAEA,SAAS,qBAAqB,OAAuC;AACnE,QAAM,YAAY,WAAW,OAAO,WAAW;AAC/C,QAAM,aAAa,OAAO,SAAS,WAAW,OAAO,YAAY,KAAK,IAAI,EAAE;AAC5E,QAAM,kBAAkB,OAAO;AAAA,IAC7B,WAAW,OAAO,iBAAiB,KAAK;AAAA,IACxC;AAAA,EACF;AACA,QAAM,mBAAmB,OAAO;AAAA,IAC9B,WAAW,OAAO,kBAAkB,KAAK;AAAA,IACzC;AAAA,EACF;AACA,QAAM,aAAa,WAAW,OAAO,YAAY;AACjD,QAAM,eAAe,WAAW,OAAO,UAAU;AAEjD,MACE,CAAC,aACD,CAAC,OAAO,SAAS,UAAU,KAC3B,CAAC,OAAO,SAAS,eAAe,KAChC,CAAC,OAAO,SAAS,gBAAgB,KACjC,CAAC,YACD;AACA,WAAO;AAAA,EACT;AAEA,QAAM,SAA0B;AAAA,IAC9B;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAEA,MAAI,iBAAiB,QAAW;AAC9B,UAAM,KAAK,OAAO,SAAS,cAAc,EAAE;AAC3C,QAAI,OAAO,SAAS,EAAE,EAAG,QAAO,WAAW;AAAA,EAC7C;AAEA,SAAO;AACT;AAEO,IAAM,sBAAsB,CAAC,QAA6B;AAC/D,QAAM,mBAAmB,aAAa,KAAK,aAAa,EAAE,CAAC;AAC3D,MAAI,CAAC,kBAAkB;AACrB,UAAM,IAAI,MAAM,uCAAuC;AAAA,EACzD;AAEA,QAAM,mBAAmB,aAAa,kBAAkB,YAAY;AACpE,QAAM,aAAa,iBAChB,IAAI,CAAC,MAAM,WAAW,GAAG,QAAQ,CAAC,EAClC,OAAO,CAAC,MAAmB,QAAQ,CAAC,CAAC;AAExC,QAAM,4BAA4B;AAAA,IAChC;AAAA,IACA;AAAA,EACF;AACA,QAAM,sBAAsB,0BACzB,IAAI,CAAC,MAAM,WAAW,GAAG,iBAAiB,CAAC,EAC3C,OAAO,CAAC,MAAmB,QAAQ,CAAC,CAAC;AAExC,QAAM,oBAAoB,aAAa,kBAAkB,aAAa;AACtE,QAAM,kBAAkB,kBACrB,IAAI,oBAAoB,EACxB,OAAO,CAAC,MAA4B,QAAQ,CAAC,CAAC;AAEjD,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;;;ACnVO,IAAM,yBAAyB,CAAC,QAAwC;AAC7E,QAAM,YAAY,YAAY,WAAW,KAAK,WAAW,CAAC;AAC1D,QAAM,SAAS,YAAY,WAAW,KAAK,QAAQ,CAAC;AACpD,QAAM,WAAW,YAAY,WAAW,KAAK,UAAU,CAAC;AACxD,QAAM,aAAa,YAAY,WAAW,KAAK,YAAY,CAAC;AAC5D,QAAM,MAAM,YAAY,WAAW,KAAK,KAAK,CAAC;AAC9C,QAAM,UAAU,YAAY,WAAW,KAAK,SAAS,CAAC;AACtD,QAAM,kBAAkB,YAAY,WAAW,KAAK,iBAAiB,CAAC;AACtE,QAAM,eAAe,WAAW,KAAK,cAAc,GAAG,KAAK;AAE3D,SAAO;AAAA,IACL,GAAI,aAAa,OAAO,EAAE,UAAU,IAAI,CAAC;AAAA,IACzC,GAAI,UAAU,OAAO,EAAE,OAAO,IAAI,CAAC;AAAA,IACnC,GAAI,YAAY,OAAO,EAAE,SAAS,IAAI,CAAC;AAAA,IACvC,GAAI,cAAc,OAAO,EAAE,WAAW,IAAI,CAAC;AAAA,IAC3C,GAAI,OAAO,OAAO,EAAE,IAAI,IAAI,CAAC;AAAA,IAC7B,GAAI,WAAW,OAAO,EAAE,QAAQ,IAAI,CAAC;AAAA,IACrC,GAAI,mBAAmB,OAAO,EAAE,gBAAgB,IAAI,CAAC;AAAA,IACrD,GAAI,eAAe,EAAE,aAAa,IAAI,CAAC;AAAA,EACzC;AACF;AAEO,IAAM,qBAAqB,CAAC,QAAoC;AACrE,QAAM,YAAY,YAAY,WAAW,KAAK,WAAW,CAAC;AAC1D,QAAM,WAAW,YAAY,WAAW,KAAK,UAAU,CAAC;AACxD,QAAM,UAAU,WAAW,KAAK,SAAS,GAAG,KAAK;AACjD,QAAM,UAAU,WAAW,KAAK,SAAS,GAAG,KAAK;AACjD,QAAM,SAAS,WAAW,KAAK,QAAQ,GAAG,KAAK;AAC/C,QAAM,cAAc,WAAW,KAAK,aAAa,GAAG,KAAK;AACzD,QAAM,kBAAkB,WAAW,KAAK,iBAAiB,GAAG,KAAK;AACjE,QAAM,iBAAiB,YAAY,WAAW,KAAK,gBAAgB,CAAC;AAEpE,SAAO;AAAA,IACL,GAAI,aAAa,OAAO,EAAE,UAAU,IAAI,CAAC;AAAA,IACzC,GAAI,YAAY,OAAO,EAAE,SAAS,IAAI,CAAC;AAAA,IACvC,GAAI,UAAU,EAAE,QAAQ,IAAI,CAAC;AAAA,IAC7B,GAAI,UAAU,EAAE,QAAQ,IAAI,CAAC;AAAA,IAC7B,GAAI,SAAS,EAAE,OAAO,IAAI,CAAC;AAAA,IAC3B,GAAI,cAAc,EAAE,YAAY,IAAI,CAAC;AAAA,IACrC,GAAI,kBAAkB,EAAE,gBAAgB,IAAI,CAAC;AAAA,IAC7C,GAAI,kBAAkB,OAAO,EAAE,eAAe,IAAI,CAAC;AAAA,EACrD;AACF;AAEO,IAAM,sBAAsB,CAAC,QAAqC;AACvE,QAAM,UAAU,WAAW,KAAK,UAAU,GAAG,KAAK;AAClD,QAAM,SAAS,YAAY,WAAW,KAAK,QAAQ,CAAC;AACpD,SAAO;AAAA,IACL,GAAI,UAAU,EAAE,QAAQ,IAAI,CAAC;AAAA,IAC7B,GAAI,UAAU,OAAO,EAAE,OAAO,IAAI,CAAC;AAAA,EACrC;AACF;AAEO,IAAM,2BAA2B,CACtC,QAC6B;AAC7B,QAAM,kBAAkB,aAAa,KAAK,YAAY,EAAE,CAAC;AAGzD,QAAM,cAAc,kBAChB,aAAa,iBAAiB,MAAM,EAAE,SACtC;AAEJ,QAAM,gBAAgB,YAAY,WAAW,KAAK,eAAe,CAAC;AAClE,QAAM,UAAU,YAAY,WAAW,KAAK,SAAS,CAAC;AACtD,QAAM,aAAa,kBACf,eAAe,eAAe,IAC9B;AAEJ,SAAO;AAAA,IACL,GAAI,iBAAiB,OAAO,EAAE,cAAc,IAAI,CAAC;AAAA,IACjD,GAAI,WAAW,OAAO,EAAE,QAAQ,IAAI,CAAC;AAAA,IACrC,GAAI,OAAO,SAAS,WAAW,IAAI,EAAE,YAAY,IAAI,CAAC;AAAA,IACtD,GAAI,cAAc,OAAO,EAAE,WAAW,IAAI,CAAC;AAAA,EAC7C;AACF;AAEO,IAAM,0BAA0B,CACrC,QAC4B;AAC5B,QAAM,WAAW,YAAY,WAAW,KAAK,OAAO,CAAC;AACrD,QAAM,kBAAkB,aAAa,KAAK,YAAY,EAAE,CAAC;AAEzD,QAAMC,SACJ,YAAY,OAAO,WAAW,IAAI,eAAe,WAAW,KAAK,OAAO,CAAC;AAE3E,SAAO;AAAA,IACL,GAAI,YAAY,OAAO,EAAE,SAAS,IAAI,CAAC;AAAA,IACvC,GAAIA,UAAS,OAAO,EAAE,OAAAA,OAAM,IAAI,CAAC;AAAA,IACjC,mBAAmB,mBAAmB;AAAA,EACxC;AACF;AAEO,IAAM,kCAAkC,CAC7C,QACoC;AAEpC,QAAM,cAAc,aAAa,KAAK,iBAAiB;AACvD,QAAM,SAAS,YACZ,IAAI,CAAC,MAAM;AACV,UAAM,IAAI,YAAY,WAAW,GAAG,GAAG,CAAC;AACxC,UAAM,IAAI,YAAY,WAAW,GAAG,GAAG,CAAC;AACxC,QAAI,KAAK,QAAQ,KAAK,KAAM,QAAO;AACnC,WAAO,EAAE,GAAG,EAAE;AAAA,EAChB,CAAC,EACA,OAAO,CAAC,MAAqC,KAAK,IAAI;AAEzD,SAAO;AAAA,IACL,OAAO,OAAO;AAAA,IACd,GAAI,OAAO,SAAS,EAAE,OAAO,IAAI,CAAC;AAAA,EACpC;AACF;;;ACtHO,IAAM,+BAA+B,CAAC,aAA6B;AACxE,MAAI,CAAC,SAAS,SAAS,GAAG,EAAG,QAAO;AACpC,SAAO,SAAS,MAAM,GAAG,EAAE,OAAO,OAAO,EAAE,GAAG,EAAE,KAAK;AACvD;AAEO,IAAM,+BAA+B,CAAC,WAK/B;AACZ,QAAM,OAAO,6BAA6B,OAAO,QAAQ;AACzD,QAAM,QAAQ,OAAO,gBAAgB,OAAO;AAE5C,SAAO;AAAA;AAAA;AAAA;AAAA,aAII,KAAK;AAAA,OACX,UAAU,OAAO,GAAG,CAAC;AAAA,YAChB,UAAU,OAAO,QAAQ,CAAC;AAAA,QAC9B,UAAU,IAAI,CAAC;AAAA,MACjB,UAAU,OAAO,QAAQ,CAAC;AAAA;AAAA;AAAA;AAIhC;;;AC3BO,IAAM,8BAA8B,CACzC,aACuB;AACvB,QAAM,IAAI,yCAAyC,KAAK,QAAQ;AAChE,MAAI,CAAC,EAAG,QAAO;AACf,SAAO,GAAG,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC;AACvB;AAMO,IAAM,kCAAkC,CAC7C,aACuB;AACvB,QAAM,WAAW,YAAY,IAAI,KAAK;AACtC,MAAI,aAAa,KAAK,OAAO,EAAG,QAAO;AACvC,QAAM,QAAQ,4BAA4B,QAAQ;AAClD,MAAI,CAAC,MAAO,QAAO;AACnB,SAAO,KAAK,KAAK;AACnB;AAEO,IAAM,iCAAiC,CAAC,WAQjC;AACZ,QAAM,KAAK,OAAO,cAAc;AAChC,QAAM,aAAa,OAAO,cAAc,IAAI;AAC5C,QAAM,QAAQ,OAAO,gBAAgB,OAAO;AAC5C,QAAM,SAAS,OAAO;AACtB,QAAM,YACJ,WAAW,QAAQ,WAAW,SAC1B,qEACA,WAAW,MACT,qCACA,WAAW,MACT,qCACA;AAIV,QAAM,QAAQ;AAAA,IACZ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,cAAc,KAAK;AAAA,IACnB,OAAO,UAAU,OAAO,EAAE,CAAC;AAAA,EAC7B;AACA,MAAI,OAAO,KAAK;AACd,UAAM,KAAK,QAAQ,UAAU,OAAO,GAAG,CAAC,QAAQ;AAAA,EAClD;AACA,QAAM;AAAA,IACJ,eAAe,UAAU;AAAA,IACzB;AAAA,IACA,eAAe,UAAU,EAAE,CAAC;AAAA,EAC9B;AACA,MAAI,WAAW;AACb,UAAM,KAAK,SAAS;AAAA,EACtB;AACA,QAAM,KAAK,eAAe,mBAAmB,SAAS;AACtD,SAAO,MAAM,KAAK,IAAI;AACxB;AAEO,IAAM,mCAAmC,CAAC,WAQnC;AACZ,QAAM,KAAK,OAAO,cAAc;AAChC,QAAM,aAAa,OAAO,cAAc,IAAI;AAC5C,QAAM,QAAQ,OAAO,gBAAgB,OAAO;AAC5C,QAAM,SAAS,OAAO;AACtB,QAAM,YACJ,WAAW,QAAQ,WAAW,SAC1B,qEACA,WAAW,MACT,qCACA,WAAW,MACT,qCACA;AAGV,QAAM,QAAQ;AAAA,IACZ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,cAAc,KAAK;AAAA,IACnB,OAAO,UAAU,OAAO,IAAI,CAAC;AAAA,EAC/B;AACA,MAAI,OAAO,KAAK;AACd,UAAM,KAAK,QAAQ,UAAU,OAAO,GAAG,CAAC,QAAQ;AAAA,EAClD;AACA,QAAM;AAAA,IACJ,eAAe,UAAU;AAAA,IACzB;AAAA,IACA,eAAe,UAAU,EAAE,CAAC;AAAA,EAC9B;AACA,MAAI,WAAW;AACb,UAAM,KAAK,SAAS;AAAA,EACtB;AACA,QAAM,KAAK,eAAe,mBAAmB,SAAS;AACtD,SAAO,MAAM,KAAK,IAAI;AACxB;AAEO,IAAM,2BAA2B,CAAC,WAI3B;AACZ,QAAM,KAAK,OAAO,cAAc;AAEhC,SAAO;AAAA;AAAA;AAAA;AAAA,aAII,OAAO,OAAO;AAAA,QACnB,UAAU,OAAO,IAAI,CAAC;AAAA,cAChB,UAAU,EAAE,CAAC;AAAA;AAAA;AAAA;AAI3B;;;ACzIO,IAAM,UAAU,CAAC,OAA8B,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,EAAE,CAAC;AAE/F,IAAM,qBAAqB,CAAC,KAA8B,MAAoB;AAEnF,SAAO,IAAI,GAAG,UAAU,EAAE,YAAY,CAAC,iBAAiB,EAAE,SAAS,IAAI,CAAC,gBAAgB,EAAE,QAAQ,CAAC,eAAe,EAAE,SAAS,CAAC,kBAAkB,EAAE,WAAW,CAAC,oBAAoB,EAAE,WAAW,CAAC,cAAc,GAAG;AACnN;;;ACYO,IAAM,2BAA2B,CAAC,WAO3B;AACZ,SAAO;AAAA;AAAA;AAAA;AAAA,OAIF,UAAU,OAAO,GAAG,CAAC;AAAA;AAAA,aAEf,OAAO,OAAO;AAAA;AAAA,cAEb,UAAU,OAAO,UAAU,CAAC;AAAA,cAC5B,UAAU,OAAO,UAAU,CAAC;AAAA,EACxC,mBAAmB,aAAa,OAAO,KAAK,CAAC;AAAA,EAC7C,mBAAmB,WAAW,OAAO,GAAG,CAAC;AAAA;AAAA;AAAA;AAI3C;AAEO,IAAM,2BAA2B,CAAC,WAI3B;AACZ,SAAO;AAAA;AAAA;AAAA;AAAA,aAII,OAAO,OAAO;AAAA,OACpB,UAAU,OAAO,GAAG,CAAC;AAAA;AAAA,UAElB,OAAO,MAAM;AAAA;AAAA;AAAA;AAIvB;AAEO,IAAM,0BAA0B,CAAC,gBAAgC;AACtE,QAAM,aAAa,WAAW,aAAa,QAAQ;AACnD,MAAI,CAAC,WAAY,OAAM,IAAI,MAAM,2CAA2C;AAE5E,QAAM,SAAS,OAAO,SAAS,YAAY,EAAE;AAC7C,MAAI,CAAC,OAAO,SAAS,MAAM,GAAG;AAC5B,UAAM,IAAI,MAAM,8CAA8C,UAAU,EAAE;AAAA,EAC5E;AAEA,SAAO;AACT;AAEO,IAAM,uBAAuB,CAClC,UACoB;AACpB,QAAM,OAAO,oBAAI,IAAY;AAC7B,SAAO,MAAM,OAAO,CAAC,MAAM;AACzB,QAAI,KAAK,IAAI,EAAE,QAAQ,EAAG,QAAO;AACjC,SAAK,IAAI,EAAE,QAAQ;AACnB,WAAO;AAAA,EACT,CAAC;AACH;AAEO,IAAM,gCAAgC,OAAO,WAUpB;AAC9B,QAAM,YAAY,OAAO,aAAa;AAEtC,QAAM,UAAU,yBAAyB;AAAA,IACvC,KAAK,OAAO;AAAA,IACZ,SAAS,OAAO;AAAA,IAChB,YAAY,OAAO;AAAA,IACnB,YAAY,OAAO;AAAA,IACnB,OAAO,OAAO;AAAA,IACd,KAAK,OAAO;AAAA,EACd,CAAC;AAMD,QAAM,WAAW,MAAM,OAAO,QAAQ;AAAA,IACpC,OAAO;AAAA;AAAA,IAEP,YAAY;AAAA,IACZ;AAAA,EACF,CAAC;AAED,QAAM,SAAS,wBAAwB,QAAQ;AAE/C,QAAM,UAAU,yBAAyB;AAAA,IACvC,SAAS,OAAO;AAAA,IAChB,KAAK,OAAO;AAAA,IACZ;AAAA,EACF,CAAC;AAED,QAAM,QAAyB,CAAC;AAChC,QAAM,oBAAoB;AAE1B,MAAI;AACF,aAAS,IAAI,GAAG,IAAI,OAAO,eAAe,KAAK;AAC7C,UAAI;AACJ,UAAI;AACF,eAAO,MAAM,OAAO,QAAQ;AAAA,UAC1B,OAAO;AAAA;AAAA,UAEP,YAAY;AAAA,UACZ;AAAA,QACF,CAAC;AAAA,MACH,SAAS,GAAG;AACV,cAAM,WAAW,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC;AAC1D,YAAI,SAAS,SAAS,8BAA8B,EAAG;AACvD,cAAM;AAAA,MACR;AAEA,YAAM,YAAY,2BAA2B,IAAI;AACjD,YAAM,KAAK,GAAG,SAAS;AAEvB,YAAM,gBACJ,WAAW,MAAM,WAAW,KAAK,WAAW,MAAM,UAAU;AAC9D,UAAI,iBAAiB,MAAM;AACzB,YAAI,cAAc,KAAK,MAAM,IAAK;AAAA,MACpC,WACE,UAAU,WAAW,KACrB,UAAU,SAAS,mBACnB;AACA;AAAA,MACF;AAAA,IACF;AAAA,EACF,UAAE;AACA,QAAI;AACF,YAAM,OAAO,QAAQ;AAAA,QACnB,OAAO;AAAA;AAAA,QAEP,YAAY;AAAA,QACZ,WAAW,KAAK,IAAI,WAAW,GAAK;AAAA,MACtC,CAAC;AAAA,IACH,QAAQ;AAAA,IAER;AAAA,EACF;AAEA,SAAO;AACT;AAMO,IAAM,mCAAmC,CAAC,WAInC;AACZ,QAAM,OACJ,OAAO,SAAS,MAAM,GAAG,EAAE,OAAO,OAAO,EAAE,GAAG,EAAE,KAAK,OAAO;AAC9D,SAAO;AAAA;AAAA;AAAA;AAAA,OAIF,UAAU,OAAO,GAAG,CAAC;AAAA,aACf,OAAO,OAAO;AAAA,YACf,UAAU,OAAO,QAAQ,CAAC;AAAA,QAC9B,UAAU,IAAI,CAAC;AAAA,MACjB,UAAU,OAAO,QAAQ,CAAC;AAAA;AAAA;AAAA;AAIhC;AAKO,IAAM,mCAAmC,CAAC,WAInC;AACZ,SAAO;AAAA;AAAA;AAAA;AAAA,aAII,OAAO,OAAO;AAAA,OACpB,UAAU,OAAO,GAAG,CAAC;AAAA,UAClB,OAAO,MAAM;AAAA;AAAA;AAAA;AAIvB;AAaO,IAAM,wCAAwC,OAAO,WAQrC;AACrB,QAAM,YAAY,OAAO,aAAa;AACtC,QAAM,gBAAgB,OAAO,iBAAiB;AAE9C,QAAM,UAAU,iCAAiC;AAAA,IAC/C,KAAK,OAAO;AAAA,IACZ,SAAS,OAAO;AAAA,IAChB,UAAU,OAAO;AAAA,EACnB,CAAC;AAGD,QAAM,WAAW,MAAM,OAAO,QAAQ;AAAA,IACpC,OAAO;AAAA,IACP,YAAY;AAAA,IACZ;AAAA,EACF,CAAC;AAED,QAAM,SAAS,wBAAwB,QAAQ;AAE/C,QAAM,UAAU,iCAAiC;AAAA,IAC/C,SAAS,OAAO;AAAA,IAChB,KAAK,OAAO;AAAA,IACZ;AAAA,EACF,CAAC;AAED,QAAM,SAAmB,CAAC;AAE1B,MAAI;AACF,aAAS,IAAI,GAAG,IAAI,eAAe,KAAK;AACtC,UAAI;AACJ,UAAI;AAEF,eAAO,MAAM,OAAO,WAAW;AAAA,UAC7B,OAAO;AAAA,UACP,YAAY;AAAA,UACZ;AAAA,QACF,CAAC;AAAA,MACH,SAAS,GAAG;AACV,cAAM,WAAW,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC;AAE1D,YAAI,SAAS,SAAS,kBAAkB,KAAK,SAAS,SAAS,OAAO;AACpE;AACF,cAAM;AAAA,MACR;AAEA,UAAI,KAAK,WAAW,EAAG;AACvB,aAAO,KAAK,IAAI;AAIhB,UAAI,KAAK,SAAS,IAAO;AAAA,IAC3B;AAAA,EACF,UAAE;AACA,QAAI;AAEF,YAAM,OAAO,QAAQ;AAAA,QACnB,OAAO;AAAA,QACP,YAAY;AAAA,QACZ,WAAW,KAAK,IAAI,WAAW,GAAK;AAAA,MACtC,CAAC;AAAA,IACH,QAAQ;AAAA,IAER;AAAA,EACF;AAEA,SAAO,OAAO,OAAO,MAAM;AAC7B;;;AC3SA,IAAM,gBAA4C;AAAA,EAChD,GAAG;AAAA,EACH,GAAG;AAAA,EACH,GAAG;AAAA,EACH,GAAG;AACL;AAEA,IAAM,oBAAoB,CAAC,eAA4C;AACrE,SAAO,eAAe,UAAa,eAAe,OAAO,eAAe;AAC1E;AAEA,IAAM,oBAAoB,CAAC,MAAsF;AAC/G,SAAO,EAAE,QAAQ,KAAK,EAAE,SAAS,MAAM,EAAE,YAAY,KAAK,EAAE,UAAU;AACxE;AAEA,IAAM,sBAAsB,CAAC,WAMjB;AACV,QAAM,EAAE,QAAQ,mBAAmB,SAAS,KAAK,SAAS,IAAI;AAC9D,MAAI,CAAC,kBAAmB;AAExB,MAAI,CAAC,UAAU;AACb,KAAC,OAAO,QAAQ,OAAO,KAAK,KAAK,QAAQ,sEAAsE,OAAO,SAAS,GAAG,WAAW;AAC7I;AAAA,EACF;AAEA,QAAM,MAAM;AACZ,QAAM,YAAY,WAAW,KAAK,OAAO,KAAK,WAAW,KAAK,OAAO;AACrE,QAAM,aAAa,WAAW,KAAK,QAAQ,KAAK,WAAW,KAAK,QAAQ;AACxE,QAAM,YAAY,WAAW,KAAK,OAAO,KAAK,WAAW,KAAK,OAAO;AACrE,QAAM,cAAc,WAAW,KAAK,SAAS,KAAK,WAAW,KAAK,SAAS;AAC3E,QAAM,mBAAmB,WAAW,KAAK,cAAc,KAAK,WAAW,KAAK,cAAc;AAC1F,QAAM,YAAY,WAAW,KAAK,OAAO,KAAK,WAAW,KAAK,OAAO;AACrE,QAAM,aAAa,WAAW,KAAK,QAAQ,KAAK,WAAW,KAAK,QAAQ;AAExE,QAAM,QAAQ,OAAO,aAAa,GAAG;AACrC,QAAM,SAAS,OAAO,cAAc,GAAG;AACvC,QAAM,YAAY,OAAO,aAAa,GAAG;AACzC,QAAM,UAAU,OAAO,eAAe,GAAG;AACzC,QAAM,QAAQ,OAAO,aAAa,GAAG;AACrC,QAAM,YAAY,kBAAkB,UAAU;AAC9C,QAAM,YAAY,aAAa,kBAAkB,EAAE,OAAO,QAAQ,WAAW,QAAQ,CAAC;AAEtF,QAAM,aAAa;AACnB,QAAM,aAAa,IAAI,UAAU,aAAa,MAAM,IAAI,MAAM,GAAG,UAAU,IAAI;AAAA,iBAAoB,IAAI,SAAS,UAAU;AAE1H,GAAC,OAAO,QAAQ,OAAO,KAAK;AAAA,IAC1B;AAAA,IACA,sEAAsE,OAAO,SAAS,GAAG,aAC5E,SAAS,cAAc,SAAS,UAClC,KAAK,WAAW,MAAM,UAAU,SAAS,YAAY,OAAO,UAAU,KAAK,iBAAiB,oBAAoB,GAAG,cAC/G,KAAK,UAAU,EAAE,WAAW,YAAY,WAAW,aAAa,kBAAkB,WAAW,WAAW,CAAC,CAAC,aAC3G,KAAK,UAAU,UAAU,CAAC;AAAA,EAC1C;AACF;AAEA,IAAM,cAAc,CAAC,WAGa;AAChC,QAAM,EAAE,SAAS,UAAU,IAAI;AAE/B,QAAM,QAAQ,OAAO,WAAW,WAAW,OAAO,KAAK,GAAG;AAC1D,QAAM,SAAS,OAAO,WAAW,WAAW,QAAQ,KAAK,GAAG;AAC5D,QAAM,kBAAkB,OAAO,WAAW,WAAW,cAAc,KAAK,GAAG;AAC3E,QAAM,YAAY,OAAO,WAAW,WAAW,OAAO,KAAK,GAAG;AAC9D,QAAM,UAAU,OAAO,WAAW,WAAW,SAAS,KAAK,GAAG;AAC9D,QAAM,QAAQ,OAAO,WAAW,WAAW,OAAO,KAAK,GAAG;AAE1D,QAAM,UAAU,WAAW,WAAW,QAAQ;AAC9C,QAAM,YAAY,kBAAkB,OAAO;AAE3C,MAAI,CAAC,aAAa,CAAC,kBAAkB,EAAE,OAAO,QAAQ,WAAW,QAAQ,CAAC,EAAG,QAAO;AAEpF,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,cAAc,cAAc,eAAe,KAAK,WAAW,eAAe;AAAA,IAC1E;AAAA,IACA;AAAA,IACA;AAAA,IACA,YAAY;AAAA,EACd;AACF;AAEO,IAAM,0CAA0C,CAAC,WAK3B;AAC3B,QAAM,EAAE,SAAS,KAAK,OAAO,IAAI;AACjC,QAAM,oBAAoB,OAAO,sBAAsB;AAEvD,QAAM,UAA4B,CAAC;AACnC,MAAI,eAAe;AAEnB,MAAI,mBAAmB;AACrB,UAAM,UAAU;AAChB,UAAM,UAAU,IAAI,UAAU,UAAU,MAAM,IAAI,MAAM,GAAG,OAAO,IAAI;AAAA,iBAAoB,IAAI,SAAS,OAAO;AAC9G,UAAM,cAAc;AAAA,MAClB,YAAY,gBAAgB,KAAK,GAAG;AAAA,MACpC,WAAW,eAAe,KAAK,GAAG;AAAA,MAClC,WAAW,eAAe,KAAK,GAAG;AAAA,MAClC,aAAa,iBAAiB,KAAK,GAAG;AAAA,MACtC,cAAc,kBAAkB,KAAK,GAAG;AAAA,MACxC,aAAa,iBAAiB,KAAK,GAAG;AAAA,IACxC;AACA,KAAC,OAAO,QAAQ,OAAO,KAAK;AAAA,MAC1B;AAAA,MACA,sEAAsE,OAAO,WAAW,IAAI,MAAM,gBAAgB,KAAK,UAAU,WAAW,CAAC,YAAY,KAAK,UAAU,OAAO,CAAC;AAAA,IAClL;AAAA,EACF;AAEA,QAAM,YAAY,IAAI,MAAM,2CAA2C;AACvE,MAAI,WAAW;AACb,UAAM,UAAU,UAAU,CAAC,KAAK;AAChC,wBAAoB,EAAE,QAAQ,mBAAmB,SAAS,KAAK,cAAc,UAAU,QAAQ,CAAC;AAChG,UAAM,IAAI,YAAY,EAAE,SAAS,QAAQ,WAAW,QAAQ,CAAC;AAC7D,QAAI,GAAG;AACL,cAAQ,KAAK,CAAC;AACd,qBAAe,gBAAgB,EAAE,UAAU;AAAA,IAC7C;AAAA,EACF;AAEA,QAAM,WAAW,IAAI,MAAM,yCAAyC;AACpE,MAAI,UAAU;AACZ,UAAM,SAAS,SAAS,CAAC,KAAK;AAC9B,wBAAoB,EAAE,QAAQ,mBAAmB,SAAS,KAAK,aAAa,UAAU,OAAO,CAAC;AAC9F,UAAM,IAAI,YAAY,EAAE,SAAS,OAAO,WAAW,OAAO,CAAC;AAC3D,QAAI,GAAG;AACL,cAAQ,KAAK,CAAC;AACd,qBAAe,gBAAgB,EAAE,UAAU;AAAA,IAC7C;AAAA,EACF;AAEA,QAAM,cAAc,CAAC,aAAa,eAAe,gBAAgB,aAAa;AAC9E,aAAW,OAAO,aAAa;AAC7B,UAAM,WAAW,IAAI,MAAM,IAAI,OAAO,IAAI,GAAG,yBAAyB,GAAG,GAAG,CAAC;AAC7E,QAAI,CAAC,SAAU;AAEf,UAAM,SAAS,SAAS,CAAC,KAAK;AAC9B,wBAAoB,EAAE,QAAQ,mBAAmB,SAAS,KAAK,UAAU,OAAO,CAAC;AACjF,UAAM,IAAI,YAAY,EAAE,SAAS,OAAO,WAAW,OAAO,CAAC;AAC3D,QAAI,GAAG;AACL,cAAQ,KAAK,CAAC;AACd,qBAAe,gBAAgB,EAAE,UAAU;AAAA,IAC7C;AAEA;AAAA,EACF;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;;;ACpKO,IAAM,4BAA4B,CAAC,WAA+B;AACvE,QAAM,QAAQ,OAAO;AACrB,SAAO;AAAA;AAAA;AAAA,aAGI,OAAO,OAAO;AAAA,UACjB,UAAU,OAAO,MAAM,CAAC;AAAA,mBACf,UAAU,OAAO,eAAe,CAAC;AAAA;AAAA,aAEvC,UAAU,MAAM,SAAS,CAAC;AAAA,cACzB,MAAM,UAAU;AAAA,mBACX,MAAM,eAAe;AAAA,oBACpB,MAAM,gBAAgB;AAAA,cAC5B,UAAU,MAAM,UAAU,CAAC;AAAA;AAAA;AAAA;AAIzC;AAEO,IAAM,0BAA0B,CAAC,OAAe,kBAAkC;AAGvF,QAAM,QAAQ;AACd,QAAM,gBAAgB;AACtB,QAAM,cAAc,gBAAgB,MAAM;AAC1C,QAAM,YAAY;AAClB,QAAM,UAAU,cAAc,MAAM,IAAI,IAAI,IAAK,cAAc;AAC/D,QAAM,WAAW,YAAY,MAAM,SAAS;AAC5C,QAAM,MAAM,OAAO,MAAM,QAAQ;AAEjC,MAAI,cAAc,OAAO,CAAC;AAC1B,MAAI,cAAc,aAAa,CAAC;AAChC,MAAI,cAAc,aAAa,CAAC;AAChC,MAAI,cAAc,KAAQ,CAAC;AAC3B,MAAI,cAAc,eAAe,EAAE;AACnC,QAAM,KAAK,KAAK,EAAE;AAElB,SAAO;AACT;;;AChCO,IAAM,kCAAkC,CAAC,WAGK;AACnD,QAAM,cACJ,OAAO,QAAQ,gBAAgB,KAAK,CAAC,MAAM,EAAE,UAAU,YAAY,MAAM,OAAO,KAChF,OAAO,QAAQ,gBAAgB,CAAC;AAElC,MAAI,CAAC,aAAa;AAChB,UAAM,IAAI,MAAM,iCAAiC,OAAO,OAAO,kCAAkC;AAAA,EACnG;AAEA,QAAM,aAAyB;AAAA,IAC7B,SAAS,OAAO;AAAA,IAChB,QAAQ,OAAO,QAAQ,WAAW,CAAC,KAAK;AAAA,IACxC,iBAAiB,OAAO,QAAQ,oBAAoB,CAAC,KAAK;AAAA,IAC1D;AAAA,EACF;AAEA,QAAM,YAAY,KAAK,MAAM,YAAY,mBAAmB,CAAC;AAC7D,QAAM,gBAAgB,YAAY;AAClC,MAAI,aAAa,KAAK,iBAAiB,GAAG;AACxC,UAAM,IAAI,MAAM,+CAA+C,YAAY,gBAAgB,EAAE;AAAA,EAC/F;AAEA,SAAO;AAAA,IACL,YAAY,0BAA0B,UAAU;AAAA,IAChD,MAAM;AAAA,MACJ,SAAS,OAAO;AAAA,MAChB;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACF;AAEO,IAAM,0BAA0B,OAAO,WAKzB;AACnB,QAAM,UAAU,YAA6B;AAC3C,UAAM,QAAQ,MAAM,OAAO,OAAO,UAAU;AAAA,MAC1C,OAAO;AAAA,MACP,SAAS,OAAO;AAAA,MAChB,GAAI,OAAO,qBAAqB,OAAO,EAAE,mBAAmB,OAAO,kBAAkB,IAAI,CAAC;AAAA,MAC1F,YAAY,OAAO;AAAA,MACnB,cAAc;AAAA,IAChB,CAAC;AACD,WAAO,MAAM,OAAO;AAAA,EACtB;AAEA,QAAM,OAAO,MAAM,QAAQ;AAC3B,MAAI,SAAS,KAAK;AAChB,UAAM,OAAO,OAAO,UAAU;AAAA,MAC5B,OAAO;AAAA,MACP,SAAS,OAAO;AAAA,MAChB,GAAI,OAAO,qBAAqB,OAAO,EAAE,mBAAmB,OAAO,kBAAkB,IAAI,CAAC;AAAA,MAC1F,YAAY;AAAA,MACZ,cAAc;AAAA,IAChB,CAAC;AAED,UAAM,YAAY,MAAM,QAAQ;AAChC,QAAI,cAAc,KAAK;AACrB,YAAM,IAAI,MAAM,iDAAiD,SAAS,GAAG;AAAA,IAC/E;AACA;AAAA,EACF;AAEA,MAAI,SAAS,KAAK;AAChB,UAAM,IAAI,MAAM,qCAAqC,IAAI,GAAG;AAAA,EAC9D;AACF;;;AC3EO,IAAM,4BAA4B,CAAC,WAQvB;AACjB,QAAM,EAAE,QAAQ,SAAS,mBAAmB,KAAK,IAAI;AAErD,QAAM,SAAmB,CAAC;AAC1B,MAAI,gBAAgB;AACpB,MAAI,SAAS;AACb,MAAI,UAAU;AACd,MAAI,sBAAsB,KAAK,IAAI;AAEnC,QAAM,UAAU,CAAC,MAAoB;AACnC,QAAI,EAAE,WAAW,EAAG;AACpB,WAAO,KAAK,CAAC;AACb,qBAAiB,EAAE;AAAA,EACrB;AAEA,QAAM,UAAU,CAAC,MAAsB;AACrC,QAAI,KAAK,EAAG,QAAO,OAAO,MAAM,CAAC;AACjC,QAAI,gBAAgB,GAAG;AACrB,YAAM,IAAI,MAAM,2BAA2B,CAAC,wBAAwB,aAAa,EAAE;AAAA,IACrF;AAEA,QAAI,OAAO,WAAW,GAAG;AACvB,YAAM,OAAO,OAAO,CAAC;AACrB,UAAI,KAAK,WAAW,GAAG;AACrB,eAAO,SAAS;AAChB,wBAAgB;AAChB,eAAO;AAAA,MACT;AACA,UAAI,KAAK,SAAS,GAAG;AACnB,cAAMC,OAAM,KAAK,SAAS,GAAG,CAAC;AAC9B,eAAO,CAAC,IAAI,KAAK,SAAS,CAAC;AAC3B,yBAAiB;AACjB,eAAOA;AAAA,MACT;AAAA,IACF;AAEA,UAAM,MAAM,OAAO,YAAY,CAAC;AAChC,QAAI,MAAM;AACV,WAAO,MAAM,GAAG;AACd,YAAM,OAAO,OAAO,CAAC;AACrB,UAAI,CAAC,KAAM,OAAM,IAAI,MAAM,mCAAmC;AAC9D,YAAM,OAAO,KAAK,IAAI,IAAI,KAAK,KAAK,MAAM;AAC1C,WAAK,KAAK,KAAK,KAAK,GAAG,IAAI;AAE3B,UAAI,SAAS,KAAK,QAAQ;AACxB,eAAO,MAAM;AAAA,MACf,OAAO;AACL,eAAO,CAAC,IAAI,KAAK,SAAS,IAAI;AAAA,MAChC;AACA,aAAO;AAAA,IACT;AAEA,qBAAiB;AACjB,WAAO;AAAA,EACT;AAEA,QAAM,qBAAqB,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,KAAK,MAAM,OAAO,oBAAoB,CAAC,CAAC,CAAC;AAE5F,QAAM,aAAa,OAAO,WAAoC;AAC5D,QAAI,OAAO,WAAW,EAAG;AACzB,QAAI,OAAO,SAAS,oBAAoB;AACtC,YAAM,IAAI,MAAM,+CAA+C,OAAO,MAAM,GAAG;AAAA,IACjF;AAEA,UAAM,QAAkB,CAAC;AACzB,QAAI,cAAc;AAClB,eAAW,SAAS,QAAQ;AAC1B,YAAM,KAAK,wBAAwB,OAAO,KAAK,SAAS,CAAC;AACzD,qBAAe,KAAK,IAAI,IAAI,MAAM,SAAS,KAAK,IAAI,CAAC;AAAA,IACvD;AAEA,UAAM,OAAO,yBAAyB;AAAA,MACpC,OAAO;AAAA,MACP;AAAA,MACA,GAAI,qBAAqB,OAAO,EAAE,kBAAkB,IAAI,CAAC;AAAA,MACzD,cAAc,wBAAwB,OAAO;AAAA,MAC7C,SAAS,OAAO,OAAO,KAAK;AAAA,MAC5B,cAAc;AAAA,IAChB,CAAC;AAED,UAAM,eAAgB,cAAc,KAAK,YAAY,aAAc;AAEnE,UAAM,MAAM,KAAK,IAAI;AACrB,QAAI,MAAM,oBAAqB,uBAAsB,MAAM;AAAA,QACtD,wBAAuB;AAE5B,UAAM,WAAW,sBAAsB,KAAK,IAAI;AAChD,QAAI,WAAW,EAAG,OAAM,QAAQ,QAAQ;AAAA,EAC1C;AAEA,QAAM,OAAO,YAA2B;AACtC,QAAI,QAAS;AACb,cAAU;AACV,QAAI;AACF,aAAO,MAAM;AACX,YAAI,iBAAiB,KAAK,eAAe;AACvC,gBAAM,SAAmB,CAAC;AAC1B,iBAAO,OAAO,SAAS,sBAAsB,iBAAiB,KAAK,eAAe;AAChF,mBAAO,KAAK,QAAQ,KAAK,aAAa,CAAC;AAAA,UACzC;AACA,gBAAM,WAAW,MAAM;AACvB;AAAA,QACF;AAEA,YAAI,QAAQ;AACV,cAAI,gBAAgB,GAAG;AACrB,kBAAM,SAAS,OAAO,MAAM,KAAK,eAAe,GAAI;AACpD,kBAAM,OAAO,QAAQ,aAAa;AAClC,iBAAK,KAAK,QAAQ,CAAC;AACnB,kBAAM,WAAW,CAAC,MAAM,CAAC;AAAA,UAC3B;AACA;AAAA,QACF;AAEA;AAAA,MACF;AAAA,IACF,UAAE;AACA,gBAAU;AAAA,IACZ;AAAA,EACF;AAEA,QAAM,OAAO,YAA2B;AACtC,QAAI,OAAQ;AACZ,aAAS;AACT,UAAM,KAAK;AAEX,UAAM,YAAY,sBAAsB,KAAK,IAAI;AACjD,QAAI,YAAY,EAAG,OAAM,QAAQ,YAAY,GAAG;AAAA,QAC3C,OAAM,QAAQ,GAAG;AAEtB,UAAM,QAAQ,MAAM,OAAO,UAAU;AAAA,MACnC,OAAO;AAAA,MACP;AAAA,MACA,GAAI,qBAAqB,OAAO,EAAE,kBAAkB,IAAI,CAAC;AAAA,MACzD,YAAY;AAAA,MACZ,cAAc;AAAA,IAChB,CAAC;AACD,QAAI,MAAM,OAAO,iBAAiB,KAAK;AACrC,YAAM,IAAI,MAAM,oCAAoC,MAAM,OAAO,YAAY,GAAG;AAAA,IAClF;AAEA,QAAI,OAAO,mBAAmB;AAC5B,UAAI;AACF,cAAM,OAAO,MAAM,EAAE,QAAQ,oBAAoB,CAAC;AAAA,MACpD,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AAAA,IACL;AAAA,IACA,WAAW,OAAO,UAAkB;AAClC,UAAI,OAAQ,OAAM,IAAI,MAAM,wBAAwB;AACpD,UAAI,MAAM,WAAW,EAAG;AACxB,cAAQ,KAAK;AACb,YAAM,KAAK;AAAA,IACb;AAAA,IACA;AAAA,EACF;AACF;;;AC/KO,IAAM,mBAAmB,CAAC,UAA2B;AAC1D,QAAM,IAAI,MAAM,KAAK;AACrB,SAAO,oBAAoB,KAAK,CAAC,KAAK,QAAQ,KAAK,CAAC;AACtD;AAEO,IAAM,wBAAwB,CAAC,UAAuC;AAC3E,QAAM,OAAO,oBAAI,IAAa;AAE9B,QAAM,OAAO,CAAC,MAAmC;AAC/C,QAAI,KAAK,KAAM,QAAO;AAEtB,QAAI,OAAO,MAAM,UAAU;AACzB,YAAM,IAAI,EAAE,KAAK;AACjB,UAAI,iBAAiB,CAAC,EAAG,QAAO;AAGhC,YAAM,IAAI,EAAE,MAAM,qBAAqB;AACvC,UAAI,IAAI,CAAC,KAAK,iBAAiB,EAAE,CAAC,CAAC,EAAG,QAAO,EAAE,CAAC;AAChD,aAAO;AAAA,IACT;AAEA,QAAI,OAAO,MAAM,SAAU,QAAO;AAClC,QAAI,KAAK,IAAI,CAAC,EAAG,QAAO;AACxB,SAAK,IAAI,CAAC;AAEV,QAAI,MAAM,QAAQ,CAAC,GAAG;AACpB,iBAAW,MAAM,GAAG;AAClB,cAAM,IAAI,KAAK,EAAE;AACjB,YAAI,EAAG,QAAO;AAAA,MAChB;AACA,aAAO;AAAA,IACT;AAEA,eAAW,MAAM,OAAO,OAAO,CAA4B,GAAG;AAC5D,YAAM,IAAI,KAAK,EAAE;AACjB,UAAI,EAAG,QAAO;AAAA,IAChB;AAEA,WAAO;AAAA,EACT;AAEA,SAAO,KAAK,KAAK;AACnB;;;ACxCO,IAAM,2CAA2C,OAAO,WAI5B;AACjC,QAAM,OAAO,MAAM;AACnB,QAAM,WAAW,MAAM,OAAO,iBAAiB;AAC/C,QAAM,QAAQ,SACX,QAAQ,CAAC,MAAM,EAAE,OAAO,UAAU,CAAC,CAAC,EACpC,KAAK,CAAC,MAAM,OAAO,GAAG,YAAY,YAAY,EAAE,YAAY,OAAO,OAAO;AAE7E,QAAM,gBAAgB,OAAO,OAAO,IAAI,KAAK;AAC7C,SAAO,gBAAgB,iBAAiB,YAAY,IAAI,eAAe;AACzE;AAEO,IAAM,oCAAoC,OAAO,WAErB;AACjC,QAAM,OAAO,MAAM,OAAO,QAAQ;AAClC,QAAM,UAAU,KAAK,gBAAgB,IAAI,KAAK;AAC9C,SAAO,UAAU,iBAAiB,MAAM,IAAI,SAAS;AACvD;AAEO,IAAM,0BAA0B,OAAO,WAIX;AACjC,QAAM,OAAO,MAAM;AAEnB,MAAI;AACF,UAAM,MAAM,MAAM,OAAO,OAAO;AAChC,UAAM,UAAU,sBAAsB,GAAG;AACzC,QAAI,QAAS,QAAO;AAAA,EACtB,QAAQ;AAAA,EAER;AAEA,MAAI;AACF,UAAM,UAAU,MAAM,OAAO,WAAW;AACxC,UAAM,cAAc,sBAAsB,OAAO;AACjD,QAAI,YAAa,QAAO;AAAA,EAC1B,QAAQ;AAAA,EAER;AAEA,SAAO;AACT;AAEO,IAAM,qCAAqC,OAAO,WAEtB;AACjC,QAAM,SAAS,MAAM,OAAO,QAAQ,EAAE,OAAO,KAAK,WAAW,IAAO,CAAC;AACrE,SAAO,sBAAsB,MAAM;AACrC;;;ACrDA,IAAM,mBAAmB,CAAC,cAAsD;AAC9E,QAAM,OAAO,aAAa,IAAI,KAAK;AACnC,MAAI,CAAC,IAAK,QAAO;AAEjB,QAAM,QAAQ,IACX,MAAM,GAAG,EACT,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EACnB,KAAK,CAAC,MAAM,EAAE,SAAS,KAAK,EAAE,YAAY,MAAM,MAAM;AAEzD,SAAO;AACT;AAEO,IAAM,8BAA8B,CAAC,WAAoE;AAC9G,QAAM,EAAE,KAAK,SAAS,IAAI,MAAM,IAAI;AAEpC,QAAM,MAAc,EAAE,SAAS,GAAG;AAElC,QAAM,aAAa,CAAC,UAAkB,iBAA+B;AACnE,QAAI,iBAAiB,GAAI;AAEzB,UAAM,eAAgB,WAAW,UAAU,QAAQ,KAAK,IAAI,KAAK,EAAG,YAAY;AAChF,UAAM,aACJ,WAAW,UAAU,QAAQ,KAC7B,WAAW,UAAU,QAAQ,KAC7B,WAAW,UAAU,QAAQ,KAC7B,IACA,KAAK;AAEP,QAAI,YAAY,SAAS,IAAI,GAAG;AAC9B,UAAI,SAAS,EAAE,OAAO,GAAG,WAAW,OAAO,QAAQ,KAAK;AAAA,IAC1D;AACA,QAAI,YAAY,SAAS,KAAK,GAAG;AAC/B,UAAI,SAAS,EAAE,OAAO,GAAG,WAAW,OAAO,QAAQ,MAAM;AAAA,IAC3D;AAEA,UAAM,cAAc,iBAAiB,SAAS;AAC9C,QAAI,eAAe,YAAY,SAAS,IAAI,GAAG;AAC7C,UAAI,KAAK;AAAA,QACP,SAAS;AAAA,QACT,aAAa;AAAA,QACb,GAAI,cAAc,EAAE,MAAM,YAAY,IAAI,CAAC;AAAA,MAC7C;AAAA,IACF;AAEA,QAAI,YAAY,SAAS,KAAK,GAAG;AAC/B,UAAI,UAAU,EAAE,UAAU,MAAM,WAAW,MAAM;AAAA,IACnD;AAAA,EACF;AAGA,MAAI,IAAI,SAAS,iBAAiB,GAAG;AACnC,UAAM,oBAAoB,IAAI,SAAS,8CAA8C;AACrF,eAAW,SAAS,mBAAmB;AACrC,YAAM,WAAW,MAAM,CAAC,KAAK;AAC7B,YAAM,cAAc,WAAW,UAAU,WAAW;AACpD,YAAM,eAAe,gBAAgB,SAAY,OAAO,WAAW,IAAI;AACvE,iBAAW,UAAU,YAAY;AAAA,IACnC;AACA,WAAO;AAAA,EACT;AAGA,aAAW,KAAK,EAAE;AAClB,SAAO;AACT;;;AChEA,IAAM,qBAAqB,CAAC,MAA8C;AACxE,MAAI,MAAM,OAAW,QAAO;AAC5B,QAAM,IAAI,EAAE,KAAK,EAAE,YAAY;AAC/B,MAAI,MAAM,OAAQ,QAAO;AACzB,MAAI,MAAM,QAAS,QAAO;AAC1B,QAAM,IAAI,OAAO,CAAC;AAClB,SAAO,OAAO,SAAS,CAAC,IAAI,IAAI;AAClC;AAEA,IAAM,eAAe,CAAC,MAAmC;AACvD,MAAI,MAAM,OAAW,QAAO;AAC5B,QAAM,IAAI,EAAE,KAAK,EAAE,YAAY;AAC/B,SAAO,MAAM,OAAO,MAAM;AAC5B;AAEO,IAAM,sBAAsB,CAAC,WAAuD;AACzF,QAAM,EAAE,KAAK,QAAQ,IAAI;AAEzB,QAAM,SAAS,WAAW,KAAK,QAAQ;AACvC,QAAM,YAAY,WAAW,KAAK,YAAY;AAC9C,QAAM,cAAc,WAAW,KAAK,kBAAkB;AACtD,QAAM,WAAW,WAAW,KAAK,UAAU;AAC3C,QAAM,cAAc,WAAW,KAAK,gBAAgB;AAEpD,QAAM,QAA2B,EAAE,QAAQ;AAE3C,QAAM,UAAU,mBAAmB,MAAM;AACzC,MAAI,YAAY,OAAW,OAAM,SAAS;AAE1C,MAAI,cAAc,QAAW;AAC3B,UAAM,IAAI,OAAO,SAAS;AAC1B,QAAI,OAAO,SAAS,CAAC,EAAG,OAAM,YAAY;AAAA,EAC5C;AAEA,QAAM,eAAe,mBAAmB,WAAW;AACnD,MAAI,iBAAiB,OAAW,OAAM,cAAc;AAEpD,QAAM,YAAY,mBAAmB,QAAQ;AAC7C,MAAI,cAAc,OAAW,OAAM,WAAW;AAE9C,QAAM,eAAe,mBAAmB,WAAW;AACnD,MAAI,iBAAiB,OAAW,OAAM,cAAc;AAEpD,SAAO;AAAA,IACL,SAAS,aAAa,MAAM;AAAA,IAC5B;AAAA,EACF;AACF;;;AC1CO,IAAM,iCAAiC,OAAO,WASlB;AACjC,QAAM,QAAQ,OAAO;AAErB,QAAM,yCAAyC;AAC/C,MAAI;AACF,UAAM,MAAM,MAAM,kCAAkC;AAAA,MAClD,SAAS,MAAM,OAAO,QAAQ;AAAA,IAChC,CAAC;AACD,UAAM,2BAA2B,OAAO,mBAAmB,EAAE;AAC7D,QAAI,IAAK,QAAO;AAAA,EAClB,SAAS,GAAG;AACV,UAAM,qBAAqB,kBAAkB,CAAC,CAAC,EAAE;AAAA,EACnD;AAEA,MAAI,OAAO,cAAc,OAAO;AAC9B,UAAM,4CAA4C;AAClD,QAAI;AACF,YAAM,MAAM,MAAM,wBAAwB;AAAA,QACxC,OAAO,MAAM,OAAO,SAAS;AAAA,QAC7B,QAAQ,MAAM,OAAO,UAAU;AAAA,QAC/B,YAAY,MAAM,OAAO,cAAc;AAAA,MACzC,CAAC;AACD,YAAM,6BAA6B,OAAO,QAAQ,EAAE;AACpD,UAAI,IAAK,QAAO;AAAA,IAClB,SAAS,GAAG;AACV,YAAM,wCAAwC,kBAAkB,CAAC,CAAC,EAAE;AAAA,IACtE;AAAA,EACF,OAAO;AACL,UAAM,iDAAiD;AAAA,EACzD;AAEA,QAAM,kDAAkD;AACxD,MAAI;AACF,UAAM,MAAM,MAAM,mCAAmC;AAAA,MACnD,SAAS,CAAC,MAAM,OAAO,QAAQ,CAAC;AAAA,IAClC,CAAC;AACD;AAAA,MACE,MAAM,6BAA6B,GAAG,KAAK;AAAA,IAC7C;AACA,QAAI,IAAK,QAAO;AAAA,EAClB,SAAS,GAAG;AACV,UAAM,qBAAqB,kBAAkB,CAAC,CAAC,EAAE;AAAA,EACnD;AAEA,SAAO;AACT;;;AC3DO,IAAM,4BAA4B,CAAC,QAA+B;AACvE,QAAM,SAAS,WAAW,KAAK,QAAQ;AACvC,QAAM,QAAQ,WAAW,KAAK,OAAO;AACrC,QAAM,SAAS,WAAW,KAAK,QAAQ;AACvC,QAAM,iBAAiB,WAAW,KAAK,gBAAgB;AAEvD,QAAM,SAAwB;AAAA,IAC5B,SAAS,WAAW,OAAO,UAAU,OAAO,WAAW;AAAA,EACzD;AAEA,MAAI,mBAAmB,QAAW;AAChC,WAAO,aAAa,OAAO,cAAc;AAAA,EAC3C;AAEA,SAAO;AACT;AAEO,IAAM,gCAAgC,CAC3C,SACA,OACW;AAGX,SAAO,yBAAyB,SAAS,KAAK,IAAI,GAAG,KAAK,MAAM,CAAC;AACnE;AAEO,IAAM,0BAA0B,CAAC,KAAa,OAAwB;AAC3E,MAAI,cAAc;AAClB,QAAM,MAAM,KAAK,IAAI;AAGrB,MAAI,2BAA2B,KAAK,WAAW,GAAG;AAChD,kBAAc,YAAY;AAAA,MACxB;AAAA,MACA,WAAW,GAAG;AAAA,IAChB;AAAA,EACF;AACA,MAAI,yBAAyB,KAAK,WAAW,GAAG;AAC9C,kBAAc,YAAY;AAAA,MACxB;AAAA,MACA,UAAU,GAAG;AAAA,IACf;AAAA,EACF;AACA,MAAI,2BAA2B,KAAK,WAAW,GAAG;AAChD,kBAAc,YAAY;AAAA,MACxB;AAAA,MACA,WAAW,GAAG;AAAA,IAChB;AAAA,EACF;AAEA,MAAI,iCAAiC,KAAK,WAAW,GAAG;AACtD,kBAAc,YAAY;AAAA,MACxB;AAAA,MACA,cAAc,GAAG;AAAA,IACnB;AAAA,EACF;AAEA,SAAO;AACT;AAEO,IAAM,+BAA+B,CAC1C,KACA,eACW;AACX,MAAI,cAAc;AAElB,MAAI,2CAA2C,KAAK,WAAW,GAAG;AAChE,kBAAc,YAAY;AAAA,MACxB;AAAA,MACA,mBAAmB,UAAU;AAAA,IAC/B;AAAA,EACF;AAGA,MAAI,2BAA2B,KAAK,WAAW,GAAG;AAChD,kBAAc,YAAY;AAAA,MACxB;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAmBO,IAAM,6BAA6B,CACxC,QACwB;AACxB,QAAM,YAAY,WAAW,KAAK,WAAW;AAC7C,QAAM,SAAS,WAAW,KAAK,QAAQ;AACvC,QAAM,iBAAiB,WAAW,KAAK,gBAAgB;AACvD,QAAM,eAAe,WAAW,KAAK,UAAU;AAC/C,QAAM,aAAa,WAAW,KAAK,YAAY;AAE/C,QAAM,SAA8B;AAAA,IAClC,oBAAoB,cAAc;AAAA,IAClC,SAAS,WAAW;AAAA,EACtB;AAEA,MAAI,mBAAmB,QAAW;AAChC,WAAO,aAAa,OAAO,cAAc;AAAA,EAC3C;AACA,MAAI,iBAAiB,QAAW;AAC9B,WAAO,WAAW,OAAO,YAAY;AAAA,EACvC;AACA,MAAI,eAAe,QAAW;AAC5B,WAAO,aAAa;AAAA,EACtB;AAEA,SAAO;AACT;AAMO,IAAM,+BAA+B,CAC1C,KACA,OACW;AACX,MAAI,cAAc;AAClB,QAAM,MAAM,KAAK,IAAI;AAGrB,MAAI,iCAAiC,KAAK,WAAW,GAAG;AACtD,kBAAc,YAAY;AAAA,MACxB;AAAA,MACA,cAAc,GAAG;AAAA,IACnB;AAAA,EACF;AACA,MAAI,2BAA2B,KAAK,WAAW,GAAG;AAChD,kBAAc,YAAY;AAAA,MACxB;AAAA,MACA,WAAW,GAAG;AAAA,IAChB;AAAA,EACF;AAEA,SAAO;AACT;AAKO,IAAM,+BAA+B,CAC1C,KACA,aAKW;AACX,MAAI,cAAc;AAElB,MAAI,SAAS,aAAa,QAAW;AACnC,QAAI,+BAA+B,KAAK,WAAW,GAAG;AACpD,oBAAc,YAAY;AAAA,QACxB;AAAA,QACA,aAAa,SAAS,QAAQ;AAAA,MAChC;AAAA,IACF;AAAA,EACF;AAEA,MAAI,SAAS,eAAe,QAAW;AACrC,QAAI,mCAAmC,KAAK,WAAW,GAAG;AACxD,oBAAc,YAAY;AAAA,QACxB;AAAA,QACA,eAAe,SAAS,UAAU;AAAA,MACpC;AAAA,IACF;AAAA,EACF;AAEA,MAAI,SAAS,eAAe,QAAW;AACrC,QAAI,2CAA2C,KAAK,WAAW,GAAG;AAChE,oBAAc,YAAY;AAAA,QACxB;AAAA,QACA,mBAAmB,SAAS,UAAU;AAAA,MACxC;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;;;A3BoHO,IAAM,+BAA+B,oBAAI,IAAY;AAAA,EAC1D;AAAA,EACA;AACF,CAAC;AAEM,IAAM,iCAAiC,oBAAI,IAAY;AAAA,EAC5D;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAEM,IAAM,mBAAmB,oBAAI,IAAY;AAAA,EAC9C,GAAG;AAAA,EACH,GAAG;AACL,CAAC;AAEM,IAAM,mBAAmB,CAAC,UAA2B;AAC1D,SACE,iBAAiB,IAAI,KAAK,KAAK,MAAM,YAAY,EAAE,SAAS,UAAU;AAE1E;AAMO,IAAM,sBAAgC,CAAC,OAAO,YAAY,SAAS;AAOnE,IAAM,yBAAmC;AAAA,EAC9C;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AACF;AAQO,IAAM,gBAAgB,CAAC,UAA4B;AACxD,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,aAAa,MAAM,KAAK;AAC9B,QAAM,QAAQ,WAAW,YAAY;AAGrC,MAAI,oBAAoB,SAAS,KAAK,EAAG,QAAO;AAGhD,SAAO,uBAAuB,KAAK,CAAC,YAAY,QAAQ,KAAK,UAAU,CAAC;AAC1E;AAEO,IAAM,qBAAN,MAAM,oBAAmB;AAAA,EACrB;AAAA,EACA;AAAA,EACQ;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA;AAAA;AAAA;AAAA,EAKT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA;AAAA,EAEA;AAAA,EACS,+BAAyC,CAAC;AAAA,EACnD;AAAA,EACA;AAAA,EACS,uBAAuB,oBAAI,IAE1C;AAAA,EACM,wBAAwB;AAAA,EACxB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACS,mCAAmC,IAAI;AAAA,EAChD;AAAA,EACA;AAAA,EACS,qCAAqC,oBAAI,IAGxD;AAAA,EACe,8BAA8B;AAAA,EACvC;AAAA,EACA;AAAA,EACA,yBAAyB;AAAA,EACzB,+BAA+B;AAAA,EACtB,qBAAqB,oBAAI,IAA+B;AAAA,EACxD,6BAA6B,oBAAI,IAGhD;AAAA,EACM,cAAc,oBAAI,IAAwB;AAAA;AAAA,EACjC,qBAAqB,oBAAI,IAAoB;AAAA,EAC7C,0BAA0B,oBAAI,IAG7C;AAAA;AAAA;AAAA;AAAA;AAAA,EAMe,0BAA0B,oBAAI,IAG7C;AAAA,EACF,OAAwB,4BAA4B,IAAI,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAU5C,mBAAmB,oBAAI,IAOtC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMF,8BAAiE;AAC/D,WAAO;AAAA,MACL,OAAO,KAAK,iBAAiB;AAAA,MAC7B,MAAM,MAAM,KAAK,KAAK,iBAAiB,KAAK,CAAC;AAAA,IAC/C;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,kBAAqD,oBAAI,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAW7D,wCACN,SACoB;AACpB,UAAM,KAAK,OAAO,OAAO;AACzB,QAAI,CAAC,OAAO,SAAS,EAAE,EAAG,QAAO;AAGjC,QAAI,KAAK,gBAAgB,IAAI,EAAE,EAAG,QAAO;AAEzC,UAAM,aAAa,KAAK;AACxB,UAAM,aAAa;AAEnB,QAAI;AACJ,QAAI;AAEJ,eAAW,CAAC,iBAAiB,IAAI,KAAK,KAAK,gBAAgB,QAAQ,GAAG;AACpE,YAAM,MAAM,KAAK;AACjB,UAAI,OAAO,QAAQ,YAAY,CAAC,OAAO,SAAS,GAAG,EAAG;AACtD,UAAI,QAAQ,WAAY,YAAW;AACnC,UAAI,QAAQ,WAAY,YAAW;AAAA,IACrC;AAEA,WAAO,YAAY;AAAA,EACrB;AAAA,EAEQ,mCACN,SACkC;AAClC,UAAM,SAAS,KAAK,gBAAgB,IAAI,OAAO;AAC/C,QAAI,OAAQ,QAAO;AAEnB,UAAM,WAAW,KAAK,wCAAwC,OAAO;AACrE,QAAI,YAAY,KAAM,QAAO;AAC7B,WAAO,KAAK,gBAAgB,IAAI,QAAQ;AAAA,EAC1C;AAAA;AAAA,EAGA,4CACE,SACmC;AACnC,UAAM,OAAO,KAAK,mCAAmC,OAAO;AAC5D,QAAI,CAAC,KAAM,QAAO;AAClB,UAAM,cAAc,KAAK,cAAc,KAAK,OAAO,YAAY;AAC/D,QAAI,eAAe,OAAQ,QAAO;AAClC,WAAO;AAAA,MACL,MAAM,KAAK;AAAA,MACX,KAAK,KAAK;AAAA,MACV,OAAO,KAAK;AAAA,MACZ,GAAI,OAAO,KAAK,UAAU,WAAW,EAAE,OAAO,KAAK,MAAM,IAAI,CAAC;AAAA,MAC9D,GAAI,KAAK,eAAe,SACpB,EAAE,eAAe,KAAK,cAAc,IACpC,CAAC;AAAA,MACL,GAAI,KAAK,YAAY,EAAE,WAAW,KAAK,UAAU,IAAI,CAAC;AAAA,MACtD,GAAI,KAAK,iBAAiB,EAAE,gBAAgB,KAAK,eAAe,IAAI,CAAC;AAAA,MACrE,GAAI,OAAO,KAAK,YAAY,YAAY,EAAE,SAAS,KAAK,QAAQ,IAAI,CAAC;AAAA,MACrE,GAAI,OAAO,KAAK,mBAAmB,YAC/B,EAAE,gBAAgB,KAAK,eAAe,IACtC,CAAC;AAAA,MACL,GAAI,OAAO,KAAK,WAAW,YAAY,EAAE,QAAQ,KAAK,OAAO,IAAI,CAAC;AAAA,MAClE,GAAI,OAAO,KAAK,aAAa,YACzB,EAAE,UAAU,KAAK,SAAS,IAC1B,CAAC;AAAA,MACL,GAAI,KAAK,aAAa,EAAE,YAAY,KAAK,WAAW,IAAI,CAAC;AAAA,MACzD,GAAI,OAAO,KAAK,gBAAgB,WAC5B,EAAE,aAAa,KAAK,YAAY,IAChC,CAAC;AAAA,IACP;AAAA,EACF;AAAA;AAAA,EAGiB,oBAAoB,oBAAI,IAGvC;AAAA,EAEM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,kBAAkB,oBAAI,IAAkC;AAAA;AAAA;AAAA;AAAA,EAKxD,kBAAyC,CAAC;AAAA,EAC1C,4BAA4B;AAAA;AAAA;AAAA;AAAA;AAAA,EAM5B,0BAA0B,oBAAI,IAA8B;AAAA;AAAA;AAAA;AAAA;AAAA,EAM5D,cAEH,CAAC;AAAA,EACE,wBAAwB;AAAA;AAAA,EAGf,qBAAqB;AAAA,EAC9B,oBAAoB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOpB,6BACN;AAAA,EACM,0BAIH,CAAC;AAAA;AAAA;AAAA;AAAA,EAKN,MAAc,qBAAoC;AAChD,QAAI,KAAK,sBAAuB;AAChC,SAAK,wBAAwB;AAE7B,WAAO,KAAK,YAAY,SAAS,GAAG;AAClC,YAAM,OAAO,KAAK,YAAY,MAAM;AACpC,UAAI,MAAM;AAER,cAAM,sBAAsB,KAAK,IAAI,IAAI,KAAK;AAC9C,YAAI,sBAAsB,KAAK,oBAAoB;AACjD,gBAAM,IAAI;AAAA,YAAQ,CAAC,MACjB,WAAW,GAAG,KAAK,qBAAqB,mBAAmB;AAAA,UAC7D;AAAA,QACF;AAEA,cAAM,KAAK,QAAQ;AAGnB,aAAK,oBAAoB,KAAK,IAAI;AAAA,MACpC;AAAA,IACF;AAEA,SAAK,wBAAwB;AAAA,EAC/B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,uBACN,WACA,UACY;AAEZ,QAAI,UAAU;AACZ,YAAM,WAAW,KAAK,wBAAwB,IAAI,QAAQ;AAC1D,UAAI,UAAU;AACZ,aAAK,QAAQ;AAAA,UACX,+CAA+C,QAAQ;AAAA,QACzD;AACA,eAAO;AAAA,MACT;AAAA,IACF;AAGA,QAAI;AACJ,QAAI;AACJ,UAAM,UAAU,IAAI,QAAW,CAAC,SAAS,WAAW;AAClD,uBAAiB;AACjB,sBAAgB;AAAA,IAClB,CAAC;AAGD,QAAI,UAAU;AACZ,WAAK,wBAAwB,IAAI,UAAU,OAAO;AAClD,cAAQ,QAAQ,MAAM;AACpB,aAAK,wBAAwB,OAAO,QAAQ;AAAA,MAC9C,CAAC;AAAA,IACH;AAGA,SAAK,YAAY,KAAK;AAAA,MACpB,SAAS,YAAY;AACnB,YAAI;AACF,gBAAM,SAAS,MAAM,UAAU;AAC/B,yBAAe,MAAM;AAAA,QACvB,SAAS,GAAG;AACV,wBAAc,CAAC;AAAA,QACjB;AAAA,MACF;AAAA,IACF,CAAC;AAGD,SAAK,KAAK,mBAAmB;AAE7B,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWQ,gCACN,OAC6C;AAC7C,QAAI;AACJ,QAAI;AACJ,UAAM,UAAU,IAAI;AAAA,MAClB,CAAC,SAAS,WAAW;AACnB,yBAAiB;AACjB,wBAAgB;AAAA,MAClB;AAAA,IACF;AAGA,SAAK,YAAY,KAAK;AAAA,MACpB,SAAS,MAAM;AACb,eAAO,IAAI,QAAc,CAAC,gBAAgB;AAExC,gBAAM,EACH,KAAK,CAAC,WAAW;AAEhB,2BAAe;AAAA,cACb;AAAA,cACA,SAAS,MAAM,YAAY;AAAA,YAC7B,CAAC;AAAA,UACH,CAAC,EACA,MAAM,CAAC,MAAM;AAEZ,0BAAc,CAAC;AACf,wBAAY;AAAA,UACd,CAAC;AAAA,QACL,CAAC;AAAA,MACH;AAAA,IACF,CAAC;AAGD,SAAK,KAAK,mBAAmB;AAE7B,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,gCACN,UAC2B;AAE3B,UAAM,WAAW,SAAS,MAAM,GAAG,EAAE,IAAI,KAAK;AAE9C,UAAM,SAAS,SAAS,UAAU,GAAG,EAAE,EAAE,YAAY;AACrD,WAAO,OAAO,SAAS,GAAG,IAAI,cAAc;AAAA,EAC9C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQiB,0BAA0B,oBAAI,IAG7C;AAAA;AAAA;AAAA;AAAA,EAKF,MAAc,yBAAwC;AACpD,QAAI,KAAK,6BAA6B,KAAK,gBAAgB,WAAW,GAAG;AACvE;AAAA,IACF;AAEA,SAAK,4BAA4B;AAEjC,WAAO,KAAK,gBAAgB,SAAS,GAAG;AACtC,YAAM,OAAO,KAAK,gBAAgB,MAAM;AACxC,UAAI,CAAC,KAAM;AACX,YAAM,KAAK,IAAI;AAAA,IACjB;AAEA,SAAK,4BAA4B;AAAA,EACnC;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,2BACZ,WACY;AACZ,WAAO,IAAI,QAAW,CAAC,SAAS,WAAW;AACzC,WAAK,gBAAgB,KAAK;AAAA,QACxB,KAAK,YAAY;AACf,cAAI;AACF,oBAAQ,MAAM,UAAU,CAAC;AAAA,UAC3B,SAAS,GAAG;AACV,mBAAO,CAAC;AAAA,UACV;AAAA,QACF;AAAA,MACF,CAAC;AACD,WAAK,KAAK,uBAAuB;AAAA,IACnC,CAAC;AAAA,EACH;AAAA,EAEQ,uBAAuB,KAAK,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAazC,MAAc,uBACZ,YACA,QAIC;AAGD,UAAM,WAAW,KAAK,iBAAiB,IAAI,UAAU;AACrD,QAAI,UAAU;AACZ,cAAQ;AAAA,QACN,iDAAiD,UAAU;AAAA,MAC7D;AACA,WAAK,iBAAiB,OAAO,UAAU;AAEvC,eAAS,OACN,MAAM,EAAE,QAAQ,6BAA6B,CAAC,EAC9C,MAAM,CAAC,MAAM;AACZ,gBAAQ,QAAQ,+CAA+C,CAAC,EAAE;AAAA,MACpE,CAAC;AAAA,IACL;AAGA,YAAQ,QAAQ,6CAA6C,UAAU,EAAE;AACzE,UAAM,kBAAkB,IAAI,eAAe;AAAA,MACzC,MAAM,KAAK;AAAA,MACX,UAAU,KAAK;AAAA,MACf,UAAU,KAAK;AAAA,MACf,QAAQ,UAAU,KAAK;AAAA,MACvB,cAAc,KAAK,OAAO,iBAAiB;AAAA,IAC7C,CAAC;AAGD,UAAM,gBAAgB,MAAM;AAE5B,SAAK,iBAAiB,IAAI,YAAY;AAAA,MACpC,QAAQ;AAAA,MACR,UAAU;AAAA;AAAA,MACV,WAAW,KAAK,IAAI;AAAA,IACtB,CAAC;AAED,WAAO;AAAA,MACL,QAAQ;AAAA,MACR,SAAS,MAAM,KAAK,uBAAuB,YAAY,MAAM;AAAA,IAC/D;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAc,uBACZ,YACA,QACe;AACf,UAAM,QAAQ,KAAK,iBAAiB,IAAI,UAAU;AAClD,QAAI,CAAC,MAAO;AAGZ,SAAK,iBAAiB,OAAO,UAAU;AACvC,YAAQ,QAAQ,2CAA2C,UAAU,EAAE;AAEvE,QAAI;AACF,YAAM,MAAM,OAAO,MAAM,EAAE,QAAQ,0BAA0B,CAAC;AAAA,IAChE,SAAS,GAAG;AACV,cAAQ,QAAQ,2CAA2C,CAAC,EAAE;AAAA,IAChE;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAyBA,MAAM,uBACJ,YACA,QAIC;AACD,WAAO,MAAM,KAAK,uBAAuB,YAAY,MAAM;AAAA,EAC7D;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,0BAAyC;AACrD,UAAM,UAAU,MAAM,KAAK,KAAK,iBAAiB,QAAQ,CAAC;AAC1D,SAAK,iBAAiB,MAAM;AAE5B,UAAM,QAAQ;AAAA,MACZ,QAAQ,IAAI,OAAO,CAAC,KAAK,KAAK,MAAM;AAClC,YAAI;AACF,eAAK,QAAQ,QAAQ,sCAAsC,GAAG,EAAE;AAChE,gBAAM,MAAM,OAAO,MAAM,EAAE,QAAQ,cAAc,CAAC;AAAA,QACpD,QAAQ;AAAA,QAER;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AAAA,EAEQ,oBAAoB,KAA+B;AACzD,UAAM,WAAW,KAAK,OAAO,iBAAiB;AAC9C,QAAI,UAAU;AACZ,YAAM,MAAM,KAAK,OAAO,qBAAqB;AAC7C,YAAM,MAAM,MAAM,0BAA0B,GAAG,KAAK;AACpD;AAAA,QACE;AAAA,QACA,KAAK;AAAA,QACL;AAAA,QACA,iBAAiB,IAAI,IAAI,YAAY,IAAI,OAAO,cAAc,IAAI,SAAS;AAAA,MAC7E;AAAA,IACF;AAEA,eAAW,MAAM,KAAK,sBAAsB;AAC1C,UAAI;AAEF,aAAK,QAAQ,QAAQ,GAAG,GAAG,CAAC,EAAE,MAAM,CAAC,MAAe;AAClD,WAAC,KAAK,OAAO,QAAQ,KAAK,OAAO,OAAO;AAAA,YACtC,KAAK;AAAA,YACL;AAAA,YACA,kBAAkB,CAAC;AAAA,UACrB;AAAA,QACF,CAAC;AAAA,MACH,SAAS,GAAG;AAEV,SAAC,KAAK,OAAO,QAAQ,KAAK,OAAO,OAAO;AAAA,UACtC,KAAK;AAAA,UACL;AAAA,UACA,kBAAkB,CAAC;AAAA,QACrB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EAEA,YACE,MAWA;AACA,UAAM,MAAM,sBAAsB,KAAK,YAAY;AAEnD,SAAK,SAAS;AAAA,MACZ,KAAK;AAAA,MACL,IAAI,WACF,IAAI,qBACJ,IAAI,mBACJ,IAAI,aACJ,IAAI,eACJ,IAAI;AAAA,IACR;AACA,SAAK,SAAS,IAAI,eAAe,IAAI;AACrC,SAAK,OAAO,KAAK;AACjB,SAAK,WAAW,KAAK;AACrB,SAAK,WAAW,KAAK;AACrB,SAAK,MAAM,KAAK;AAChB,SAAK,aAAa,KAAK,cAAc;AACrC,SAAK,aAAa,IAAI,kBAAkB;AAAA,MACtC,MAAM,KAAK;AAAA,MACX,UAAU,KAAK;AAAA,MACf,UAAU,KAAK;AAAA,MACf,WAAW;AAAA,IACb,CAAC;AACD,SAAK,SAAS,IAAI,cAAc;AAAA,MAC9B,MAAM,KAAK;AAAA,MACX,UAAU,KAAK;AAAA,MACf,UAAU,KAAK;AAAA,MACf,QAAQ,KAAK;AAAA,MACb,aAAa,KAAK,OAAO,iBAAiB;AAAA,IAC5C,CAAC;AAGD,SAAK,OAAO,GAAG,SAAS,CAAC,UAAU;AACjC,YAAM,SAAS,iBAAiB,KAAK;AACrC,UAAI,CAAC,OAAQ;AAEb,WAAK,oBAAoB,MAAM;AAAA,IACjC,CAAC;AAGD,SAAK,OAAO,GAAG,eAAe,CAAC,QAAgB;AAC7C,UAAI;AACF,aAAK,yBAAyB,GAAG;AAAA,MACnC,SAAS,GAAY;AACnB,aAAK,OAAO;AAAA,UACV;AAAA,UACA,kBAAkB,CAAC;AAAA,QACrB;AAAA,MACF;AAAA,IACF,CAAC;AAGD,SAAK,OAAO,GAAG,QAAQ,CAAC,UAAU;AAChC,YAAM,QAAQ,MAAM,OAAO;AAC3B,UACE,UAAU,8BACV,UAAU,yBACV,UAAU,2BACV,UAAU,gCACV,UAAU,+BACV,UAAU,sCACV;AACA;AAAA,MACF;AAEA,UAAI;AACF,YAAI,MAAM,KAAK,WAAW,EAAG;AAC7B,cAAM,MAAM,KAAK,OAAO;AAAA,UACtB,MAAM;AAAA,UACN,MAAM,OAAO;AAAA,UACb,KAAK,OAAO;AAAA,QACd;AACA,YAAI,CAAC,OAAO,CAAC,IAAI,WAAW,OAAO,EAAG;AACtC,aAAK,0BAA0B,OAAO,KAAK,MAAM,OAAO,SAAS;AAAA,MACnE,SAAS,GAAY;AACnB,aAAK,OAAO;AAAA,UACV;AAAA,UACA,kBAAkB,CAAC;AAAA,QACrB;AAAA,MACF;AAAA,IACF,CAAC;AAED,UAAM,IAAI,KAAK;AACf,QAAI,OAAO,MAAM,YAAY,OAAO,SAAS,CAAC,KAAK,IAAI,GAAG;AACxD,WAAK,qCAAqC,KAAK,MAAM,CAAC;AACtD,WAAK,OAAO,GAAG,SAAS,MAAM;AAC5B,YAAI;AACF,eAAK,KAAK,6BAA6B;AAAA,QACzC,QAAQ;AAAA,QAER;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,WAAW,SAAkC;AACjD,UAAM,KAAK,KAAK,iBAAiB,OAAO;AACxC,WAAO,MAAM,KAAK,OAAO,WAAW,EAAE;AAAA,EACxC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASQ,sBACN,SACA,OACA,KACA,YACQ;AACR,WAAO,GAAG,OAAO,IAAI,MAAM,QAAQ,CAAC,IAAI,IAAI,QAAQ,CAAC,IAAI,UAAU;AAAA,EACrE;AAAA;AAAA;AAAA;AAAA,EAKQ,oBACN,SACA,OACA,KACA,YAC6B;AAC7B,UAAM,MAAM,KAAK,sBAAsB,SAAS,OAAO,KAAK,UAAU;AACtE,UAAM,SAAS,KAAK,gBAAgB,IAAI,GAAG;AAE3C,QAAI,CAAC,OAAQ,QAAO;AAEpB,UAAM,MAAM,KAAK,IAAI;AACrB,QAAI,MAAM,OAAO,WAAW,OAAO,OAAO;AAExC,WAAK,gBAAgB,OAAO,GAAG;AAC/B,aAAO;AAAA,IACT;AAEA,WAAO,OAAO;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA,EAKQ,gBACN,SACA,OACA,KACA,YACA,YACA,OACM;AACN,UAAM,MAAM,KAAK,sBAAsB,SAAS,OAAO,KAAK,UAAU;AACtE,SAAK,gBAAgB,IAAI,KAAK;AAAA,MAC5B;AAAA,MACA,UAAU,KAAK,IAAI;AAAA,MACnB,OAAO,SAAS,KAAK;AAAA,IACvB,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKA,uBAA6B;AAC3B,SAAK,gBAAgB,MAAM;AAAA,EAC7B;AAAA;AAAA;AAAA;AAAA,EAKA,6BACE,SACA,OACA,KACA,YACM;AACN,UAAM,MAAM,KAAK,sBAAsB,SAAS,OAAO,KAAK,UAAU;AACtE,SAAK,gBAAgB,OAAO,GAAG;AAAA,EACjC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,sBAAsB,OAAqB;AACzC,SAAK,uBAAuB;AAAA,EAC9B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcA,MAAM,+BACJ,QAC+C;AAC/C,WAAO,MAAM,+BAA+B;AAAA,MAC1C,GAAG;AAAA,MACH,KAAK;AAAA,MACL,QAAQ,KAAK;AAAA,MACb,MAAM,KAAK;AAAA,MACX,UAAU,KAAK;AAAA,MACf,UAAU,KAAK;AAAA,IACjB,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,sCACJ,QAOsD;AACtD,WAAO,MAAM,sCAAsC;AAAA,MACjD,GAAG;AAAA,MACH,KAAK;AAAA,MACL,QAAQ,OAAO,UAAU,KAAK;AAAA,MAC9B,MAAM,KAAK;AAAA,MACX,UAAU,KAAK;AAAA,MACf,UAAU,KAAK;AAAA,IACjB,CAAC;AAAA,EACH;AAAA,EAEA,MAAc,+BAA8C;AAC1D,UAAM,YAAY,KAAK;AACvB,QAAI,aAAa,KAAM;AAEvB,UAAM,OAAO,KAAK,OAAO,wBAAwB;AACjD,QAAI,CAAC,MAAM,UAAW;AAEtB,UAAM,MAAM,KAAK,IAAI;AACrB,UAAM,WAAW;AACjB,UAAM,SAAS,MAAM;AACrB,WACE,KAAK,6BAA6B,UAClC,KAAK,6BAA6B,CAAC,IAAK,QACxC;AACA,WAAK,6BAA6B,MAAM;AAAA,IAC1C;AACA,SAAK,6BAA6B,KAAK,GAAG;AAE1C,QAAI,KAAK,6BAA6B,SAAS,UAAW;AAE1D,QAAI,KAAK,8BAA+B;AACxC,UAAM,aAAa,KAAK;AACxB,QACE,KAAK,iCAAiC,QACtC,MAAM,KAAK,gCAAgC;AAE3C;AAEF,SAAK,gCAAgC;AACrC,KAAC,KAAK,OAAO,QAAQ,KAAK,OAAO,OAAO;AAAA,MACtC,KAAK;AAAA,MACL;AAAA,MACA;AAAA,QACE,WAAW,KAAK;AAAA,QAChB,QAAQ,KAAK;AAAA,QACb,8BAA8B,KAAK,6BAA6B;AAAA,QAChE;AAAA,QACA;AAAA,QACA;AAAA,QACA,QAAQ;AAAA,MACV;AAAA,IACF;AAEA,SAAK,gCAAgC,KAAK,0BAA0B,MAAM,EACvE,MAAM,CAAC,MAAM;AACZ,OAAC,KAAK,OAAO,QAAQ,KAAK,OAAO,OAAO;AAAA,QACtC,KAAK;AAAA,QACL;AAAA,QACA;AAAA,MACF;AAAA,IACF,CAAC,EACA,QAAQ,MAAM;AACb,WAAK,gCAAgC;AAAA,IACvC,CAAC;AAAA,EACL;AAAA,EAEA,MAAc,0BACZ,QACe;AACf,QAAI;AAEJ,QAAI,WAAW,UAAU,WAAW,YAAY;AAC9C,UAAI;AACF,cAAM,KAAK,OAAO;AAClB;AAAA,MACF,SAAS,GAAG;AACV,kBAAU;AACV,YAAI,WAAW,WAAY,OAAM;AAAA,MACnC;AAAA,IACF;AAEA,QAAI,WAAW,UAAU,WAAW,OAAO;AACzC,UAAI;AACF,cAAM,KAAK,OAAO,MAAM;AACxB,cAAM,KAAK,OAAO,OAAO;AACzB;AAAA,MACF,SAAS,GAAG;AACV,kBAAU;AACV,YAAI,WAAW,MAAO,OAAM;AAAA,MAC9B;AAAA,IACF;AAEA,UAAM,mBAAmB,QACrB,UACA,IAAI,MAAM,OAAO,WAAW,gCAAgC,CAAC;AAAA,EACnE;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,cACJ,UACe;AACf,SAAK,qBAAqB,IAAI,QAAQ;AACtC,UAAM,KAAK,4BAA4B;AACvC,SAAK,iCAAiC;AAAA,EACxC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,eACJ,UACe;AACf,QAAI,UAAU;AACZ,WAAK,qBAAqB,OAAO,QAAQ;AAAA,IAC3C,OAAO;AACL,WAAK,qBAAqB,MAAM;AAAA,IAClC;AAEA,QAAI,KAAK,qBAAqB,SAAS,GAAG;AACxC,WAAK,gCAAgC;AACrC,WAAK,sBAAsB;AAC3B,YAAM,KAAK,8BAA8B;AAAA,IAC3C,OAAO;AAEL,YAAM,QAAQ,KAAK,OAAO,eAAe,MAAM;AAC/C,UAAI,OAAO;AACT,aAAK,uBAAuB;AAAA,MAC9B,WAAW,KAAK,OAAO,wBAAwB,GAAG;AAChD,aAAK,kBAAkB;AAAA,MACzB;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,mCAAyC;AAC/C,QAAI,KAAK,4BAA6B;AACtC,QAAI,KAAK,qBAAqB,SAAS,EAAG;AAE1C,SAAK,8BAA8B,YAAY,MAAM;AAEnD,WAAK,KAAK,6BAA6B;AAAA,IACzC,GAAG,KAAK,gCAAgC;AAAA,EAC1C;AAAA,EAEQ,kCAAwC;AAC9C,QAAI,CAAC,KAAK,4BAA6B;AACvC,kBAAc,KAAK,2BAA2B;AAC9C,SAAK,8BAA8B;AAAA,EACrC;AAAA,EAEA,MAAc,+BAA8C;AAC1D,QAAI,KAAK,qBAAqB,SAAS,EAAG;AAC1C,QAAI,KAAK;AACP,aAAO,MAAM,KAAK;AAEpB,SAAK,kCAAkC,YAAY;AACjD,UAAI;AACF,cAAM,KAAK,gBAAgB;AAC3B,aAAK,wBAAwB;AAC7B,SAAC,KAAK,OAAO,SAAS,KAAK,OAAO,KAAK;AAAA,UACrC,KAAK;AAAA,UACL;AAAA,UACA;AAAA,YACE,YAAY,KAAK;AAAA,UACnB;AAAA,QACF;AAAA,MACF,SAAS,GAAG;AACV,SAAC,KAAK,OAAO,SAAS,KAAK,OAAO,KAAK;AAAA,UACrC,KAAK;AAAA,UACL;AAAA,UACA;AAAA,QACF;AAAA,MACF;AAAA,IACF,GAAG,EAAE,QAAQ,MAAM;AACjB,WAAK,iCAAiC;AAAA,IACxC,CAAC;AAED,WAAO,MAAM,KAAK;AAAA,EACpB;AAAA,EAEA,MAAc,8BAA6C;AACzD,QAAI,KAAK,qBAAqB,SAAS,EAAG;AAC1C,QAAI,KAAK,sBAAuB;AAChC,QAAI,KAAK;AACP,aAAO,MAAM,KAAK;AAEpB,SAAK,gCAAgC,YAAY;AAG/C,UAAI,CAAC,KAAK,OAAO,YAAY;AAC3B,cAAM,KAAK,gBAAgB;AAAA,MAC7B;AACA,WAAK,wBAAwB;AAI7B,YAAM,QAAQ,KAAK,OAAO,eAAe,MAAM;AAC/C,UAAI,OAAO;AAGT,aAAK,uBAAuB;AAAA,MAC9B,WAAW,KAAK,OAAO,wBAAwB,GAAG;AAChD,cAAM,UAAU,KAAK,OAAO,uBAAuB,KAAK;AAExD,cAAM,KAAK,6BAA6B,OAAO;AAG/C,aAAK,kBAAkB;AAAA,MACzB;AAAA,IACF,GAAG,EAAE,QAAQ,MAAM;AACjB,WAAK,+BAA+B;AAAA,IACtC,CAAC;AAED,WAAO,MAAM,KAAK;AAAA,EACpB;AAAA,EAEA,MAAc,gCAA+C;AAC3D,QAAI,CAAC,KAAK,yBAAyB,CAAC,KAAK,OAAO,WAAY;AAC5D,QAAI,KAAK;AACP,aAAO,MAAM,KAAK;AAEpB,QAAI,KAAK,8BAA8B;AACrC,UAAI;AACF,cAAM,KAAK;AAAA,MACb,QAAQ;AAAA,MAER;AAAA,IACF;AAEA,SAAK,kCAAkC,YAAY;AACjD,YAAM,KAAK,kBAAkB;AAC7B,WAAK,wBAAwB;AAG7B,WAAK,gCAAgC;AAGrC,WAAK,iBAAiB;AAGtB,WAAK,sBAAsB;AAAA,IAC7B,GAAG,EAAE,QAAQ,MAAM;AACjB,WAAK,iCAAiC;AAAA,IACxC,CAAC;AAED,WAAO,MAAM,KAAK;AAAA,EACpB;AAAA,EAEQ,iBAAiB,SAAiC;AACxD,WAAO,WAAW,OAAO,IAAI;AAAA,EAC/B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,kBAAmC;AACvC,QAAI,KAAK,kBAAkB,QAAW;AACpC,aAAO,KAAK;AAAA,IACd;AAEA,QAAI;AACF,YAAM,UAAU,MAAM,KAAK,kBAAkB,CAAC;AAC9C,YAAM,aACH,SAAqC,kBACtC,OAAQ,QAAoC,mBAAmB,WACzD,QAAoD,gBAClD,aACJ;AAEN,UAAI,eAAe,QAAW;AAC5B,aAAK,gBACH,OAAO,eAAe,WAClB,OAAO,SAAS,YAAY,EAAE,IAC9B;AAAA,MACR;AAAA,IACF,QAAQ;AAAA,IAER;AAGA,QACE,KAAK,kBAAkB,UACvB,CAAC,OAAO,SAAS,KAAK,aAAa,GACnC;AACA,WAAK,gBAAgB;AAAA,IACvB;AAEA,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,cAAgC;AACpC,UAAM,eAAe,MAAM,KAAK,gBAAgB;AAChD,QAAI,eAAe,EAAG,QAAO;AAI7B,QAAI;AACF,YAAM,OAAO,MAAM,KAAK,QAAQ,QAAW;AAAA,QACzC,MAAM,CAAC,MAAM;AAAA,QACb,WAAW;AAAA,MACb,CAAC;AACD,UAAI,cAAc,KAAK,IAAI,GAAG;AAC5B,aAAK,OAAO;AAAA,UACV,2CAA2C,KAAK,IAAI;AAAA,QACtD;AACA,eAAO;AAAA,MACT;AAAA,IACF,QAAQ;AAAA,IAER;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,MACJ,eACe;AACf,UAAM,KAAK,OAAO,MAAM,aAAa;AAAA,EACvC;AAAA,EAEA,MAAM,MAAM,SAA8C;AAExD,SAAK,iBAAiB;AACtB,SAAK,sBAAsB;AAE3B,UAAM,KAAK,QAAQ;AAEnB,UAAM,KAAK,wBAAwB;AACnC,UAAM,KAAK,OAAO;AAAA,MAChB,SAAS,SAAS,EAAE,QAAQ,QAAQ,OAAO,IAAI;AAAA,IACjD;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,UAAyB;AAC7B,UAAM,UAAU,MAAM,KAAK,KAAK,WAAW;AAC3C,SAAK,YAAY,MAAM;AAGvB,UAAM,QAAQ;AAAA,MACZ,QAAQ,IAAI,OAAO,WAAW;AAC5B,YAAI;AACF,gBAAM,OAAO,KAAK;AAAA,QACpB,SAAS,OAAO;AACd,eAAK,OAAO;AAAA,YACV;AAAA,YACA;AAAA,UACF;AAAA,QACF;AAAA,MACF,CAAC;AAAA,IACH;AAEA,QAAI,QAAQ,SAAS,GAAG;AACtB,WAAK,OAAO;AAAA,QACV,mCAAmC,QAAQ,MAAM;AAAA,MACnD;AAAA,IACF;AAAA,EACF;AAAA;AAAA,EAGA,MAAM,QACJ,QACA,QAAQ,GACS;AAEjB,QAAI,CAAC,KAAK,OAAO,UAAU;AACzB,YAAM,KAAK,OAAO,MAAM;AAAA,IAC1B;AAGA,UAAM,QAAQ,MAAM,KAAK,OAAO,UAAU,MAAM;AAChD,QAAI,MAAM,OAAO,iBAAiB,KAAK;AACrC,aAAO,MAAM,KAAK,iBAAiB,QAAQ,OAAO,KAAK;AAAA,IACzD;AAGA,QAAI,MAAM,KAAK,WAAW,EAAG,QAAO;AACpC,WAAO,KAAK,OAAO;AAAA,MACjB,MAAM;AAAA,MACN,MAAM,OAAO;AAAA,MACb,KAAK,OAAO;AAAA,IACd;AAAA,EACF;AAAA,EAEQ,qBACN,QACA,SACS;AAQT,WACE,YAAY,MACX,OAAO,UAAU,gCAChB,OAAO,UAAU;AAAA;AAAA,IAGjB,OAAO,UAAU;AAAA;AAAA,IAGjB,OAAO,UAAU,8BACjB,OAAO,UAAU;AAAA,EAEvB;AAAA,EAEA,MAAc,iBACZ,QACA,OACA,OACiB;AACjB,UAAM,YAAY,MAAM,KAAK,WAAW;AACxC,UAAM,kBACJ;AAEF,QAAI,KAAK,qBAAqB,QAAQ,MAAM,KAAK,MAAM,GAAG;AACxD,YAAM,IAAI,MAAM,eAAe;AAAA,IACjC;AAOA,QAAI,QAAQ,GAAG;AACb,YAAM,QAAQ,IAAI;AAClB,aAAO,MAAM,KAAK,QAAQ,QAAQ,QAAQ,CAAC;AAAA,IAC7C;AAGA,QAAI,WAAW;AACb,YAAM,IAAI,MAAM,eAAe;AAAA,IACjC;AAGA,WAAO,KAAK,OAAO;AAAA,MACjB,MAAM;AAAA,MACN,MAAM,OAAO;AAAA,MACb,KAAK,OAAO;AAAA,IACd;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,eAAe,SAAwC;AAC3D,UAAM,MAAM,MAAM,KAAK,QAAQ;AAAA,MAC7B,OAAO;AAAA,MACP,GAAI,YAAY,SAAY,EAAE,QAAQ,IAAI,CAAC;AAAA,IAC7C,CAAC;AACD,WAAO,oBAAoB,GAAG;AAAA,EAChC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,UACJ,UAAU,GACV,SACe;AACf,UAAM,KAAK,OAAO,MAAM;AACxB,UAAM,KAAK,KAAK,iBAAiB,OAAO;AAExC,UAAM,QAAQ,KAAK,OAAO,eAAe,MAAM;AAC/C,UAAM,oBACJ,SAAS,sBAAsB,QAAQ,KAAK;AAE9C,UAAM,QAAQ,MAAM,KAAK,OAAO,UAAU;AAAA,MACxC,OAAO;AAAA,MACP,SAAS;AAAA,MACT,GAAI,qBAAqB,OAAO,EAAE,kBAAkB,IAAI,CAAC;AAAA,MACzD,YAAY;AAAA,MACZ,cAAc;AAAA,IAChB,CAAC;AAED,QAAI,MAAM,OAAO,iBAAiB,KAAK;AACrC,YAAM,IAAI;AAAA,QACR,oCAAoC,MAAM,OAAO,YAAY;AAAA,MAC/D;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,WACJ,YACA,UAAU,GACV,SACe;AACf,UAAM,KAAK,OAAO,MAAM;AACxB,UAAM,KAAK,KAAK,iBAAiB,OAAO;AAExC,UAAM,QAAQ,KAAK,OAAO,eAAe,MAAM;AAC/C,UAAM,oBACJ,SAAS,sBAAsB,QAAQ,KAAK;AAE9C,UAAM,UAAU,YAA6B;AAC3C,YAAM,QAAQ,MAAM,KAAK,OAAO,UAAU;AAAA,QACxC,OAAO;AAAA,QACP,SAAS;AAAA,QACT,GAAI,qBAAqB,OAAO,EAAE,kBAAkB,IAAI,CAAC;AAAA,QACzD;AAAA,QACA,cAAc;AAAA,MAChB,CAAC;AACD,aAAO,MAAM,OAAO;AAAA,IACtB;AAEA,UAAM,OAAO,MAAM,QAAQ;AAC3B,QAAI,SAAS,KAAK;AAChB,YAAM,KAAK;AAAA,QACT;AAAA,QACA,qBAAqB,OAAO,EAAE,kBAAkB,IAAI;AAAA,MACtD;AACA,YAAM,YAAY,MAAM,QAAQ;AAChC,UAAI,cAAc,KAAK;AACrB,cAAM,IAAI;AAAA,UACR,iDAAiD,SAAS;AAAA,QAC5D;AAAA,MACF;AACA;AAAA,IACF;AAEA,QAAI,SAAS,KAAK;AAChB,YAAM,IAAI,MAAM,qCAAqC,IAAI,GAAG;AAAA,IAC9D;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,kBACJ,UAAU,GACV,SAKsB;AACtB,QAAI,CAAC,KAAK,OAAO,SAAU,OAAM,KAAK,OAAO,MAAM;AAInD,UAAM,QAAQ,KAAK,OAAO,eAAe,MAAM;AAC/C,UAAM,oBAAoB,QAAQ,UAAU;AAE5C,UAAM,UAAU,MAAM,KAAK,eAAe,OAAO;AACjD,UAAM,EAAE,YAAY,KAAK,IAAI,gCAAgC;AAAA,MAC3D;AAAA,MACA;AAAA,IACF,CAAC;AACD,UAAM,wBAAwB;AAAA,MAC5B,QAAQ,KAAK;AAAA,MACb;AAAA,MACA;AAAA,MACA,GAAI,qBAAqB,OAAO,EAAE,kBAAkB,IAAI,CAAC;AAAA,IAC3D,CAAC;AAED,WAAO,0BAA0B;AAAA,MAC/B,QAAQ,KAAK;AAAA,MACb;AAAA,MACA,GAAI,qBAAqB,OAAO,EAAE,kBAAkB,IAAI,CAAC;AAAA,MACzD;AAAA,MACA,GAAI,SAAS,oBAAoB,OAC7B,EAAE,kBAAkB,QAAQ,iBAAiB,IAC7C,CAAC;AAAA,MACL,GAAI,SAAS,qBAAqB,OAC9B,EAAE,mBAAmB,QAAQ,kBAAkB,IAC/C,CAAC;AAAA,IACP,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA4BA,MAAM,2BACJ,UAAU,GACV,SASsB;AACtB,UAAM,SAAS,SAAS,UAAU,KAAK;AACvC,UAAM,gBAAgB,SAAS,iBAAiB;AAChD,UAAM,WAAW,SAAS,YAAY;AAGtC,UAAM,aAAa,QAAQ,QAAQ,MAAM,OAAO,IAAI,KAAK,IAAI,CAAC;AAE9D,YAAQ;AAAA,MACN,qCAAqC,UAAU,iBAAiB,aAAa;AAAA,IAC/E;AAGA,UAAM,EAAE,QAAQ,iBAAiB,QAAQ,IACvC,MAAM,KAAK,uBAAuB,YAAY,MAAM;AAGtD,UAAM,UAAU,KAAK,4BAA4B;AACjD,YAAQ;AAAA,MACN,8CAA8C,QAAQ,KAAK,UAAU,QAAQ,QAAQ,IAAI,KAAK,QAAQ,KAAK,KAAK,IAAI,CAAC,MAAM,EAAE;AAAA,IAC/H;AAEA,QAAI;AAEF,YAAM,QAAQ,gBAAgB,eAAe,MAAM;AACnD,YAAM,oBAAoB,QAAQ,UAAU;AAG5C,YAAM,UAAU,MAAM,KAAK;AAAA,QACzB;AAAA,QACA;AAAA,MACF;AACA,YAAM,EAAE,YAAY,KAAK,IAAI,gCAAgC;AAAA,QAC3D;AAAA,QACA;AAAA,MACF,CAAC;AAGD,YAAM,wBAAwB;AAAA,QAC5B,QAAQ;AAAA,QACR;AAAA,QACA;AAAA,QACA,GAAI,qBAAqB,OAAO,EAAE,kBAAkB,IAAI,CAAC;AAAA,MAC3D,CAAC;AAGD,YAAM,eAAe,0BAA0B;AAAA,QAC7C,QAAQ;AAAA,QACR;AAAA,QACA,GAAI,qBAAqB,OAAO,EAAE,kBAAkB,IAAI,CAAC;AAAA,QACzD;AAAA,QACA,GAAI,SAAS,oBAAoB,OAC7B,EAAE,kBAAkB,QAAQ,iBAAiB,IAC7C,CAAC;AAAA;AAAA,QAEL,mBAAmB;AAAA,MACrB,CAAC;AAGD,UAAI;AACJ,UAAI,UAAU;AAEd,YAAM,iBAAiB,MAAM;AAC3B,YAAI,UAAW,cAAa,SAAS;AACrC,YAAI,gBAAgB,KAAK,CAAC,SAAS;AACjC,sBAAY,WAAW,YAAY;AACjC,gBAAI,CAAC,SAAS;AACZ,sBAAQ;AAAA,gBACN,iCAAiC,aAAa,0BAA0B,UAAU;AAAA,cACpF;AACA,oBAAM,YAAY;AAAA,YACpB;AAAA,UACF,GAAG,aAAa;AAAA,QAClB;AAAA,MACF;AAEA,YAAM,cAAc,YAA2B;AAC7C,YAAI,QAAS;AACb,kBAAU;AAEV,YAAI,WAAW;AACb,uBAAa,SAAS;AACtB,sBAAY;AAAA,QACd;AAEA,YAAI;AACF,gBAAM,aAAa,KAAK;AAAA,QAC1B,SAAS,GAAG;AACV,kBAAQ,QAAQ,iDAAiD,CAAC,EAAE;AAAA,QACtE;AAGA,YAAI;AACF,gBAAM,QAAQ;AACd,gBAAMC,WAAU,KAAK,4BAA4B;AACjD,kBAAQ;AAAA,YACN,qCAAqC,UAAU,eAAeA,SAAQ,KAAK,UAAUA,SAAQ,QAAQ,IAAI,KAAKA,SAAQ,KAAK,KAAK,IAAI,CAAC,MAAM,EAAE;AAAA,UAC/I;AAAA,QACF,SAAS,GAAG;AACV,kBAAQ,QAAQ,4CAA4C,CAAC,EAAE;AAAA,QACjE;AAAA,MACF;AAGA,qBAAe;AAEf,aAAO;AAAA,QACL,MAAM,aAAa;AAAA,QACnB,WAAW,OAAO,UAAkB;AAClC,cAAI,QAAS,OAAM,IAAI,MAAM,wBAAwB;AACrD,yBAAe;AACf,iBAAO,MAAM,aAAa,UAAU,KAAK;AAAA,QAC3C;AAAA,QACA,MAAM;AAAA,MACR;AAAA,IACF,SAAS,GAAG;AAEV,UAAI;AACF,cAAM,QAAQ;AAAA,MAChB,QAAQ;AAAA,MAER;AACA,YAAM;AAAA,IACR;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAc,yBACZ,QACA,SACsB;AACtB,UAAM,QAAQ,MAAM,OAAO,UAAU;AAAA,MACnC,OAAO;AAAA,MACP;AAAA,MACA,YAAY;AAAA,MACZ,cAAc;AAAA,IAChB,CAAC;AAED,UAAM,MACJ,MAAM,KAAK,WAAW,IAClB,KACA,OAAO,cAAc,MAAM,MAAM,MAAM,OAAO,WAAW,OAAO,GAAG;AACzE,WAAO,oBAAoB,GAAG;AAAA,EAChC;AAAA;AAAA,EAGA,MAAM,WACJ,QACiB;AACjB,UAAM,KAAK,OAAO,MAAM;AACxB,WAAO,MAAM,KAAK,OAAO,WAAW,MAAM;AAAA,EAC5C;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,aAA4C;AAChD,UAAM,MAAM,MAAM,KAAK,QAAQ,EAAE,OAAO,GAAG,CAAC;AAE5C,UAAM,QAA8B,CAAC;AACrC,UAAM,cAAc,IAAI;AAAA,MACtB;AAAA,IACF;AACA,eAAW,KAAK,aAAa;AAC3B,YAAM,SAAS,EAAE,CAAC,KAAK,IAAI,YAAY;AACvC,YAAM,QAAQ,EAAE,CAAC,KAAK;AACtB,YAAM,KAA6B,CAAC;AACpC,iBAAW,OAAO,MAAM,SAAS,6BAA6B,GAAG;AAC/D,cAAM,KAAK,IAAI,CAAC,KAAK,IAAI,YAAY;AACrC,cAAM,IAAI,OAAO,IAAI,CAAC,CAAC;AACvB,YAAI,OAAO,SAAS,CAAC,EAAG,IAAG,CAAC,IAAI;AAAA,MAClC;AACA,UAAI,OAAO,KAAK,EAAE,EAAE,OAAQ,OAAM,KAAK,IAAI;AAAA,IAC7C;AACA,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,MAAM,WAA0C;AAC9C,WAAO,KAAK,WAAW;AAAA,EACzB;AAAA;AAAA,EAGA,MAAM,eAAe,QAGH;AAChB,UAAM,MAAM,GAAG,OAAO,KAAK,CAAC,EAAG,YAAY,CAAC,GAAG,OAAO,KAAK,MAAM,CAAC,CAAC;AACnE,UAAM,MACJ,iDAEI,GAAG,0BACI,OAAO,SAAS,IAAI,CAAC,cAC3B,GAAG;AAEV,UAAM,KAAK,QAAQ,EAAE,OAAO,IAAI,YAAY,IAAI,CAAC;AAAA,EACnD;AAAA;AAAA,EAGA,MAAM,QACJ,SACA,SAOqC;AACrC,UAAM,MAKF,EAAE,OAAO,WAAW,OAAO,KAAK,IAAI;AACxC,QAAI,YAAY,OAAW,KAAI,UAAU;AACzC,QAAI,SAAS,aAAa,KAAM,KAAI,YAAY,QAAQ;AACxD,QAAI,SAAS,gBAAgB,KAAM,KAAI,eAAe,QAAQ;AAC9D,UAAM,MAAM,MAAM,KAAK,QAAQ,GAAG;AAElC,UAAM,OAAO,SAAS,MAAM,SACxB,QAAQ,OACR;AAAA,MACE;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AACJ,WAAO,YAAY,KAAK,IAAI;AAAA,EAC9B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUQ,yBAAyB,KAAmB;AAClD,UAAM,gBAAgB,aAAa,KAAK,aAAa;AACrD,UAAM,YAAY,aAAa,KAAK,SAAS;AAC7C,UAAM,YAAY,CAAC,GAAG,eAAe,GAAG,SAAS;AAEjD,SAAK,OAAO;AAAA,MACV,4DAA4D,KAAK,UAAU,SAAS,CAAC;AAAA,IACvF;AAEA,UAAM,UAAU,2BAA2B,SAAS;AACpD,UAAM,QAAQ,KAAK,IAAI;AAEvB,eAAW,SAAS,SAAS;AAC3B,YAAM,WAAW,KAAK,gBAAgB,IAAI,MAAM,OAAO;AACvD,YAAM,EAAE,YAAY,MAAM,OAAO,IAAI,kCAAkC;AAAA,QACrE;AAAA,QACA;AAAA,QACA;AAAA,MACF,CAAC;AACD,UAAI,cAAc,CAAC,KAAM;AAEzB,iBAAW,OAAO,QAAQ;AACxB,aAAK,oBAAoB,GAAG;AAAA,MAC9B;AACA,WAAK,gBAAgB,IAAI,MAAM,SAAS,IAAI;AAAA,IAC9C;AAEA,QAAI,QAAQ,SAAS,GAAG;AACtB,YAAM,OAAO,gCAAgC,KAAK,eAAe;AACjE,WAAK,OAAO;AAAA,QACV,0DAA0D,KAAK,UAAU,IAAI,CAAC;AAAA,MAChF;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,eACJ,SACA,SAGqC;AACrC,UAAM,MAA8D;AAAA,MAClE,OAAO;AAAA,MACP;AAAA,IACF;AACA,QAAI,SAAS,aAAa,KAAM,KAAI,YAAY,QAAQ;AACxD,UAAM,MAAM,MAAM,KAAK,QAAQ,GAAG;AAIlC,UAAM,WAAW,WAAW,KAAK,UAAU,KAAK,WAAW,KAAK,MAAM;AACtE,UAAM,UACJ,WAAW,KAAK,SAAS,KAAK,WAAW,KAAK,iBAAiB;AACjE,UAAM,kBAAkB,WAAW;AACnC,UAAM,YAAY,WAAW,KAAK,WAAW;AAC7C,UAAM,YAAY,WAAW,KAAK,WAAW;AAE7C,WAAO;AAAA,MACL,GAAI,WAAW,EAAE,SAAS,IAAI,CAAC;AAAA,MAC/B,GAAI,UAAU,EAAE,SAAS,gBAAgB,IAAI,CAAC;AAAA,MAC9C,GAAI,YAAY,EAAE,UAAU,IAAI,CAAC;AAAA,MACjC,GAAI,YAAY,EAAE,UAAU,IAAI,CAAC;AAAA,IACnC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,MAAM,mBAAmB,SAE4B;AAEnD,UAAM,MAAkE;AAAA,MACtE,OAAO;AAAA,MACP,YAAY;AAAA,IACd;AACA,QAAI,SAAS,aAAa,KAAM,KAAI,YAAY,QAAQ;AAExD,QAAI,MAAM,MAAM,KAAK,QAAQ,GAAG;AAGhC,QAAI,CAAC,OAAO,IAAI,KAAK,EAAE,WAAW,GAAG;AACnC,YAAM,YAAmD;AAAA,QACvD,OAAO;AAAA,MACT;AACA,UAAI,SAAS,aAAa,KAAM,WAAU,YAAY,QAAQ;AAC9D,YAAM,MAAM,KAAK,QAAQ,SAAS;AAAA,IACpC;AAGA,QAAI,CAAC,OAAO,IAAI,KAAK,EAAE,WAAW,GAAG;AACnC,aAAO,oBAAI,IAAI;AAAA,IACjB;AAEA,UAAM,SAAS,oBAAI,IAAwC;AAI3D,QAAI,gBAAgB,aAAa,KAAK,SAAS;AAC/C,QAAI,cAAc,WAAW,GAAG;AAC9B,sBAAgB,aAAa,KAAK,aAAa;AAAA,IACjD;AACA,QAAI,cAAc,WAAW,GAAG;AAC9B,sBAAgB,aAAa,KAAK,SAAS;AAAA,IAC7C;AACA,QAAI,cAAc,WAAW,GAAG;AAC9B,sBAAgB,aAAa,KAAK,SAAS;AAAA,IAC7C;AAEA,eAAW,SAAS,eAAe;AAEjC,YAAM,cACJ,WAAW,OAAO,SAAS,KAC3B,WAAW,OAAO,WAAW,KAC7B,WAAW,OAAO,IAAI;AACxB,YAAM,UAAU,cACZ,OAAO,SAAS,aAAa,EAAE,IAC/B;AAEJ,UAAI,YAAY,UAAa,CAAC,OAAO,SAAS,OAAO,GAAG;AAEtD;AAAA,MACF;AAGA,YAAM,WACJ,WAAW,OAAO,UAAU,KAAK,WAAW,OAAO,MAAM;AAC3D,YAAM,UACJ,WAAW,OAAO,SAAS,KAAK,WAAW,OAAO,iBAAiB;AACrE,YAAM,kBAAkB,WAAW;AACnC,YAAM,YAAY,WAAW,OAAO,WAAW;AAC/C,YAAM,YAAY,WAAW,OAAO,WAAW;AAC/C,YAAM,OAAO,WAAW,OAAO,MAAM;AAErC,aAAO,IAAI,SAAS;AAAA,QAClB,GAAI,WAAW,EAAE,SAAS,IAAI,CAAC;AAAA,QAC/B,GAAI,UAAU,EAAE,SAAS,gBAAgB,IAAI,CAAC;AAAA,QAC9C,GAAI,YAAY,EAAE,UAAU,IAAI,CAAC;AAAA,QACjC,GAAI,YAAY,EAAE,UAAU,IAAI,CAAC;AAAA,QACjC,GAAI,OAAO,EAAE,KAAK,IAAI,CAAC;AAAA,MACzB,CAAC;AAAA,IACH;AAIA,QAAI,OAAO,SAAS,GAAG;AAGrB,YAAM,cAAc,aAAa,KAAK,MAAM;AAC5C,iBAAW,aAAa,aAAa;AAEnC,cAAM,cAAc,WAAW,WAAW,SAAS;AACnD,YAAI,aAAa;AACf,gBAAM,UAAU,OAAO,SAAS,aAAa,EAAE;AAC/C,cAAI,OAAO,SAAS,OAAO,GAAG;AAC5B,kBAAM,WACJ,WAAW,WAAW,UAAU,KAChC,WAAW,WAAW,MAAM;AAC9B,kBAAM,UACJ,WAAW,WAAW,SAAS,KAC/B,WAAW,WAAW,iBAAiB;AACzC,kBAAM,kBAAkB,WAAW;AACnC,kBAAM,YAAY,WAAW,WAAW,WAAW;AACnD,kBAAM,YAAY,WAAW,WAAW,WAAW;AACnD,kBAAM,OAAO,WAAW,WAAW,MAAM;AAEzC,mBAAO,IAAI,SAAS;AAAA,cAClB,GAAI,WAAW,EAAE,SAAS,IAAI,CAAC;AAAA,cAC/B,GAAI,UAAU,EAAE,SAAS,gBAAgB,IAAI,CAAC;AAAA,cAC9C,GAAI,YAAY,EAAE,UAAU,IAAI,CAAC;AAAA,cACjC,GAAI,YAAY,EAAE,UAAU,IAAI,CAAC;AAAA,cACjC,GAAI,OAAO,EAAE,KAAK,IAAI,CAAC;AAAA,YACzB,CAAC;AAAA,UACH;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,mBACJ,SACA,SAGyC;AACzC,UAAM,OAAO,MAAM,KAAK,QAAQ,SAAS;AAAA,MACvC,GAAI,SAAS,aAAa,OAAO,EAAE,WAAW,QAAQ,UAAU,IAAI,CAAC;AAAA,MACrE,MAAM,CAAC,QAAQ,MAAM;AAAA,IACvB,CAAC;AACD,WAAO;AAAA,MACL;AAAA,MACA,QAAQ,KAAK,QAAQ,IAAI,KAAK;AAAA,MAC9B,OAAO,KAAK,QAAQ,IAAI,KAAK;AAAA,IAC/B;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,8BAAkE;AAChE,UAAM,MAAM,oBAAI,IAAmC;AACnD,eAAW,CAAC,SAAS,IAAI,KAAK,KAAK,gBAAgB,QAAQ,GAAG;AAC5D,YAAM,cAAc,KAAK,cAAc,KAAK,OAAO,YAAY;AAC/D,UAAI,eAAe,OAAQ;AAC3B,UAAI,IAAI,SAAS;AAAA,QACf,MAAM,KAAK;AAAA,QACX,KAAK,KAAK;AAAA,QACV,OAAO,KAAK;AAAA,QACZ,GAAI,OAAO,KAAK,UAAU,WAAW,EAAE,OAAO,KAAK,MAAM,IAAI,CAAC;AAAA,QAC9D,GAAI,KAAK,eAAe,SACpB,EAAE,eAAe,KAAK,cAAc,IACpC,CAAC;AAAA,QACL,GAAI,KAAK,YAAY,EAAE,WAAW,KAAK,UAAU,IAAI,CAAC;AAAA,QACtD,GAAI,KAAK,iBAAiB,EAAE,gBAAgB,KAAK,eAAe,IAAI,CAAC;AAAA,QACrE,GAAI,OAAO,KAAK,YAAY,YAAY,EAAE,SAAS,KAAK,QAAQ,IAAI,CAAC;AAAA,QACrE,GAAI,OAAO,KAAK,mBAAmB,YAC/B,EAAE,gBAAgB,KAAK,eAAe,IACtC,CAAC;AAAA,QACL,GAAI,OAAO,KAAK,WAAW,YAAY,EAAE,QAAQ,KAAK,OAAO,IAAI,CAAC;AAAA,QAClE,GAAI,OAAO,KAAK,aAAa,YACzB,EAAE,UAAU,KAAK,SAAS,IAC1B,CAAC;AAAA,QACL,GAAI,KAAK,aAAa,EAAE,YAAY,KAAK,WAAW,IAAI,CAAC;AAAA,QACzD,GAAI,OAAO,KAAK,gBAAgB,WAC5B,EAAE,aAAa,KAAK,YAAY,IAChC,CAAC;AAAA,MACP,CAAC;AAAA,IACH;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,+BAAqD;AACnD,UAAM,MAAM,oBAAI,IAAqB;AACrC,eAAW,CAAC,SAAS,IAAI,KAAK,KAAK,gBAAgB,QAAQ,GAAG;AAC5D,UAAI,OAAO,KAAK,aAAa,UAAW,KAAI,IAAI,SAAS,KAAK,QAAQ;AAAA,IACxE;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,sBAAsB,SAIc;AACxC,UAAM,SAAS,SAAS,UAAU;AAElC,UAAM,WAAW,KAAK,4BAA4B;AAClD,UAAM,YACJ,SAAS,UAAU,SAAS,QAAQ,WAAW,MAAM,KAAK,SAAS,KAAK,CAAC,GAExE,IAAI,CAAC,MAAM,OAAO,CAAC,CAAC,EACpB,OAAO,CAAC,MAAM,OAAO,SAAS,CAAC,CAAC,EAChC,KAAK,CAAC,GAAG,MAAM,IAAI,CAAC;AAEvB,UAAM,UAAU,MAAM,KAAK,eAAe,EAAE,MAAM,MAAM;AACtD,WAAK,OAAO;AAAA,QACV;AAAA,MACF;AAAA,IACF,CAAC;AAED,UAAM,mBAAmB,CAAC,MAAwB;AAChD,UAAI,OAAO,MAAM,SAAU,QAAO,IAAI;AACtC,UAAI,OAAO,MAAM,UAAU;AACzB,cAAM,IAAI,OAAO,CAAC;AAClB,YAAI,OAAO,SAAS,CAAC,EAAG,QAAO,IAAI;AACnC,eAAO,EAAE,SAAS,KAAK,MAAM;AAAA,MAC/B;AACA,aAAO,QAAQ,CAAC;AAAA,IAClB;AAEA,UAAM,qBAAqB,oBAAI,IAAqB;AACpD,UAAM,sBAAsB,oBAAI,IAAqB;AACrD,QAAI,SAAS;AACX,iBAAW,MAAM,UAAU;AACzB,cAAM,OAAO,0BAA0B,EAAE,SAAS,IAAI,QAAQ,CAAC;AAC/D,2BAAmB,IAAI,IAAI,QAAQ,KAAK,UAAU,CAAC;AACnD,cAAM,2BAA2B,QAAQ,SAAS,CAAC,GAAG;AAAA,UACpD,CAAC,MAAM,EAAE,UAAU,MAAM,iBAAiB,EAAE,sBAAsB,CAAC;AAAA,QACrE;AACA,4BAAoB;AAAA,UAClB;AAAA,UACA,QAAQ,KAAK,UAAU,KAAK;AAAA,QAC9B;AAAA,MACF;AAAA,IACF;AAEA,UAAM,WAAW,YAAY,SAAS,KAAK,GAAG,CAAC;AAC/C,UAAM,SAAS,KAAK,wBAAwB,IAAI,QAAQ;AACxD,QAAI,QAAQ;AACV,aAAO;AAAA,QACL,UAAU,CAAC,GAAG,OAAO,QAAQ;AAAA,QAC7B,SAAS,OAAO,QAAQ,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE,EAAE;AAAA,MAC/C;AAAA,IACF;AAEA,UAAM,YAAY,SAAS;AAC3B,UAAM,iBAAiB,oBAAI,IAA+B;AAC1D,UAAM,wBAAwB,oBAAI,IAGhC;AACF,eAAW,WAAW,UAAU;AAC9B,UAAI;AACF,cAAM,OAAO,MAAM,KAAK,QAAQ,SAAS;AAAA,UACvC,GAAI,aAAa,OAAO,EAAE,UAAU,IAAI,CAAC;AAAA,UACzC,MAAM,CAAC,QAAQ,QAAQ,cAAc;AAAA,QACvC,CAAC;AACD,uBAAe,IAAI,SAAS,IAAI;AAAA,MAMlC,QAAQ;AAAA,MAAC;AAAA,IACX;AAEA,UAAM,UAAU,SAAS,IAAI,CAAC,YAAY;AACxC,YAAMC,UAAS,SAAS,IAAI,OAAO;AACnC,YAAM,OAAO,eAAe,IAAI,OAAO;AACvC,YAAM,cAAc,sBAAsB,IAAI,OAAO;AACrD,YAAM,YAAY,mBAAmB,IAAI,OAAO,KAAK;AACrD,YAAM,QAAQ,MAAM,QAAQ;AAC5B,YAAM,cACH,oBAAoB,IAAI,OAAO,KAAK,UAAU,YAAY,KAAK,KAAK;AAEvE,YAAM,kBAAkB,QAAQ,MAAM,KAAK,IAAI;AAC/C,YAAM,eAAe,kBACjB,iBAAiB,eAAe,IAChC;AAEJ,aAAO;AAAA,QACL;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,GAAI,aAAa,KAAK,EAAE,IAAI,YAAY,GAAG,IAAI,CAAC;AAAA,QAChD,GAAI,aAAa,MAAM,EAAE,KAAK,YAAY,IAAI,IAAI,CAAC;AAAA,QACnD,GAAI,aAAa,aACb,EAAE,YAAY,YAAY,WAAW,IACrC,CAAC;AAAA,QACL,GAAIA,SAAQ,OAAO,EAAE,MAAMA,QAAO,KAAK,IAAI,CAAC;AAAA,QAC5C,GAAIA,SAAQ,MAAM,EAAE,KAAKA,QAAO,IAAI,IAAI,CAAC;AAAA,QACzC,GAAIA,SAAQ,QAAQ,EAAE,OAAOA,QAAO,MAAM,IAAI,CAAC;AAAA,QAC/C,GAAI,OAAOA,SAAQ,UAAU,WAAW,EAAE,OAAOA,QAAO,MAAM,IAAI,CAAC;AAAA,QACnE,GAAIA,SAAQ,eAAe,SACvB,EAAE,eAAeA,QAAO,cAAc,IACtC,CAAC;AAAA,QACL,GAAIA,SAAQ,YAAY,EAAE,WAAWA,QAAO,UAAU,IAAI,CAAC;AAAA,QAC3D,GAAIA,SAAQ,iBACR,EAAE,gBAAgBA,QAAO,eAAe,IACxC,CAAC;AAAA,QACL,GAAI,OAAOA,SAAQ,YAAY,YAC3B,EAAE,SAASA,QAAO,QAAQ,IAC1B,CAAC;AAAA,QACL,GAAI,OAAOA,SAAQ,mBAAmB,YAClC,EAAE,gBAAgBA,QAAO,eAAe,IACxC,CAAC;AAAA,QACL,GAAI,OAAOA,SAAQ,WAAW,YAC1B,EAAE,QAAQA,QAAO,OAAO,IACxB,CAAC;AAAA,QACL,GAAI,OAAOA,SAAQ,aAAa,YAC5B,EAAE,UAAUA,QAAO,SAAS,IAC5B,CAAC;AAAA,QACL,GAAIA,SAAQ,aAAa,EAAE,YAAYA,QAAO,WAAW,IAAI,CAAC;AAAA,QAC9D,GAAI,OAAOA,SAAQ,gBAAgB,WAC/B,EAAE,aAAaA,QAAO,YAAY,IAClC,CAAC;AAAA,MACP;AAAA,IACF,CAAC;AAED,UAAM,SAAS,EAAE,UAAU,QAAQ;AACnC,SAAK,wBAAwB,IAAI,UAAU;AAAA,MACzC,UAAU,CAAC,GAAG,QAAQ;AAAA,MACtB,SAAS,QAAQ,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE,EAAE;AAAA,IACxC,CAAC;AACD,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAM,mBAAmB,SAGiB;AACxC,UAAM,EAAE,UAAU,QAAQ,IAAI,MAAM,KAAK,sBAAsB,OAAO;AAEtE,UAAM,yBAAyB,CAAC,UAA4B;AAC1D,YAAM,KAAK,SAAS,IAAI,KAAK;AAC7B,UAAI,CAAC,EAAG,QAAO;AACf,UAAI,iBAAiB,IAAI,CAAC,EAAG,QAAO;AACpC,YAAM,QAAQ,EAAE,YAAY;AAE5B,UAAI,MAAM,SAAS,UAAU,EAAG,QAAO;AACvC,UAAI,MAAM,SAAS,KAAK,EAAG,QAAO;AAElC,aAAO;AAAA,IACT;AAWA,UAAM,cAAc,oBAAI,IAA0B;AAClD,UAAM,cAAc,oBAAI,IAAoB;AAC5C,UAAM,iBAAyC,CAAC;AAEhD,UAAM,cAAc,CAAC,QAA8B;AACjD,UAAI,IAAI,YAAY,IAAI,GAAG;AAC3B,UAAI,CAAC,GAAG;AACN,YAAI,EAAE,KAAK,UAAU,CAAC,GAAG,UAAU,oBAAI,IAAI,GAAG,SAAS,oBAAI,IAAI,EAAE;AACjE,oBAAY,IAAI,KAAK,CAAC;AAAA,MACxB;AACA,aAAO;AAAA,IACT;AAEA,eAAW,KAAK,SAAS;AACvB,YAAM,OAAO,EAAE,OAAO,IAAI,KAAK,KAAK;AACpC,YAAM,UAAU,EAAE,gBAAgB,IAAI,KAAK,KAAK;AAChD,YAAM,SAAS,EAAE,SAAS,IAAI,KAAK,KAAK;AACxC,YAAM,QAAQ,EAAE,QAAQ,IAAI,KAAK,KAAK;AAGtC,UAAI,MAAM,MACN,OAAO,GAAG,KACV,SACE,MAAM,MAAM,KACZ,MAAM,EAAE,OAAO;AACrB,UAAI,CAAC,OAAO,QAAQ;AAClB,cAAM,WAAW,YAAY,IAAI,MAAM;AACvC,YAAI,SAAU,OAAM;AAAA,MACtB;AAEA,YAAM,IAAI,YAAY,GAAG;AACzB,UAAI,CAAC,EAAE,SAAS,SAAS,EAAE,OAAO,EAAG,GAAE,SAAS,KAAK,EAAE,OAAO;AAC9D,UAAI,CAAC,EAAE,OAAO,IAAK,GAAE,MAAM;AAC3B,UAAI,CAAC,EAAE,gBAAgB,OAAQ,GAAE,eAAe;AAChD,UAAI,MAAO,GAAE,SAAS,IAAI,KAAK;AAC/B,UAAI,KAAM,GAAE,QAAQ,IAAI,IAAI;AAG5B,UAAI,OAAQ,aAAY,IAAI,QAAQ,GAAG;AACvC,qBAAe,EAAE,OAAO,IAAI;AAAA,IAC9B;AAEA,UAAM,gBAAgB,CAAC,MAAwC;AAC7D,UAAI,EAAE,SAAS,SAAS,EAAG,QAAO,MAAM,KAAK,EAAE,QAAQ,EAAE,CAAC;AAE1D,iBAAW,KAAK,EAAE,UAAU;AAC1B,YAAI,uBAAuB,CAAC,EAAG,QAAO;AAAA,MACxC;AACA,aAAO,EAAE,SAAS,OAAO,MAAM,KAAK,EAAE,QAAQ,EAAE,CAAC,IAAI;AAAA,IACvD;AAEA,UAAM,eAAe,CAAC,MAAwC;AAC5D,UAAI,EAAE,QAAQ,SAAS,EAAG,QAAO,MAAM,KAAK,EAAE,OAAO,EAAE,CAAC;AACxD,aAAO,EAAE,QAAQ,OAAO,MAAM,KAAK,EAAE,OAAO,EAAE,CAAC,IAAI;AAAA,IACrD;AAEA,UAAM,SAAyC,MAAM;AAAA,MACnD,YAAY,OAAO;AAAA,IACrB,EACG,IAAI,CAAC,MAAM;AACV,QAAE,SAAS,KAAK,CAAC,GAAG,MAAM,IAAI,CAAC;AAC/B,YAAM,OAAO,aAAa,CAAC;AAC3B,YAAM,QAAQ,cAAc,CAAC;AAC7B,YAAM,eACJ,EAAE,SAAS,SAAS,KAAK,uBAAuB,KAAK;AACvD,YAAM,SACJ,EAAE,SAAS,SAAS,IAChB,UAAU,EAAE,MAAM,QAAQ,EAAE,eAAe,WAAW,UAAU,WAAW,EAAE,SAAS,MAAM,cAC5F,uBAAuB,KAAK,IAC1B,oCACA;AACR,aAAO;AAAA,QACL,KAAK,EAAE;AAAA,QACP,GAAI,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,IAAI,CAAC;AAAA,QAC9B,GAAI,EAAE,eAAe,EAAE,cAAc,EAAE,aAAa,IAAI,CAAC;AAAA,QACzD,GAAI,OAAO,EAAE,KAAK,IAAI,CAAC;AAAA,QACvB,GAAI,QAAQ,EAAE,MAAM,IAAI,CAAC;AAAA,QACzB,UAAU,EAAE;AAAA,QACZ;AAAA,QACA;AAAA,MACF;AAAA,IACF,CAAC,EACA,KAAK,CAAC,GAAG,OAAO,EAAE,SAAS,CAAC,KAAK,MAAM,EAAE,SAAS,CAAC,KAAK,EAAE;AAE7D,WAAO,EAAE,UAAU,QAAQ,eAAe;AAAA,EAC5C;AAAA;AAAA,EAGA,MAAM,UACJ,SACA,SACiB;AACjB,UAAM,KAAK,KAAK,iBAAiB,OAAO;AACxC,WAAO,MAAM,KAAK,QAAQ;AAAA,MACxB,OAAO;AAAA,MACP,SAAS;AAAA,MACT,GAAI,SAAS,aAAa,OAAO,EAAE,WAAW,QAAQ,UAAU,IAAI,CAAC;AAAA,IACvE,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAkBA,MAAM,kBAAkB,SAAkD;AACxE,UAAM,KAAK,KAAK,iBAAiB,OAAO;AACxC,UAAM,MAAM,MAAM,KAAK,UAAU,EAAE;AACnC,UAAM,MAAM,KAAK,OAAO,iBAAiB;AACzC,WAAO,wCAAwC;AAAA,MAC7C,SAAS;AAAA,MACT;AAAA,MACA,QAAQ,KAAK;AAAA,MACb,mBAAmB,KAAK,sBAAsB;AAAA,IAChD,CAAC;AAAA,EACH;AAAA,EAKA,MAAM,UACJ,iBACA,aACe;AACf,UAAM,KACJ,OAAO,oBAAoB,WACvB,KAAK,iBAAiB,eAAe,IACrC;AACN,UAAM,SACJ,OAAO,oBAAoB,WAAW,cAAe;AACvD,UAAM,KAAK,QAAQ,EAAE,OAAO,IAAI,SAAS,IAAI,YAAY,OAAO,CAAC;AAAA,EACnE;AAAA,EAkBA,MAAM,oBACJ,kBACA,gBACA,gBACe;AACf,UAAM,KACJ,OAAO,qBAAqB,WACxB,KAAK,iBAAiB,gBAAgB,IACtC,KAAK,iBAAiB,cAAoC;AAChE,UAAM,UACJ,OAAO,qBAAqB,WACvB,iBACD;AACN,UAAM,QACJ,OAAO,qBAAqB,WACvB,iBACA;AACP,UAAM,UAAU,UAAU,UAAU,IAAI;AAExC,UAAM,gBACJ,YAAY,SACR,CAAC,YAAY,IACb,YAAY,QACV,CAAC,WAAW,IACZ,CAAC,aAAa,eAAe,gBAAgB,aAAa;AAElE,UAAM,UAAU,MAAM,KAAK,UAAU,EAAE;AAEvC,QAAI,UAAyB;AAC7B,eAAW,OAAO,eAAe;AAC/B,YAAM,YAAY,IAAI;AAAA,QACpB,KAAK,GAAG;AAAA,MACV;AACA,UAAI,CAAC,UAAU,KAAK,OAAO,EAAG;AAC9B,YAAM,OAAO,QAAQ,QAAQ,WAAW,KAAK,OAAO,IAAI;AACxD,UAAI,SAAS,SAAS;AACpB,kBAAU;AACV;AAAA,MACF;AAAA,IACF;AAEA,QAAI,CAAC,SAAS;AACZ,YAAM,IAAI;AAAA,QACR,6CAA6C,OAAO,UAAU,cAAc,KAAK,GAAG,CAAC,4BAA4B,EAAE;AAAA,MACrH;AAAA,IACF;AAEA,UAAM,KAAK,UAAU,IAAI,OAAO;AAAA,EAClC;AAAA;AAAA,EAGA,MAAM,WAAW,SAIC;AAChB,QAAI,QAAQ,eAAe;AACzB,YAAM,KAAK,eAAe;AAAA,QACxB,MAAM;AAAA,QACN,QAAQ,QAAQ,gBAAgB;AAAA,MAClC,CAAC;AACH,QAAI,QAAQ,cAAc;AACxB,YAAM,KAAK,eAAe;AAAA,QACxB,MAAM;AAAA,QACN,QAAQ,QAAQ,eAAe;AAAA,MACjC,CAAC;AACH,QAAI,QAAQ,cAAc;AACxB,YAAM,KAAK,eAAe;AAAA,QACxB,MAAM;AAAA,QACN,QAAQ,QAAQ,eAAe;AAAA,MACjC,CAAC;AAAA,EACL;AAAA;AAAA,EAGA,MAAM,OAAO,SAAiC;AAC5C,UAAM,MAA2C,EAAE,OAAO,GAAG;AAC7D,QAAI,YAAY,OAAW,KAAI,UAAU;AACzC,UAAM,KAAK,QAAQ,GAAG;AAAA,EACxB;AAAA;AAAA,EAGA,MAAM,OAAsB;AAC1B,UAAM,KAAK,QAAQ,EAAE,OAAO,eAAe,CAAC;AAAA,EAC9C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAM,eACJ,SACA,SAGiD;AACjD,UAAM,YAAY,SAAS,aAAa;AAExC,UAAM,OAAO,CAAC,MAAmC;AAC/C,UAAI,OAAO,MAAM,SAAU,QAAO;AAClC,YAAM,IAAI,EAAE,KAAK;AACjB,aAAO,IAAI,IAAI;AAAA,IACjB;AAEA,UAAM,QAAQ,CAAC,QAAyD;AACtE,UAAI,CAAC,IAAK,QAAO;AACjB,YAAM,KACJ,KAAK,WAAW,KAAK,IAAI,CAAC,KAC1B,KAAK,WAAW,KAAK,IAAI,CAAC,KAC1B,KAAK,WAAW,KAAK,MAAM,CAAC,KAC5B,KAAK,WAAW,KAAK,MAAM,CAAC,KAC5B;AACF,YAAM,MACJ,KAAK,WAAW,KAAK,KAAK,CAAC,KAC3B,KAAK,WAAW,KAAK,KAAK,CAAC,KAC3B;AACF,YAAM,aACJ,KAAK,WAAW,KAAK,YAAY,CAAC,KAClC,KAAK,WAAW,KAAK,MAAM,CAAC,KAC5B,KAAK,WAAW,KAAK,UAAU,CAAC,KAChC;AACF,aAAO,MAAM,OAAO,aAChB;AAAA,QACE,GAAI,KAAK,EAAE,GAAG,IAAI,CAAC;AAAA,QACnB,GAAI,MAAM,EAAE,IAAI,IAAI,CAAC;AAAA,QACrB,GAAI,aAAa,EAAE,WAAW,IAAI,CAAC;AAAA,MACrC,IACA;AAAA,IACN;AAEA,UAAM,QAAQ,CACZ,GACA,MAC2C;AAC3C,UAAI,CAAC,KAAK,CAAC,EAAG,QAAO;AACrB,aAAO;AAAA,QACL,GAAI,GAAG,KAAK,EAAE,IAAI,EAAE,GAAG,IAAI,GAAG,KAAK,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC;AAAA,QACnD,GAAI,GAAG,MAAM,EAAE,KAAK,EAAE,IAAI,IAAI,GAAG,MAAM,EAAE,KAAK,EAAE,IAAI,IAAI,CAAC;AAAA,QACzD,GAAI,GAAG,aACH,EAAE,YAAY,EAAE,WAAW,IAC3B,GAAG,aACD,EAAE,YAAY,EAAE,WAAW,IAC3B,CAAC;AAAA,MACT;AAAA,IACF;AAEA,QAAI,YAAY,QAAW;AAEzB,UAAI;AACJ,UAAI;AAEJ,UAAI;AACF,gBAAQ,MAAM,KAAK,QAAQ,EAAE,OAAO,IAAI,UAAU,CAAC;AAAA,MACrD,QAAQ;AAAA,MAER;AAEA,UAAI;AACF,gBAAQ,MAAM,KAAK,QAAQ,EAAE,OAAO,IAAI,UAAU,CAAC;AAAA,MACrD,QAAQ;AAAA,MAER;AAEA,YAAM,SAAS,MAAM,MAAM,KAAK,GAAG,MAAM,KAAK,CAAC;AAC/C,UAAI,OAAQ,QAAO;AAEnB,UAAI;AACF,cAAM,MAAM,MAAM,KAAK,cAAc;AACrC,eAAO,MAAM,GAAG;AAAA,MAClB,QAAQ;AACN,eAAO;AAAA,MACT;AAAA,IACF;AAEA,UAAM,KAAK,KAAK,iBAAiB,OAAO;AACxC,QAAI;AACF,YAAM,MAAM,MAAM,KAAK,QAAQ,EAAE,OAAO,IAAI,SAAS,IAAI,UAAU,CAAC;AACpE,YAAM,SAAS,MAAM,GAAG;AACxB,UAAI,OAAQ,QAAO;AAAA,IACrB,QAAQ;AAAA,IAER;AAEA,QAAI;AACF,YAAM,MAAM,MAAM,KAAK,cAAc,EAAE;AACvC,aAAO,MAAM,GAAG;AAAA,IAClB,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA,EAGA,MAAM,cAAc,SAAmC;AACrD,UAAM,MAA2C,EAAE,OAAO,IAAI;AAC9D,QAAI,YAAY,OAAW,KAAI,UAAU;AACzC,WAAO,MAAM,KAAK,QAAQ,GAAG;AAAA,EAC/B;AAAA,EAKA,MAAM,cACJ,cACA,UACe;AACf,UAAM,UACJ,OAAO,iBAAiB,YAAY,iBAAiB,SACjD,eACA;AACN,UAAM,MAAM,OAAO,iBAAiB,WAAW,eAAe;AAC9D,UAAM,KAAK,QAAQ;AAAA,MACjB,OAAO;AAAA,MACP,GAAI,YAAY,SAAY,CAAC,IAAI,EAAE,QAAQ;AAAA,MAC3C,YAAY;AAAA,IACd,CAAC;AAAA,EACH;AAAA;AAAA,EAGA,OAAO,yBAAyB,SAAyB;AACvD,WACE,8EACuC,UAAU,OAAO,OAAO,CAAC,CAAC;AAAA,EAErE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,MAAM,eAAe,SAAoC;AACvD,UAAM,MAAM,MAAM,KAAK,QAAQ;AAAA,MAC7B,OAAO;AAAA,MACP,GAAI,YAAY,SAAY,EAAE,QAAQ,IAAI,CAAC;AAAA,IAC7C,CAAC;AAGD,UAAM,SAAS,WAAW,KAAK,QAAQ;AACvC,WAAO,WAAW,OAAO,WAAW;AAAA,EACtC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,eACJ,SACA,SAC4B;AAC5B,UAAM,MAAM,MAAM,KAAK,QAAQ;AAAA,MAC7B,OAAO;AAAA,MACP,GAAI,YAAY,SAAY,EAAE,QAAQ,IAAI,CAAC;AAAA,MAC3C,GAAI,SAAS,aAAa,OAAO,EAAE,WAAW,QAAQ,UAAU,IAAI,CAAC;AAAA,IACvE,CAAC;AACD,WAAO,uBAA0C,GAAG;AAAA,EACtD;AAAA,EAiBA,MAAM,eACJ,MACA,MACA,MACe;AAGf,WAAO,MAAO,KAAK;AAAA,MACjB;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,OAAO,SAAsC;AACjD,UAAM,KAAK,KAAK,iBAAiB,OAAO;AACxC,UAAM,QAAQ;AACd,UAAM,MAAM,MAAM,KAAK,QAAQ,EAAE,OAAO,SAAS,GAAG,CAAC;AAGrD,WAAO;AAAA,MACL,SAAS;AAAA,MACT,YAAY;AAAA,QACV,QAAQ,OAAO,WAAW,KAAK,QAAQ,KAAK,GAAG;AAAA,QAC/C,MAAM,WAAW,KAAK,MAAM,KAAK;AAAA,QACjC,KAAK,WAAW,KAAK,KAAK,KAAK;AAAA,MACjC;AAAA,MACA,SAAS;AAAA,QACP,QAAQ,OAAO,WAAW,KAAK,YAAY,KAAK,GAAG;AAAA,QACnD,KAAK,WAAW,KAAK,SAAS,KAAK;AAAA,MACrC;AAAA,MACA,WAAW,OAAO,WAAW,KAAK,WAAW,KAAK,GAAG;AAAA,IACvD;AAAA,EACF;AAAA,EAQA,MAAM,OACJ,cACA,UACe;AACf,UAAM,KACJ,OAAO,iBAAiB,WACpB,KAAK,iBAAiB,YAAY,IAClC,KAAK,iBAAiB,QAA8B;AAC1D,UAAM,MACJ,OAAO,iBAAiB,WAAY,WAAyB;AAC/D,UAAM,QAAQ;AACd,UAAM,MACJ,4EAGY,EAAE,iCAEH,IAAI,WAAW,MAAM,kBACvB,UAAU,IAAI,WAAW,IAAI,CAAC,eAC/B,UAAU,IAAI,WAAW,GAAG,CAAC,uCAG1B,IAAI,QAAQ,MAAM,iBACrB,UAAU,IAAI,QAAQ,GAAG,CAAC,8BAEpB,IAAI,SAAS;AAG7B,UAAM,KAAK,QAAQ,EAAE,OAAO,SAAS,IAAI,YAAY,IAAI,CAAC;AAAA,EAC5D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,WAAW,SAAoC;AACnD,UAAM,KAAK,KAAK,iBAAiB,OAAO;AACxC,UAAM,iBAAiB,MAAM,KAAK,yBAAyB,EAAE;AAC7D,WAAO,MAAM,wBAAwB;AAAA,MACnC,SAAS,CAAC,GAAG,UAAU,KAAK,QAAQ,GAAG,KAAK;AAAA,MAC5C,SAAS;AAAA,MACT,GAAI,kBAAkB,eAAe,SAAS,IAC1C,EAAE,eAAe,IACjB,CAAC;AAAA,IACP,CAAC;AAAA,EACH;AAAA;AAAA,EAGA,MAAM,WAAW,SAAoC;AACnD,WAAO,MAAM,KAAK,WAAW,OAAO;AAAA,EACtC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,cACJ,SACA,QACA,SACwB;AACxB,UAAM,KAAK,KAAK,iBAAiB,OAAO;AACxC,UAAM,aACJ,gFAGQ,EAAE,eACD,UAAU,MAAM,CAAC;AAI5B,UAAM,MAAM,MAAM,KAAK;AAAA,MACrB;AAAA,QACE,OAAO;AAAA,QACP,SAAS;AAAA,QACT;AAAA,QACA,GAAI,SAAS,aAAa,OAAO,EAAE,WAAW,QAAQ,UAAU,IAAI,CAAC;AAAA,QACrE,GAAI,SAAS,qBAAqB,OAC9B,EAAE,mBAAmB,QAAQ,kBAAkB,IAC/C,CAAC;AAAA,MACP;AAAA,MACA;AAAA,IACF;AAEA,WAAO,uBAAsC,GAAG;AAAA,EAClD;AAAA,EAEQ,mCAAmC,MAAwB;AACjE,UAAM,OAAO,QAAQ,IAAI,KAAK;AAC9B,QAAI,CAAC,IAAK,QAAO,CAAC;AAElB,UAAM,IAAI,IAAI,YAAY;AAC1B,QAAI,CAAC,KAAK,MAAM,OAAQ,QAAO,CAAC;AAIhC,UAAM,eAAuC;AAAA,MAC3C,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,KAAK;AAAA,MACL,SAAS;AAAA,MACT,KAAK;AAAA,MACL,QAAQ;AAAA,MACR,SAAS;AAAA,MACT,MAAM;AAAA,MACN,SAAS;AAAA,IACX;AAEA,UAAM,YAAY,aAAa,CAAC,KAAK;AACrC,QAAI,cAAc,EAAG,QAAO,CAAC,SAAS;AACtC,WAAO,CAAC,WAAW,CAAC;AAAA,EACtB;AAAA,EAEA,MAAc,uBACZ,SAC+B;AAC/B,UAAM,MAAM,KAAK,IAAI;AACrB,UAAM,SAAS,KAAK,mBAAmB,IAAI,OAAO;AAElD,QAAI,UAAU,MAAM,OAAO,cAAc,IAAI,KAAQ;AACnD,aAAO,OAAO;AAAA,IAChB;AAEA,UAAM,cAAc,MAAM,KAAK,iBAAiB,SAAS;AAAA,MACvD,WAAW;AAAA,IACb,CAAC;AACD,SAAK,mBAAmB;AAAA,MACtB;AAAA,MACA,eAAe,OACX,EAAE,OAAO,aAAa,aAAa,IAAI,IACvC,EAAE,aAAa,IAAI;AAAA,IACzB;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,+BACZ,SACA,iBACiB;AACjB,UAAM,OAAO,mBAAmB,IAAI,KAAK;AACzC,QAAI,CAAC,IAAK,QAAO;AAEjB,UAAM,sBAAsB,KAAK,mCAAmC,GAAG;AACvE,UAAM,cAAc,MAAM,KAAK,uBAAuB,OAAO;AAE7D,QAAI,eAAe,YAAY,SAAS,GAAG;AACzC,YAAM,YAAY,IAAI;AAAA,QACpB,YACG,QAAQ,CAAC,MAAM,KAAK,mCAAmC,CAAC,CAAC,EACzD,IAAI,CAAC,MAAM,EAAE,YAAY,CAAC;AAAA,MAC/B;AAEA,iBAAW,KAAK,qBAAqB;AACnC,YAAI,UAAU,IAAI,EAAE,YAAY,CAAC,EAAG,QAAO;AAAA,MAC7C;AAAA,IACF;AAGA,WAAO,oBAAoB,CAAC,KAAK;AAAA,EACnC;AAAA,EAEA,MAAc,yBACZ,SAC+B;AAC/B,UAAM,MAAM,KAAK,IAAI;AACrB,UAAM,SAAS,KAAK,2BAA2B,IAAI,OAAO;AAE1D,QAAI,UAAU,MAAM,OAAO,cAAc,IAAI,KAAQ;AACnD,aAAO,OAAO;AAAA,IAChB;AAEA,UAAM,cAAc,MAAM,KAAK,uBAAuB,OAAO;AAC7D,UAAM,mBAAmB,eAAe,CAAC,GAAG;AAAA,MAAQ,CAAC,MACnD,KAAK,mCAAmC,CAAC;AAAA,IAC3C;AAGA,UAAM,WAAW,CAAC,UAAU,WAAW,WAAW,QAAQ,SAAS;AACnE,UAAM,MAAM,CAAC,GAAG,iBAAiB,GAAG,QAAQ,EACzC,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EACnB,OAAO,OAAO;AAEjB,UAAM,UAAoB,CAAC;AAC3B,UAAM,OAAO,oBAAI,IAAY;AAC7B,eAAW,KAAK,KAAK;AACnB,YAAM,MAAM,EAAE,YAAY;AAC1B,UAAI,KAAK,IAAI,GAAG,EAAG;AACnB,WAAK,IAAI,GAAG;AACZ,cAAQ,KAAK,CAAC;AAAA,IAChB;AAEA,UAAM,QAAQ,QAAQ,SAAS,IAAI,UAAU;AAC7C,SAAK,2BAA2B;AAAA,MAC9B;AAAA,MACA,SAAS,OAAO,EAAE,OAAO,aAAa,IAAI,IAAI,EAAE,aAAa,IAAI;AAAA,IACnE;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,YACJ,SACA,SAWiB;AACjB,UAAM,QAAQ;AAId,QAAI,YAAY,QAAW;AACzB,YAAM,YAAY,SAAS;AAC3B,YAAM,eAAe,WAAW,gBAAgB;AAChD,YAAM,cAAc,WAAW,eAAe;AAC9C,YAAM,cAAc,WAAW,eAAe;AAC9C,YAAM,aAAa,OAAO,WAAW,WAAW,IAAI;AACpD,YAAM,UAAU,KAAK;AAAA,QACnB;AAAA,QACA,KAAK,IAAI,MAAM,OAAO,SAAS,UAAU,IAAI,aAAa,IAAI;AAAA,MAChE;AACA,YAAMC,SAAQ,SAAS,UAAU,QAAQ,WAAW,UAAU;AAC9D,YAAMC,cAA6B,SAAS,cAAc;AAC1D,YAAMC,aAAY,SAAS,aAAa;AAGxC,YAAM,OAAO,MAAM,KAAK,YAAY,cAAc;AAAA,QAChD,OAAAF;AAAA,QACA,SAAS;AAAA,QACT,YAAAC;AAAA,QACA,WAAAC;AAAA,MACF,CAAC;AAKD,YAAM,uBAAuBF,SAAQ,eAAe;AACpD,YAAM,OAAO,MAAM,KAAK,YAAY,sBAAsB;AAAA,QACxD,OAAAA;AAAA,QACA,SAASA,SAAQ,cAAc;AAAA,QAC/B,YAAAC;AAAA,QACA,WAAAC;AAAA,MACF,CAAC;AAED,UAAI;AACJ,UAAI;AACJ,UAAI;AACF,kBAAU,MAAM,KAAK,KAAK,IAAI;AAAA,MAChC,QAAQ;AAEN,eAAO;AAAA,MACT;AAEA,UAAI;AACF,kBAAU,MAAM,KAAK,KAAK,IAAI;AAAA,MAChC,QAAQ;AAEN,eAAO;AAAA,MACT;AAEA,YAAM,QAAQ,QAAQ,OAAO;AAC7B,YAAM,QAAQ,QAAQ,OAAO;AAC7B,YAAM,QAAQ,QAAQ,OAAO;AAC7B,YAAM,QAAQ,QAAQ,OAAO;AAE7B,YAAM,cAAc;AAAA,QAClB;AAAA,QACA;AAAA,QACA,WAAW;AAAA,QACX;AAAA,MACF;AAEA,YAAM,aAAa,QAAQ,KAAK,QAAQ,IAAI,QAAQ,QAAQ,KAAK;AAGjE,UAAI,OAAO,KAAK,IAAI,GAAG,KAAK,MAAM,QAAQ,OAAO,CAAC;AAClD,UAAI,OAAO,KAAK,IAAI,GAAG,KAAK,MAAM,OAAO,UAAU,CAAC;AAGpD,YAAM,eAAe,KAAK,IAAI,GAAG,KAAK,MAAM,QAAQ,OAAO,CAAC;AAC5D,UAAI,OAAO,cAAc;AACvB,eAAO;AACP,eAAO,KAAK,IAAI,GAAG,KAAK,MAAM,OAAO,UAAU,CAAC;AAAA,MAClD;AAGA,aAAO,KAAK,IAAI,MAAM,KAAK;AAC3B,aAAO,KAAK,IAAI,MAAM,KAAK;AAE3B,YAAM,EAAE,MAAM,IAAI,IAAI,4BAA4B;AAAA,QAChD,UAAU;AAAA,QACV,WAAW;AAAA,QACX,YAAY;AAAA,QACZ,UAAU;AAAA,QACV,WAAW;AAAA,QACX,QAAQ;AAAA,MACV,CAAC;AAGD,cAAQ,OAAO,EAAE,GAAG,MAAM,GAAG,KAAK,CAAC;AAEnC,cAAQ,UAAU,SAAS,MAAM,GAAG;AACpC,aAAO,MAAM,QAAQ,UAAU,SAAS,MAAM,EAAE,SAAS,GAAG,CAAC;AAAA,IAC/D;AAGA,UAAM,KAAK,YAAY,SAAY,KAAK,iBAAiB,OAAO,IAAI;AACpE,UAAM,UAAoC,SAAS,WAAW;AAC9D,UAAM,QAAQ,SAAS,UAAU;AACjC,UAAM,aAA6B,SAAS,cAAc;AAC1D,UAAM,YAAY,SAAS,aAAa;AAQxC,UAAM,eAAe,CAAC,WAIpB,wCAAwC,OAAO,YAAY,6BAA6B,OAAO,YAAY,oEAAoE,UAAU;AAE3L,UAAM,KAAK,OAAO,MAAM;AASxB,QAAI,OAAO;AACT,YAAM,yBAAyB,CAAC,IAAI,KAAK,CAAC;AAC1C,YAAM,yBACJ,YAAY,YACR,CAAC,IAAI,CAAC,IACN,CAAC,CAAC;AAGR,YAAM,oCAA+D;AAAA,QACnE;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAEA,UAAI;AACJ,iBAAW,2BAA2B,mCAAmC;AACvE,mBAAW,gBAAgB,wBAAwB;AACjD,qBAAW,MAAM,wBAAwB;AACvC,gBAAI;AACF,qBAAO,MAAM,KAAK,OAAO,WAAW;AAAA,gBAClC;AAAA,gBACA,SAAS;AAAA,gBACT,GAAI,4BAA4B,SAC5B,EAAE,mBAAmB,wBAAwB,IAC7C,CAAC;AAAA,gBACL,YAAY,aAAa,EAAE,cAAc,cAAc,GAAG,CAAC;AAAA,gBAC3D,cAAc,yBAAyB,YAAY;AAAA,gBACnD;AAAA,cACF,CAAC;AAAA,YACH,SAAS,GAAG;AACV,wBAAU;AAAA,YACZ;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAEA,YAAM,mBAAmB,QACrB,UACA,IAAI,MAAM,OAAO,WAAW,oBAAoB,CAAC;AAAA,IACvD;AAEA,WAAO,MAAM,KAAK,OAAO,WAAW;AAAA,MAClC;AAAA,MACA,SAAS;AAAA,MACT,YAAY,aAAa,EAAE,cAAc,IAAI,cAAc,GAAG,CAAC;AAAA,MAC/D,cAAc,yBAAyB,EAAE;AAAA,MACzC;AAAA,IACF,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAiCA,MAAM,cAAc,QAAuD;AACzE,WAAO,MAAM,KAAK,2BAA2B,YAAY;AACvD,YAAM,MAAM,KAAK,OAAO,iBAAiB;AACzC,YAAM,SAAS,KAAK;AAEpB,YAAM,UAAU,KAAK,iBAAiB,OAAO,WAAW,CAAC;AAGzD,YAAM,MAAM,MAAM,KAAK,uBAAuB,SAAS,OAAO,GAAG;AAIjE,YAAM,QAAQ,OAAO;AACrB,YAAM,gBAAgB,IAAI;AAAA,QACxB,MAAM,YAAY;AAAA,QAClB,MAAM,SAAS;AAAA,QACf,MAAM,QAAQ;AAAA,QACd;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAEA,YAAM,MACJ,OAAO,IAAI,QAAQ,IAAI,cAAc,QAAQ,IACzC,gBACA,OAAO;AAEb;AAAA,QACE;AAAA,QACA;AAAA,QACA;AAAA,QACA,gBAAgB,MAAM,YAAY,CAAC,SAAS,IAAI,YAAY,CAAC;AAAA,MAC/D;AAEA;AAAA,QACE;AAAA,QACA;AAAA,QACA;AAAA,QACA,yBAAyB,OAAO,KAAK,GAAG;AAAA,MAC1C;AAEA,YAAM,aAAa,OAAO,cAAc;AACxC,YAAM,aACJ,OAAO,cACP;AACF,YAAM,gBAAgB,OAAO,iBAAiB;AAE9C,YAAM,0BACJ,KAAK,wCAAwC,OAAO;AAEtD,YAAM,QAAQ,MAAM,8BAA8B;AAAA,QAChD,SAAS,CAAC,MACR,KAAK,QAAQ;AAAA,UACX,GAAG;AAAA,UACH,GAAI,2BAA2B,QAAQ,EAAE,WAAW,OAChD,EAAE,mBAAmB,wBAAwB,IAC7C,CAAC;AAAA,QACP,CAAC;AAAA,QACH;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,GAAI,OAAO,aAAa,OAAO,EAAE,WAAW,OAAO,UAAU,IAAI,CAAC;AAAA,MACpE,CAAC;AAED,YAAM,SAAS,qBAAqB,KAAK;AACzC;AAAA,QACE;AAAA,QACA;AAAA,QACA;AAAA,QACA,0BAA0B,OAAO,MAAM,uBAAuB,MAAM,MAAM;AAAA,MAC5E;AACA,aAAO;AAAA,IACT,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgBA,MAAc,qCAAqC,QAYhD;AACD,UAAM,UAAU,OAAO;AACvB,UAAM,aAAa,OAAO;AAC1B,UAAM,SAAS,OAAO,UAAU,KAAK;AAKrC,UAAM,aAAa,OAAO,WACtB,UAAU,OAAO,QAAQ,KACzB,qBAAqB,OAAO,IAAI,KAAK,IAAI,CAAC;AAC9C,UAAM,EAAE,QAAQ,iBAAiB,SAAS,uBAAuB,IAC/D,MAAM,KAAK,uBAAuB,YAAY,MAAM;AAGtD,UAAM,MAAM,MAAM,KAAK,uBAAuB,SAAS,MAAS;AAKhE,UAAM,aAAa,iCAAiC;AAAA,MAClD;AAAA,MACA,cAAc;AAAA;AAAA,MACd,MAAM,OAAO;AAAA,MACb;AAAA,MACA;AAAA,IACF,CAAC;AAGD,UAAM,SAAS;AACf,oBAAgB;AAAA,MACd;AAAA,MACA;AAAA,IACF;AAEA,UAAM,UAAyB,eAAe,cAAc,QAAQ;AACpE,UAAM,SAAS,IAAI,oBAAoB;AAAA,MACrC,QAAQ;AAAA,MACR;AAAA,MACA;AAAA,MACA,SAAS;AAAA,MACT,OAAO;AAAA,MACP;AAAA,MACA,qBAAqB;AAAA,MACrB;AAAA,IACF,CAAC;AAED,QAAI,UAAU;AACd,QAAI;AACF,YAAM,OAAO,MAAM;AAOnB,YAAM,iBAAiB,gBAAgB,kBAAkB;AACzD,YAAM,QAAQ,MAAM,gBAAgB,UAAU;AAAA,QAC5C,OAAO;AAAA,QACP;AAAA,QACA,mBAAmB;AAAA,QACnB;AAAA,QACA,cAAc;AAAA,QACd,gBAAgB;AAAA,QAChB,WAAW,OAAO;AAAA,QAClB,UAAU;AAAA,MACZ,CAAC;AAED,UAAI,MAAM,OAAO,iBAAiB,KAAK;AACrC,cAAM,IAAI;AAAA,UACR,6CAA6C,MAAM,OAAO,YAAY;AAAA,QACxE;AAAA,MACF;AAEA,gBAAU;AAAA,IACZ,SAAS,GAAG;AACV,UAAI;AACF,cAAM,OAAO,KAAK;AAAA,MACpB,QAAQ;AAAA,MAER;AACA,UAAI;AACF,wBAAgB;AAAA,UACd;AAAA,UACA;AAAA,QACF;AAAA,MACF,QAAQ;AAAA,MAER;AAEA,YAAM,uBAAuB;AAC7B,YAAM;AAAA,IACR;AAGA,QAAI,WAAW;AAEf,UAAM,OAAO,YAA2B;AACtC,UAAI,SAAU;AACd,iBAAW;AAEX,YAAM,WAAW,gCAAgC,OAAO,QAAQ;AAChE,UAAI,WAAW,UAAU;AACvB,YAAI;AACF,gBAAM,UAAU,yBAAyB;AAAA,YACvC;AAAA,YACA,MAAM;AAAA,YACN;AAAA,UACF,CAAC;AAED,gBAAM,gBAAgB,QAAQ;AAAA,YAC5B,OAAO;AAAA,YACP;AAAA,YACA,YAAY;AAAA,YACZ,cAAc;AAAA,YACd,WAAW;AAAA,YACX,UAAU;AAAA,UACZ,CAAC;AAAA,QACH,QAAQ;AAAA,QAER;AAAA,MACF;AAEA,UAAI;AACF,wBAAgB;AAAA,UACd;AAAA,UACA;AAAA,QACF;AAAA,MACF,QAAQ;AAAA,MAER;AAEA,YAAM,OAAO,KAAK;AAGlB,YAAM,uBAAuB;AAAA,IAC/B;AAIA,UAAM,eAAe,CAAC,WAAmB;AACvC,UAAI,SAAU;AACd,cAAQ;AAAA,QACN,uCAAuC,UAAU,KAAK,MAAM;AAAA,MAC9D;AACA,WAAK,KAAK;AAAA,IACZ;AAEA,WAAO,KAAK,SAAS,MAAM,aAAa,eAAe,CAAC;AACxD,WAAO;AAAA,MAAK;AAAA,MAAS,CAAC,MACpB,aAAa,iBAAiB,GAAG,WAAW,CAAC,EAAE;AAAA,IACjD;AAGA,oBAAgB;AAAA,MAAK;AAAA,MAAS,CAAC,MAC7B,aAAa,iBAAiB,GAAG,WAAW,CAAC,EAAE;AAAA,IACjD;AACA,oBAAgB,KAAK,SAAS,MAAM,aAAa,eAAe,CAAC;AAEjE,WAAO,EAAE,QAAQ,QAAQ,KAAK;AAAA,EAChC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,MAAc,8BAA8B,QAYzC;AACD,UAAM,UAAU,OAAO;AACvB,UAAM,aAAa,OAAO;AAC1B,UAAM,SAAS,OAAO,UAAU,KAAK;AAKrC,UAAM,aAAa,OAAO,WACtB,UAAU,OAAO,QAAQ,KACzB,cAAc,OAAO,IAAI,KAAK,IAAI,CAAC;AACvC,UAAM,EAAE,QAAQ,iBAAiB,SAAS,uBAAuB,IAC/D,MAAM,KAAK,uBAAuB,YAAY,MAAM;AAGtD,QAAI;AACJ,QAAI;AACF,YAAM,MAAM,KAAK,uBAAuB,SAAS,MAAS;AAAA,IAC5D,QAAQ;AAAA,IAER;AAGA,UAAM,0BACJ,KAAK,wCAAwC,OAAO;AAItD,UAAM,aAAa,+BAA+B;AAAA,MAChD;AAAA,MACA,cAAc;AAAA;AAAA,MACd,IAAI,OAAO;AAAA,MACX,GAAI,MAAM,EAAE,IAAI,IAAI,CAAC;AAAA,MACrB;AAAA,IACF,CAAC;AAGD,UAAM,SAAS;AACf,oBAAgB;AAAA,MACd;AAAA,MACA;AAAA,IACF;AAEA,UAAM,UAAyB,eAAe,cAAc,QAAQ;AACpE,UAAM,SAAS,IAAI,oBAAoB;AAAA,MACrC,QAAQ;AAAA,MACR;AAAA,MACA;AAAA,MACA,SAAS;AAAA,MACT,OAAO;AAAA,MACP;AAAA,MACA,qBAAqB;AAAA,MACrB;AAAA,IACF,CAAC;AAED,QAAI,UAAU;AACd,QAAI;AACF,YAAM,OAAO,MAAM;AAKnB,YAAM,QAAQ,2BAA2B;AACzC,YAAM,oBAAoB,QACrB,2BAA2B,KAC5B,gBAAgB,kBAAkB;AAEtC,YAAM,QAAQ,MAAM,gBAAgB,UAAU;AAAA,QAC5C,OAAO;AAAA,QACP;AAAA,QACA;AAAA,QACA;AAAA;AAAA,QAEA,cAAc;AAAA,QACd,cAAc;AAAA,QACd,gBAAgB;AAAA,QAChB,WAAW,OAAO;AAAA,QAClB,UAAU;AAAA,MACZ,CAAC;AAED,UAAI,MAAM,OAAO,iBAAiB,KAAK;AACrC,cAAM,IAAI;AAAA,UACR,sCAAsC,MAAM,OAAO,YAAY,uBAAuB,iBAAiB;AAAA,QACzG;AAAA,MACF;AAEA,gBAAU;AAAA,IACZ,SAAS,GAAG;AACV,UAAI;AACF,cAAM,OAAO,KAAK;AAAA,MACpB,QAAQ;AAAA,MAER;AACA,UAAI;AACF,wBAAgB;AAAA,UACd;AAAA,UACA;AAAA,QACF;AAAA,MACF,QAAQ;AAAA,MAER;AAEA,YAAM,uBAAuB;AAC7B,YAAM;AAAA,IACR;AAGA,QAAI,WAAW;AAEf,UAAM,OAAO,YAA2B;AACtC,UAAI,SAAU;AACd,iBAAW;AAEX,YAAM,WAAW,gCAAgC,OAAO,QAAQ;AAChE,UAAI,WAAW,UAAU;AACvB,YAAI;AACF,gBAAM,UAAU,yBAAyB;AAAA,YACvC;AAAA,YACA,MAAM;AAAA,YACN;AAAA,UACF,CAAC;AAED,gBAAM,gBAAgB,QAAQ;AAAA,YAC5B,OAAO;AAAA,YACP;AAAA,YACA,YAAY;AAAA,YACZ,cAAc;AAAA,YACd,WAAW;AAAA,YACX,UAAU;AAAA,UACZ,CAAC;AAAA,QACH,QAAQ;AAAA,QAER;AAAA,MACF;AAEA,UAAI;AACF,wBAAgB;AAAA,UACd;AAAA,UACA;AAAA,QACF;AAAA,MACF,QAAQ;AAAA,MAER;AAEA,YAAM,OAAO,KAAK;AAGlB,YAAM,uBAAuB;AAAA,IAC/B;AAIA,UAAM,eAAe,CAAC,WAAmB;AACvC,UAAI,SAAU;AACd,cAAQ;AAAA,QACN,uCAAuC,UAAU,KAAK,MAAM;AAAA,MAC9D;AACA,WAAK,KAAK;AAAA,IACZ;AAEA,WAAO,KAAK,SAAS,MAAM,aAAa,eAAe,CAAC;AACxD,WAAO;AAAA,MAAK;AAAA,MAAS,CAAC,MACpB,aAAa,iBAAiB,GAAG,WAAW,CAAC,EAAE;AAAA,IACjD;AAGA,oBAAgB;AAAA,MAAK;AAAA,MAAS,CAAC,MAC7B,aAAa,iBAAiB,GAAG,WAAW,CAAC,EAAE;AAAA,IACjD;AACA,oBAAgB,KAAK,SAAS,MAAM,aAAa,eAAe,CAAC;AAEjE,WAAO,EAAE,QAAQ,QAAQ,KAAK;AAAA,EAChC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,MAAM,2BAA2B,QAyB9B;AACD,UAAM,KAAK,OAAO,MAAM;AAGxB,UAAM,UAAU,KAAK,iBAAiB,OAAO,WAAW,CAAC;AAEzD,UAAM,aAAa,KAAK,gCAAgC,OAAO,QAAQ;AACvE,UAAM,YAAY,OAAO,aAAa;AAMtC,UAAM,QAAQ,OAAO,SAAU,MAAM,KAAK,YAAY;AAEtD,UAAM,eAAe;AAAA,MACnB;AAAA,MACA,UAAU,OAAO;AAAA,MACjB;AAAA,MACA;AAAA,MACA,GAAI,OAAO,UAAU,OAAO,EAAE,QAAQ,OAAO,OAAO,IAAI,CAAC;AAAA,MACzD,GAAI,OAAO,YAAY,OAAO,EAAE,UAAU,OAAO,SAAS,IAAI,CAAC;AAAA,IACjE;AAEA,UAAM,SAAS,QACX,MAAM,KAAK,8BAA8B,YAAY,IACrD,MAAM,KAAK,qCAAqC,YAAY;AAEhE,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,MAAM,0BAA0B,QAIN;AACxB,UAAM,MAAM,MAAM,KAAK,QAAQ;AAAA,MAC7B,OAAO;AAAA,MACP,GAAI,QAAQ,WAAW,OAAO,EAAE,SAAS,OAAO,QAAQ,IAAI,CAAC;AAAA,MAC7D,GAAI,QAAQ,cAAc,OAAO,EAAE,YAAY,OAAO,WAAW,IAAI,CAAC;AAAA,MACtE,GAAI,QAAQ,aAAa,OAAO,EAAE,WAAW,OAAO,UAAU,IAAI,CAAC;AAAA,IACrE,CAAC;AACD,WAAO,uBAAuB,GAAG;AAAA,EACnC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,yBAAyB,QAOI;AACjC,UAAM,mBACJ,OAAO,WAAW,KAAK,OAAO,uBAAuB,KAAK;AAE5D,UAAM,aAAa,MAAM,KAAK,cAAc;AAAA,MAC1C,SAAS;AAAA,MACT,UAAU,OAAO;AAAA,MACjB,GAAI,OAAO,aAAa,EAAE,YAAY,OAAO,WAAW,IAAI,CAAC;AAAA,MAC7D,GAAI,OAAO,kBAAkB,SACzB,EAAE,eAAe,OAAO,cAAc,IACtC,CAAC;AAAA,IACP,CAAC;AAED,WAAO,EAAE,WAAW;AAAA,EACtB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,MAAc,uBACZ,SACA,aACiB;AACjB,UAAM,MAAM,KAAK,OAAO,iBAAiB;AACzC,UAAM,SAAS,KAAK;AAEpB;AAAA,MACE;AAAA,MACA;AAAA,MACA;AAAA,MACA,6BAA6B,WAAW,cAAc,KAAK,GAAG;AAAA,IAChE;AAEA,UAAM,kBAAkB,aAAa,KAAK;AAC1C,QAAI,iBAAiB;AACnB;AAAA,QACE;AAAA,QACA;AAAA,QACA;AAAA,QACA,uBAAuB,eAAe;AAAA,MACxC;AACA,aAAO;AAAA,IACT;AAEA,UAAM,aAAa,MAAM,KAAK,mCAAmC,OAAO;AACxE,QAAI,YAAY;AACd;AAAA,QACE;AAAA,QACA;AAAA,QACA;AAAA,QACA,0BAA0B,UAAU;AAAA,MACtC;AACA,aAAO;AAAA,IACT;AAEA,UAAM,aAAa,KAAK,KAAK,KAAK;AAClC,QAAI,YAAY;AACd;AAAA,QACE;AAAA,QACA;AAAA,QACA;AAAA,QACA,yBAAyB,UAAU;AAAA,MACrC;AACA,aAAO;AAAA,IACT;AAEA,QAAI,KAAK,YAAY;AACnB;AAAA,QACE;AAAA,QACA;AAAA,QACA;AAAA,QACA,0CAA0C,OAAO;AAAA,MACnD;AACA,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAEA;AAAA,MACE;AAAA,MACA;AAAA,MACA;AAAA,MACA,0DAA0D,OAAO;AAAA,IACnE;AACA,UAAM,aAAa,MAAM,KAAK,+BAA+B,OAAO;AACpE,QAAI,WAAY,QAAO;AAEvB;AAAA,MACE;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AACA,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,gBAAgB,SAAiB,KAAmB;AAC1D,UAAM,WAAW,KAAK,gBAAgB,IAAI,OAAO;AACjD,UAAM,MAAM,KAAK,IAAI;AACrB,UAAM,OAAO,YAAY,EAAE,MAAM,IAAI,KAAK,IAAI,OAAO,IAAI,aAAa,IAAI;AAC1E,SAAK,gBAAgB,IAAI,SAAS,EAAE,GAAG,MAAM,KAAK,aAAa,IAAI,CAAC;AAAA,EACtE;AAAA,EAEA,MAAc,mCACZ,SAC6B;AAC7B,UAAM,MAAM,KAAK,OAAO,iBAAiB;AACzC,UAAM,SAAS,KAAK;AAEpB,UAAM,WAAW,KAAK,8BAA8B,OAAO;AAC3D,QAAI,UAAU;AACZ;AAAA,QACE;AAAA,QACA;AAAA,QACA;AAAA,QACA,0CAA0C,QAAQ;AAAA,MACpD;AACA,aAAO;AAAA,IACT;AAEA,QAAI,KAAK,YAAY;AACnB;AAAA,QACE;AAAA,QACA;AAAA,QACA;AAAA,QACA,yEAAyE,OAAO;AAAA,MAClF;AACA,aAAO;AAAA,IACT;AAEA,QAAI;AACF;AAAA,QACE;AAAA,QACA;AAAA,QACA;AAAA,QACA,+EAA+E,OAAO;AAAA,MACxF;AACA,YAAM,eAAe,MAAM,yCAAyC;AAAA,QAClE;AAAA,QACA,OAAO,MAAM,KAAK,OAAO,MAAM;AAAA,QAC/B,kBAAkB,MAAM,KAAK,OAAO,iBAAiB;AAAA,MACvD,CAAC;AAED;AAAA,QACE;AAAA,QACA;AAAA,QACA;AAAA,QACA,uCAAuC,OAAO,QAAQ,gBAAgB,WAAW;AAAA,MACnF;AAEA,UAAI,cAAc;AAChB,aAAK,gBAAgB,SAAS,YAAY;AAC1C;AAAA,UACE;AAAA,UACA;AAAA,UACA;AAAA,UACA,gDAAgD,YAAY;AAAA,QAC9D;AACA,eAAO;AAAA,MACT;AAAA,IACF,SAAS,GAAG;AACV;AAAA,QACE;AAAA,QACA;AAAA,QACA;AAAA,QACA,uCAAuC,kBAAkB,CAAC,CAAC;AAAA,MAC7D;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,+BACZ,SAC6B;AAC7B,QAAI,KAAK,WAAY,QAAO;AAC5B,UAAM,MAAM,KAAK,OAAO,iBAAiB;AACzC,UAAM,SAAS,KAAK;AAEpB,UAAM,QAAQ,CAAC,YACb,mBAAmB,KAAK,QAAQ,0BAA0B,OAAO;AAEnE,UAAM,gBAAgB,MAAM,+BAAmC;AAAA,MAC7D;AAAA;AAAA,MAEA,SAAS,MAAM,KAAK,QAAQ;AAAA,MAC5B,UAAU,MAAM,KAAK,OAAO,MAAM;AAAA,MAClC,WAAW,MAAM,KAAK,OAAO,KAAK,UAAU,CAAC,CAAC;AAAA,MAC9C,eAAe,MAAM,KAAK,OAAO,WAAW;AAAA,MAC5C,SAAS,CAAC,MAAM,KAAK,QAAQ,CAAC;AAAA,MAC9B;AAAA,IACF,CAAC;AAED,QAAI,eAAe;AACjB,WAAK,MAAM;AACX,YAAM,0CAA0C,aAAa,EAAE;AAC/D,aAAO;AAAA,IACT;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAkBA,MAAM,sBAAsB,QAeU;AAEpC,QAAI,CAAC,KAAK,4BAA4B;AACpC,WAAK,6BAA6B,KAAK,2BAA2B,MAAM;AACxE,UAAI;AACF,eAAO,MAAM,KAAK;AAAA,MACpB,UAAE;AACA,aAAK,6BAA6B;AAElC,aAAK,gCAAgC;AAAA,MACvC;AAAA,IACF;AAGA,WAAO,IAAI,QAAkC,CAAC,SAAS,WAAW;AAChE,WAAK,wBAAwB,KAAK,EAAE,QAAQ,SAAS,OAAO,CAAC;AAAA,IAC/D,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKQ,kCAAwC;AAC9C,UAAM,OAAO,KAAK,wBAAwB,MAAM;AAChD,QAAI,CAAC,KAAM;AAEX,SAAK,6BAA6B,KAAK;AAAA,MACrC,KAAK;AAAA,IACP;AACA,SAAK,2BACF,KAAK,CAAC,WAAW;AAChB,WAAK,QAAQ,MAAM;AAAA,IACrB,CAAC,EACA,MAAM,CAAC,UAAU;AAChB,WAAK,OAAO,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC,CAAC;AAAA,IACvE,CAAC,EACA,QAAQ,MAAM;AACb,WAAK,6BAA6B;AAClC,WAAK,gCAAgC;AAAA,IACvC,CAAC;AAAA,EACL;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAc,2BAA2B,QAQH;AACpC,UAAM,KAAK,OAAO,MAAM;AAExB,UAAM,MAAM,KAAK,OAAO,iBAAiB;AACzC,UAAM,SAAS,KAAK;AACpB,UAAM,QAAQ,CAAC,YACb,mBAAmB,KAAK,QAAQ,yBAAyB,OAAO;AAElE,UAAM,UAAU,KAAK,iBAAiB,OAAO,OAAO;AACpD,UAAM,WAAW,OAAO,YAAY;AACpC,UAAM,iBAAiB,aAAa,SAAS,eAAe;AAC5D,UAAM,YAAY,OAAO,aAAa;AACtC,UAAM,OAAO,OAAO;AAMpB,UAAM,QAAQ,OAAO,SAAU,MAAM,KAAK,YAAY;AACtD,UAAM,0BAA0B,QAC3B,KAAK,wCAAwC,OAAO,KAAK,MAC1D;AAIJ,UAAM,UAAU,OAAO,WAAW,IAAI,KAAK,KAAK,QAAQ,IAAI,GAAM;AAKlE,QAAI;AACJ,QAAI,OAAO;AAET,kBAAY,OAAO;AAEnB,UAAI,CAAC,WAAW;AACd,cAAM,WAAW,KAAK,4BAA4B;AAClD,cAAM,cAAc,SAAS,IAAI,OAAO;AACxC,oBAAY,aAAa;AAAA,MAC3B;AACA,UAAI,WAAW;AACb;AAAA,UACE,2BAA2B,SAAS,oBAAoB,OAAO;AAAA,QACjE;AAAA,MACF,OAAO;AACL;AAAA,UACE,8CAA8C,OAAO;AAAA,QACvD;AAAA,MACF;AAAA,IACF;AAQA,UAAM,MAAM;AAAA;AAAA;AAAA,aAGH,OAAO,eACd,SAAS,YACL;AAAA,OACH,SAAS,WACN,EACN;AAAA,cACU,cAAc;AAAA;AAAA,QAEpB,KAAK,YAAY,CAAC;AAAA,SACjB,KAAK,SAAS,IAAI,CAAC;AAAA,OACrB,KAAK,QAAQ,CAAC;AAAA,QACb,KAAK,SAAS,CAAC;AAAA,UACb,KAAK,WAAW,CAAC;AAAA,UACjB,KAAK,WAAW,CAAC;AAAA;AAAA;AAAA,QAGnB,QAAQ,YAAY,CAAC;AAAA,SACpB,QAAQ,SAAS,IAAI,CAAC;AAAA,OACxB,QAAQ,QAAQ,CAAC;AAAA,QAChB,QAAQ,SAAS,CAAC;AAAA,UAChB,QAAQ,WAAW,CAAC;AAAA,UACpB,QAAQ,WAAW,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAQ1B;AAAA,MACE,yBAAyB,OAAO,mBAAmB,cAAc,SAAS,KAAK,YAAY,CAAC,cAAc,SAAS,UAAU,KAAK,eAAe,uBAAuB,QAAQ,aAAa,KAAK;AAAA,IACpM;AACA,UAAM;AAAA,EAAsB,GAAG,EAAE;AAMjC,QAAI;AACJ,QAAI;AACF,gBAAU,MAAM,KAAK,OAAO,uBAAuB;AAAA,QACjD,OAAO;AAAA;AAAA,QAEP,GAAI,SAAS,2BAA2B,OACpC,EAAE,mBAAmB,wBAAwB,IAC7C,CAAC;AAAA;AAAA,QAEL,gBAAgB;AAAA,QAChB,cAAc;AAAA,QACd,YAAY;AAAA,QACZ,YAAY;AAAA,QACZ;AAAA;AAAA,QAEA,YAAY;AAAA,QACZ,cAAc;AAAA,MAChB,CAAC;AACD,YAAM,wBAAwB;AAAA,IAChC,SAAS,GAAG;AACV,YAAM,MAAM,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC;AACrD,YAAM,wBAAwB,GAAG,EAAE;AACnC,YAAM;AAAA,IACR;AAGA,QAAI,QAAQ,SAAS,IAAI;AACvB,YAAM,IAAI;AAAA,QACR,mCAAmC,QAAQ,MAAM;AAAA,MACnD;AAAA,IACF;AAEA,UAAM,QAAQ,QAAQ,SAAS,GAAG,CAAC,EAAE,SAAS,OAAO;AACrD,UAAM,kBAAkB,oBAAI,IAAI,CAAC,QAAQ,MAAM,CAAC;AAIhD,QAAI,UAAU,QAAQ;AACpB;AAAA,QACE;AAAA,MACF;AAGA,YAAMC,YAAW,QAAQ,aAAa,CAAC;AACvC,YAAMC,SAAQ,QAAQ,SAAS,GAAG,IAAID,SAAQ;AAE9C,YAAM,iBAAiB,CAAC,QAAwB;AAC9C,cAAM,UAAU,KAAK,IAAI,IAAI,SAAS,GAAG,KAAK,IAAI;AAClD,YAAI,QAAQ;AACZ,YAAI,QAAQ;AACZ,iBAAS,IAAI,GAAG,IAAI,SAAS,KAAK;AAChC,cAAI,IAAI,CAAC,MAAM,KAAQ,IAAI,IAAI,CAAC,MAAM,EAAM;AAC5C,cAAI,IAAI,IAAI,CAAC,MAAM,GAAM;AACvB,oBAAQ;AACR,oBAAQ;AACR;AAAA,UACF;AACA,cAAI,IAAI,IAAI,CAAC,MAAM,KAAQ,IAAI,IAAI,CAAC,MAAM,GAAM;AAC9C,oBAAQ;AACR,oBAAQ;AACR;AAAA,UACF;AAAA,QACF;AAEA,YAAI,QAAQ,EAAG,QAAO;AACtB,cAAM,iBAAiB,QAAQ;AAC/B,YAAI,kBAAkB,IAAI,OAAQ,QAAO;AAEzC,cAAM,KAAK,IAAI,cAAc;AAC7B,YAAI,OAAO,OAAW,QAAO;AAC7B,cAAM,WAAW,KAAK;AACtB,cAAM,WAAY,MAAM,IAAK;AAE7B,YAAI,CAAC,GAAG,GAAG,GAAG,CAAC,EAAE,SAAS,QAAQ,EAAG,QAAO;AAC5C,YAAI,CAAC,IAAI,IAAI,IAAI,IAAI,EAAE,EAAE,SAAS,QAAQ,EAAG,QAAO;AAEpD,eAAO;AAAA,MACT;AAEA,YAAME,YAAW,eAAeD,MAAK;AAErC,aAAO;AAAA,QACL,OAAAA;AAAA,QACA,UAAAC;AAAA,QACA,aAAaD,OAAM;AAAA,QACnB,YAAY,CAAC;AAAA,MACf;AAAA,IACF;AAEA,QAAI,CAAC,gBAAgB,IAAI,KAAK,GAAG;AAC/B,YAAM,IAAI;AAAA,QACR,qGAAqG,KAAK;AAAA,MAC5G;AAAA,IACF;AAGA,QAAI,kBAAkB;AACtB,QAAI;AACF,YAAM,YAAY,QAAQ,aAAa,CAAC;AACxC,UACE,OAAO,SAAS,SAAS,KACzB,aAAa,MACb,aAAa,QACb,aAAa,QAAQ,QACrB;AACA,0BAAkB;AAAA,MACpB;AAAA,IACF,QAAQ;AAAA,IAER;AAEA,UAAM,eAAe,QAAQ,SAAS,GAAG,eAAe;AAGxD,UAAM,QAAQ,aAAa,aAAa,CAAC;AACzC,UAAM,SAAS,aAAa,aAAa,EAAE;AAC3C,UAAM,YAAY,aAAa,SAAS,KAAK,aAAa,EAAE,IAAI;AAGhE,UAAM,kBAAkB,QAAQ,SAAS,eAAe;AACxD,UAAM,aAAa,OAAO,KAAK,QAAQ,OAAO;AAC9C,QAAI,kBAAkB;AAEtB,aAAS,IAAI,GAAG,KAAK,gBAAgB,SAAS,GAAG,KAAK;AACpD,UACE,gBAAgB,CAAC,MAAM,WAAW,CAAC,KACnC,gBAAgB,IAAI,CAAC,MAAM,WAAW,CAAC,KACvC,gBAAgB,IAAI,CAAC,MAAM,WAAW,CAAC,KACvC,gBAAgB,IAAI,CAAC,MAAM,WAAW,CAAC,GACvC;AACA,0BAAkB;AAClB;AAAA,MACF;AAAA,IACF;AAEA,QAAI,oBAAoB,IAAI;AAG1B,YAAMA,SAAQ,QAAQ,SAAS,eAAe;AAC9C,UAAIA,OAAM,WAAW,GAAG;AACtB,cAAM,IAAI;AAAA,UACR,qGAAqG,gBAAgB,SAAS,GAAG,EAAE,EAAE,SAAS,KAAK,CAAC;AAAA,QACtJ;AAAA,MACF;AAEA,YAAM,iBAAiB,CAAC,QAAwB;AAE9C,cAAM,UAAU,KAAK,IAAI,IAAI,SAAS,GAAG,KAAK,IAAI;AAClD,YAAI,QAAQ;AACZ,YAAI,QAAQ;AACZ,iBAAS,IAAI,GAAG,IAAI,SAAS,KAAK;AAChC,cAAI,IAAI,CAAC,MAAM,KAAQ,IAAI,IAAI,CAAC,MAAM,EAAM;AAC5C,cAAI,IAAI,IAAI,CAAC,MAAM,GAAM;AACvB,oBAAQ;AACR,oBAAQ;AACR;AAAA,UACF;AACA,cAAI,IAAI,IAAI,CAAC,MAAM,KAAQ,IAAI,IAAI,CAAC,MAAM,GAAM;AAC9C,oBAAQ;AACR,oBAAQ;AACR;AAAA,UACF;AAAA,QACF;AAEA,YAAI,QAAQ,EAAG,QAAO;AACtB,cAAM,iBAAiB,QAAQ;AAC/B,YAAI,kBAAkB,IAAI,OAAQ,QAAO;AAEzC,cAAM,KAAK,IAAI,cAAc;AAC7B,YAAI,OAAO,OAAW,QAAO;AAC7B,cAAM,WAAW,KAAK;AACtB,cAAM,WAAY,MAAM,IAAK;AAG7B,YAAI,CAAC,GAAG,GAAG,GAAG,CAAC,EAAE,SAAS,QAAQ,EAAG,QAAO;AAE5C,YAAI,CAAC,IAAI,IAAI,IAAI,IAAI,EAAE,EAAE,SAAS,QAAQ,EAAG,QAAO;AAEpD,eAAO;AAAA,MACT;AAEA,YAAMC,YAAW,eAAeD,MAAK;AAErC,YAAME,cAAyC,CAAC;AAChD,UAAI,QAAQ,EAAG,CAAAA,YAAW,QAAQ;AAClC,UAAI,SAAS,EAAG,CAAAA,YAAW,SAAS;AACpC,YAAMC,MAAK,aAAa;AACxB,UAAIA,MAAK,EAAG,CAAAD,YAAW,YAAYC;AAEnC,aAAO;AAAA,QACL,OAAAH;AAAA,QACA,UAAAC;AAAA,QACA,aAAaD,OAAM;AAAA,QACnB,YAAAE;AAAA,MACF;AAAA,IACF;AAEA,UAAM,MAAM,kBAAkB;AAU9B,UAAM,mBAAmB;AACzB,UAAM,sBAAsB,QAAQ,aAAa,mBAAmB,EAAE;AACtE,UAAM,YAAY,KAAK;AACvB,UAAM,cAAc,QAAQ;AAAA,MAC1B;AAAA,MACA,mBAAmB;AAAA,IACrB;AAEA,UAAM,WAAW,YACd,SAAS,GAAG,CAAC,EACb,SAAS,OAAO,EAChB,QAAQ,OAAO,EAAE;AACpB,UAAM,WAAW,YAAY,aAAa,CAAC;AAC3C,UAAM,YACJ,aAAa,KAAK,YAAY,aAAa,EAAE,IAAI;AAGnD,UAAM,aAAa,mBAAmB;AACtC,UAAM,WAAW,aAAa;AAE9B,QAAI,WAAW,QAAQ,QAAQ;AAC7B,YAAM,IAAI;AAAA,QACR,+CAA+C,QAAQ,mBAAmB,QAAQ,MAAM;AAAA,MAC1F;AAAA,IACF;AAEA,UAAM,QAAQ,QAAQ,SAAS,YAAY,QAAQ;AAGnD,UAAM,aAAyC,CAAC;AAChD,QAAI,QAAQ,EAAG,YAAW,QAAQ;AAClC,QAAI,SAAS,EAAG,YAAW,SAAS;AACpC,UAAM,KAAK,aAAa;AACxB,QAAI,KAAK,EAAG,YAAW,YAAY;AAGnC,UAAM,SAAmC;AAAA,MACvC;AAAA,MACA;AAAA,MACA,aAAa;AAAA,MACb;AAAA,IACF;AACA,QAAI,cAAc,OAAW,QAAO,YAAY;AAEhD,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,0BAA0B,QASZ;AAClB,UAAM,YAAY,OAAO,aAAa;AACtC,UAAM,aAAa,OAAO,cAAc;AAExC,UAAM,aAOF;AAAA,MACF,MAAM,OAAO;AAAA,MACb;AAAA,IACF;AACA,QAAI,OAAO,YAAY,OAAW,YAAW,UAAU,OAAO;AAC9D,QAAI,OAAO,YAAY,OAAW,YAAW,UAAU,OAAO;AAC9D,QAAI,OAAO,aAAa,OAAW,YAAW,WAAW,OAAO;AAChE,QAAI,OAAO,UAAU,OAAW,YAAW,QAAQ,OAAO;AAE1D,UAAM,OAAO,MAAM,KAAK,sBAAsB,UAAU;AAExD,WAAO,KAAK,8BAA8B;AAAA,MACxC,OAAO,KAAK;AAAA,MACZ,UAAU,KAAK;AAAA,MACf;AAAA,MACA;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEA,MAAc,8BAA8B,QAKxB;AAClB,UAAM,gBAAgB,OAAO,SAAS,YAAY;AAClD,UAAM,OAAiB,CAAC;AACxB,QAAI,cAAc,SAAS,KAAK,KAAK,cAAc,SAAS,MAAM,GAAG;AACnE,WAAK,KAAK,QAAQ,MAAM;AAAA,IAC1B,OAAO;AACL,WAAK,KAAK,QAAQ,MAAM;AAAA,IAC1B;AAEA,UAAM,YAAY,CAAC,QAAiC;AAClD,aAAO,IAAI,QAAgB,CAAC,SAAS,WAAW;AAC9C,cAAM,SAAmB,CAAC;AAC1B,YAAI,SAAS;AACb,YAAI,WAAW;AAEf,cAAM,KAAKE,OAAM,OAAO,YAAY;AAAA,UAClC;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,QACF,CAAC;AAED,cAAM,UAAU,WAAW,MAAM;AAC/B,qBAAW;AACX,aAAG,KAAK,SAAS;AACjB,iBAAO,IAAI,MAAM,0BAA0B,OAAO,SAAS,IAAI,CAAC;AAAA,QAClE,GAAG,OAAO,SAAS;AAEnB,WAAG,OAAO,GAAG,QAAQ,CAAC,UAAkB,OAAO,KAAK,KAAK,CAAC;AAC1D,WAAG,OAAO,GAAG,QAAQ,CAAC,SAAkB,UAAU,KAAK,SAAS,CAAE;AAElE,WAAG,GAAG,SAAS,CAAC,SAAS;AACvB,uBAAa,OAAO;AACpB,cAAI,SAAU;AAEd,cAAI,SAAS,GAAG;AACd,mBAAO,IAAI,MAAM,2BAA2B,IAAI,KAAK,MAAM,EAAE,CAAC;AAC9D;AAAA,UACF;AAEA,gBAAM,cAAc,OAAO,OAAO,MAAM;AACxC,cAAI,YAAY,WAAW,GAAG;AAC5B,mBAAO,IAAI,MAAM,sCAAsC,MAAM,EAAE,CAAC;AAChE;AAAA,UACF;AAEA,kBAAQ,WAAW;AAAA,QACrB,CAAC;AAED,WAAG,GAAG,SAAS,CAAC,QAAQ;AACtB,uBAAa,OAAO;AACpB,cAAI,SAAU;AACd,iBAAO,IAAI,MAAM,iBAAiB,IAAI,OAAO,EAAE,CAAC;AAAA,QAClD,CAAC;AAED,WAAG,MAAM,IAAI,OAAO,KAAK;AAAA,MAC3B,CAAC;AAAA,IACH;AAEA,QAAI;AACJ,eAAW,OAAO,MAAM;AACtB,UAAI;AACF,eAAO,MAAM,UAAU,GAAG;AAAA,MAC5B,SAAS,GAAG;AACV,kBAAU;AAAA,MACZ;AAAA,IACF;AAEA,UAAM,MAAM,mBAAmB,QAAQ,QAAQ,UAAU,OAAO,OAAO;AACvE,UAAM,IAAI;AAAA,MACR,yDAAyD,OAAO,QAAQ,kBAAkB,GAAG;AAAA,IAC/F;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAM,sBAAsB,QAaR;AAClB,UAAM,KAAK,OAAO,MAAM;AAExB,UAAM,UAAU,KAAK,iBAAiB,OAAO,OAAO;AACpD,UAAM,aAAa,OAAO,cAAc;AACxC,UAAM,aAAa,OAAO,cAAc;AACxC,UAAM,YAAY,OAAO,aAAa;AACtC,UAAM,cAAc,OAAO,eAAe;AAG1C,UAAM,UAAU,MAAM,KAAK,cAAc;AAAA,MACvC;AAAA,MACA,UAAU,OAAO;AAAA,MACjB;AAAA,MACA,eAAe;AAAA,IACjB,CAAC;AAGD,WAAO,IAAI,QAAgB,CAAC,SAAS,WAAW;AAC9C,YAAM,SAAmB,CAAC;AAC1B,UAAI,SAAS;AACb,UAAI,WAAW;AAEf,YAAM,KAAKA,OAAM,YAAY;AAAA,QAC3B;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,GAAI,cAAc,IAAI,CAAC,OAAO,YAAY,QAAQ,CAAC,CAAC,IAAI,CAAC;AAAA,QACzD;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF,CAAC;AAED,YAAM,UAAU,WAAW,MAAM;AAC/B,mBAAW;AACX,WAAG,KAAK,SAAS;AACjB,eAAO,IAAI,MAAM,0BAA0B,SAAS,IAAI,CAAC;AAAA,MAC3D,GAAG,SAAS;AAEZ,SAAG,OAAO,GAAG,QAAQ,CAAC,UAAkB;AACtC,eAAO,KAAK,KAAK;AAAA,MACnB,CAAC;AAED,SAAG,OAAO,GAAG,QAAQ,CAAC,SAAiB;AACrC,kBAAU,KAAK,SAAS;AAAA,MAC1B,CAAC;AAED,SAAG,GAAG,SAAS,CAAC,SAAS;AACvB,qBAAa,OAAO;AACpB,YAAI,SAAU;AAEd,YAAI,SAAS,GAAG;AACd,iBAAO,IAAI,MAAM,2BAA2B,IAAI,KAAK,MAAM,EAAE,CAAC;AAC9D;AAAA,QACF;AAEA,cAAM,cAAc,OAAO,OAAO,MAAM;AACxC,YAAI,YAAY,WAAW,GAAG;AAC5B,iBAAO,IAAI,MAAM,sCAAsC,MAAM,EAAE,CAAC;AAChE;AAAA,QACF;AAEA,gBAAQ,WAAW;AAAA,MACrB,CAAC;AAED,SAAG,GAAG,SAAS,CAAC,QAAQ;AACtB,qBAAa,OAAO;AACpB,YAAI,SAAU;AACd,eAAO,IAAI,MAAM,iBAAiB,IAAI,OAAO,EAAE,CAAC;AAAA,MAClD,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAM,cAAc,QAQA;AAGlB,UAAM,UAAU,KAAK,iBAAiB,OAAO,OAAO;AACpD,UAAM,aAAa,OAAO,cAAc;AACxC,UAAM,gBAAgB,OAAO,iBAAiB;AAE9C,QAAI,WAAW,OAAO;AACtB,QAAI;AACF,YAAM,QAAQ,MAAM,KAAK,WAAW;AACpC,YAAM,OAAO,MAAM;AACnB,YAAM,SAAS,OAAO,MAAM,WAAW,WAAW,KAAK,SAAS;AAChE,YAAM,OAAO,OAAO,MAAM,SAAS,WAAW,KAAK,OAAO;AAC1D,UAAI,YAAY,QAAQ,QAAQ,QAAQ,OAAO,SAAS,IAAI,KAAK,OAAO;AACtE,mBAAW;AACb,UAAI,iBAAiB,WAAW,GAAG;AACjC,cAAM,KAAK,eAAe,EAAE,MAAM,QAAQ,QAAQ,KAAK,CAAC;AAAA,MAC1D;AAAA,IACF,QAAQ;AAAA,IAER;AAEA,QAAI,YAAY,KAAM,YAAW;AAEjC,UAAM,gBAAgB,eAAe,cAAc,IAAI;AAIvD,QAAI,SAAS,OAAO,SAAS,KAAK;AAClC,UAAM,MAAM,OAAO,QAAQ,YAAY;AACvC,QAAI,OAAO,EAAG,UAAS,OAAO,MAAM,GAAG;AACvC,aAAS,OAAO,QAAQ,QAAQ,EAAE;AAGlC,UAAM,UAAU,OAAO,QAAQ,OAAO,KAAK,EAAE,QAAQ,MAAM,KAAK;AAChE,UAAM,OAAO,mBAAmB,KAAK,QAAQ;AAC7C,UAAM,OAAO,mBAAmB,KAAK,QAAQ;AAE7C,WAAO,UAAU,KAAK,IAAI,IAAI,QAAQ,QAAQ,OAAO,YAAY,OAAO,WAAW,aAAa,SAAS,IAAI,aAAa,IAAI;AAAA,EAChI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,MAAM,wBAAwB,QAWZ;AAChB,UAAM,KAAK,OAAO,MAAM;AAExB,UAAM,UAAU,KAAK,iBAAiB,OAAO,OAAO;AACpD,UAAM,aAAa,OAAO,cAAc;AACxC,UAAM,cAAc,OAAO,eAAe;AAC1C,UAAM,aAAa,OAAO,cAAc;AACxC,UAAM,YAAY,OAAO,aAAa;AAEtC,UAAM,MAAM,QAAQ,OAAO,UAAU,GAAG,EAAE,WAAW,KAAK,CAAC;AAE3D,UAAM,UAAU,YAA2B;AACzC,UAAI,aAAa;AAEf,cAAM,KAAK,OAAO,SAAS,EAAE,iBAAiB,MAAM,UAAU,EAAE,CAAC;AAAA,MACnE;AAEA,YAAM,UAAU,MAAM,KAAK,cAAc;AAAA,QACvC;AAAA,QACA,UAAU,OAAO;AAAA,QACjB;AAAA,QACA,eAAe;AAAA,MACjB,CAAC;AAED,YAAM,IAAI,QAAc,CAAC,SAAS,WAAW;AAC3C,cAAM,KAAKA,OAAM,YAAY;AAAA,UAC3B;AAAA,UACA;AAAA,UACA;AAAA,UACA,GAAI,YAAY,CAAC,IAAI,IAAI,CAAC;AAAA,UAC1B;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA,OAAO;AAAA,QACT,CAAC;AAED,YAAI,SAAS;AACb,WAAG,OAAO,GAAG,QAAQ,CAAC,MAAM;AAC1B,oBAAU,OAAO,CAAC;AAAA,QACpB,CAAC;AAED,WAAG,GAAG,SAAS,CAAC,SAAS;AACvB,cAAI,SAAS,EAAG,QAAO,QAAQ;AAC/B,iBAAO,IAAI,MAAM,2BAA2B,IAAI;AAAA,EAAK,MAAM,EAAE,CAAC;AAAA,QAChE,CAAC;AAED,WAAG,GAAG,SAAS,MAAM;AAAA,MACvB,CAAC;AAAA,IACH;AAEA,QAAI;AACF,YAAM,QAAQ;AAAA,IAChB,SAAS,GAAG;AAEV,UAAI,aAAa;AACf,YAAI;AACF,gBAAM,KAAK,OAAO,SAAS;AAAA,YACzB,iBAAiB;AAAA,YACjB,UAAU;AAAA,YACV,WAAW;AAAA,UACb,CAAC;AAAA,QACH,QAAQ;AAAA,QAER;AAAA,MACF;AACA,YAAM,QAAQ;AAAA,IAChB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,sBAAsB,QAMV;AAChB,UAAM,aAAa,OAAO,cAAc;AACxC,UAAM,YACJ,OAAO,SAAS,OAAO,SAAS,KAAK,OAAO,aAAa,IACrD,OAAO,YACP;AAEN,UAAM,MAAM,QAAQ,OAAO,UAAU,GAAG,EAAE,WAAW,KAAK,CAAC;AAE3D,UAAM,IAAI,QAAc,CAAC,SAAS,WAAW;AAC3C,YAAM,KAAKA,OAAM,YAAY;AAAA,QAC3B;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,OAAO,SAAS;AAAA,QAChB;AAAA,QACA,OAAO;AAAA,QACP;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,OAAO;AAAA,MACT,CAAC;AAED,UAAI,SAAS;AACb,SAAG,OAAO,GAAG,QAAQ,CAAC,MAAM;AAC1B,kBAAU,OAAO,CAAC;AAAA,MACpB,CAAC;AAED,SAAG,GAAG,SAAS,CAAC,SAAS;AACvB,YAAI,SAAS,EAAG,QAAO,QAAQ;AAC/B;AAAA,UACE,IAAI,MAAM,sCAAsC,IAAI;AAAA,EAAK,MAAM,EAAE;AAAA,QACnE;AAAA,MACF,CAAC;AAED,SAAG,GAAG,SAAS,MAAM;AAAA,IACvB,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,qBAAqB,QAMP;AAClB,UAAM,KAAK,OAAO,MAAM;AAExB,UAAM,MAAM,KAAK,OAAO,iBAAiB;AACzC,UAAM,SAAS,KAAK;AACpB,UAAM,QAAQ,CAAC,YACb,mBAAmB,KAAK,QAAQ,wBAAwB,OAAO;AAEjE,UAAM,UAAU,KAAK,iBAAiB,OAAO,OAAO;AACpD,UAAM,MAAM,MAAM,KAAK,uBAAuB,SAAS,OAAO,GAAG;AACjE,UAAM,0BACJ,KAAK,wCAAwC,OAAO;AAEtD,UAAM,0BAA0B;AAAA,MAC9B;AAAA,MACA,UAAU;AAAA,MACV;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,EAAE,OAAO,CAAC,GAAG,GAAG,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC;AACxC,UAAM,yBAAyB;AAAA,MAC7B;AAAA,MACA,UAAU;AAAA,MACV;AAAA,MACA;AAAA,MACA;AAAA,IACF,EACG,OAAO,CAAC,MAAmB,OAAO,MAAM,YAAY,OAAO,SAAS,CAAC,CAAC,EACtE,OAAO,CAAC,GAAG,GAAG,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC;AAEzC,UAAM,iBAAiB,OAAO,aAAa;AAC3C,UAAM,YAAY,KAAK,IAAI;AAC3B,UAAM,uBAAuB,CAAC,GAAG,CAAC;AAClC,QAAI;AACJ,QAAI,WAAW;AACf,UAAM,cAAc;AAEpB;AAAA,MACE,2BAA2B,OAAO,QAAQ,OAAO,WAAW,qBAAqB,wBAAwB,IAAI,CAAC,MAAO,KAAK,OAAO,cAAc,OAAO,CAAC,CAAE,EAAE,KAAK,GAAG,CAAC,mBAAmB,uBAAuB,KAAK,GAAG,CAAC,uBAAuB,qBAAqB,KAAK,GAAG,CAAC,oBAAoB,cAAc,gBAAgB,WAAW;AAAA,IAC3U;AAEA,eAAW,SAAS,wBAAwB;AAC1C,YAAM,aAAa,6BAA6B;AAAA,QAC9C;AAAA,QACA;AAAA,QACA,UAAU,OAAO;AAAA,QACjB,cAAc;AAAA,MAChB,CAAC;AAED,iBAAW,QAAQ,yBAAyB;AAC1C,mBAAW,MAAM,sBAAsB;AACrC,gBAAM,YAAY,kBAAkB,KAAK,IAAI,IAAI;AACjD,cAAI,aAAa,EAAG;AACpB,cAAI,YAAY,YAAa;AAC7B;AACA,gBAAM,mBAAmB,KAAK,IAAI,KAAO,SAAS;AAClD;AAAA,YACE,WAAW,QAAQ,IAAI,WAAW,SAAS,OAAO,QAAQ,UAAU,KAAK,aAAa,QAAQ,OAAO,cAAc,IAAI,qBAAqB,EAAE,qBAAqB,gBAAgB;AAAA,UACrL;AACA,cAAI;AACF,mBAAO,MAAM,KAAK,OAAO,WAAW;AAAA,cAClC,OAAO;AAAA,cACP;AAAA,cACA,GAAI,OAAO,SAAS,WAAW,EAAE,mBAAmB,KAAK,IAAI,CAAC;AAAA,cAC9D,cAAc;AAAA,cACd,cAAc,wBAAwB,OAAO;AAAA,cAC7C;AAAA,cACA,YAAY;AAAA,cACZ,WAAW;AAAA,YACb,CAAC;AAAA,UACH,SAAS,GAAG;AACV,sBAAU;AACV,kBAAM,MAAM,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC;AACrD,kBAAM,WAAW,QAAQ,YAAY,GAAG,EAAE;AAE1C,gBAAI,IAAI,SAAS,SAAS,EAAG;AAC7B,gBACE,CAAC,IAAI,SAAS,UAAU,KACxB,CAAC,IAAI,SAAS,kBAAkB,GAChC;AACA;AAAA,YACF;AAAA,UACF;AAAA,QACF;AACA,YAAI,YAAY,YAAa;AAAA,MAC/B;AACA,UAAI,YAAY,YAAa;AAAA,IAC/B;AAEA,UAAM,mBAAmB,QACrB,UACA,IAAI,MAAM,OAAO,WAAW,8BAA8B,CAAC;AAAA,EACjE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,0BAA0B,QAKZ;AAClB,UAAM,KAAK,OAAO,MAAM;AAExB,UAAM,MAAM,KAAK,OAAO,iBAAiB;AACzC,UAAM,SAAS,KAAK;AACpB,UAAM,QAAQ,CAAC,YACb,mBAAmB,KAAK,QAAQ,6BAA6B,OAAO;AAEtE,UAAM,UAAU,KAAK,iBAAiB,OAAO,OAAO;AACpD,UAAM,MAAM,MAAM,KAAK,uBAAuB,SAAS,OAAO,GAAG;AAEjE;AAAA,MACE,oCAAoC,OAAO,SAAS,GAAG,cAAc,OAAO,QAAQ;AAAA,IACtF;AAEA,UAAM,UAAU,OAAO,MAIA;AACrB,aAAO,MAAM,KAAK,OAAO,QAAQ;AAAA,QAC/B,OAAO,EAAE;AAAA,QACT,GAAI,EAAE,cAAc,OAAO,EAAE,YAAY,EAAE,WAAW,IAAI,CAAC;AAAA,QAC3D,GAAI,EAAE,aAAa,OAAO,EAAE,WAAW,EAAE,UAAU,IAAI,CAAC;AAAA,MAC1D,CAAC;AAAA,IACH;AAEA,UAAM,aAAa,OAAO,MAIH;AACrB,aAAO,MAAM,KAAK,OAAO,WAAW;AAAA,QAClC,OAAO,EAAE;AAAA,QACT,GAAI,EAAE,cAAc,OAAO,EAAE,YAAY,EAAE,WAAW,IAAI,CAAC;AAAA,QAC3D,GAAI,EAAE,aAAa,OAAO,EAAE,WAAW,EAAE,UAAU,IAAI,CAAC;AAAA,MAC1D,CAAC;AAAA,IACH;AAEA,UAAM,SAAS,MAAM,sCAAsC;AAAA,MACzD;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,UAAU,OAAO;AAAA,MACjB,WAAW,OAAO,aAAa;AAAA,IACjC,CAAC;AAED,UAAM,6BAA6B,OAAO,MAAM,QAAQ;AACxD,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,iCAAiC,QAMnB;AAClB,UAAM,KAAK,OAAO,MAAM;AAExB,UAAM,MAAM,KAAK,OAAO,iBAAiB;AACzC,UAAM,SAAS,KAAK;AACpB,UAAM,QAAQ,CAAC,YACb;AAAA,MACE;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAEF,UAAM,UAAU,KAAK,iBAAiB,OAAO,OAAO;AACpD,UAAM,0BACJ,KAAK,wCAAwC,OAAO;AACtD,UAAM,QAAQ,OAAO;AAErB,UAAM,aAAa,KAAK,gCAAgC,OAAO,QAAQ;AAMvE,UAAM,QAAQ,2BAA2B;AAKzC,QAAI;AACJ,QAAI,OAAO;AACT,YAAM,MAAM,KAAK,uBAAuB,SAAS,OAAO,GAAG;AAAA,IAC7D;AAKA,UAAM,aAAa,MAAM,SAAS,GAAG,IACjC,+BAA+B;AAAA,MAC7B;AAAA,MACA,cAAc;AAAA;AAAA,MACd,IAAI;AAAA,MACJ,GAAI,MAAM,EAAE,IAAI,IAAI,CAAC;AAAA,MACrB;AAAA,IACF,CAAC,IACD,iCAAiC;AAAA,MAC/B;AAAA,MACA,cAAc;AAAA,MACd,MAAM;AAAA,MACN,GAAI,MAAM,EAAE,IAAI,IAAI,CAAC;AAAA,MACrB;AAAA,IACF,CAAC;AAEL,UAAM,YAAY,OAAO,aAAa;AAEtC;AAAA,MACE,qBAAqB,OAAO,QAAQ,OAAO,WAAW,UAAU,KAAK,eAAe,UAAU,UAAU,KAAK,cAAc,SAAS;AAAA,IACtI;AAEA,QAAI;AACF,aAAO,MAAM,KAAK,OAAO,WAAW;AAAA,QAClC,OAAO;AAAA,QACP;AAAA,QACA,GAAI,QAAQ,EAAE,mBAAmB,2BAA2B,GAAG,IAAI,CAAC;AAAA,QACpE,gBAAgB;AAAA,QAChB,cAAc;AAAA,QACd;AAAA,QACA,YAAY;AAAA,QACZ;AAAA,MACF,CAAC;AAAA,IACH,SAAS,GAAG;AACV,YAAM,MAAM,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC;AACrD,YAAM,oBAAoB,GAAG,EAAE;AAC/B,YAAM;AAAA,IACR;AAAA,EACF;AAAA,EAEA,MAAM,kBAAkB,QAAkD;AAExE,WAAO,KAAK,uBAAuB,YAAY;AAC7C,YAAM,KAAK,OAAO,MAAM;AAExB,YAAM,UAAU,KAAK,iBAAiB,OAAO,OAAO;AACpD,YAAM,MAAM,MAAM,KAAK,uBAAuB,SAAS,OAAO,GAAG;AACjE,YAAM,WAAW,OAAO;AAExB,UAAI;AACJ,UAAI;AACF,eAAO,MAAM,KAAK,iCAAiC;AAAA,UACjD;AAAA,UACA;AAAA,UACA;AAAA,UACA,GAAI,OAAO,aAAa,OAAO,EAAE,WAAW,OAAO,UAAU,IAAI,CAAC;AAAA,QACpE,CAAC;AAAA,MACH,SAAS,GAAG;AACV,oBAAY;AAAA,MACd;AAEA,UAAI;AACJ,UAAI;AACF,eAAO,MAAM,KAAK,qBAAqB;AAAA,UACrC;AAAA,UACA;AAAA,UACA;AAAA,UACA,GAAI,OAAO,aAAa,OAAO,EAAE,WAAW,OAAO,UAAU,IAAI,CAAC;AAAA,QACpE,CAAC;AAAA,MACH,SAAS,GAAG;AACV,sBAAc;AAAA,MAChB;AAIA,UAAI;AACF,cAAM,SAAS,MAAM,KAAK,0BAA0B;AAAA,UAClD;AAAA,UACA;AAAA,UACA;AAAA,UACA,GAAI,OAAO,aAAa,OAAO,EAAE,WAAW,OAAO,UAAU,IAAI,CAAC;AAAA,QACpE,CAAC;AACD,YAAI,OAAO,SAAS,GAAG;AACrB,iBAAO;AAAA,QACT;AAAA,MACF,SAAS,GAAG;AAAA,MAEZ;AAEA,YAAM,YACJ,qBAAqB,QACjB,UAAU,UACV,aAAa,OACX,OAAO,SAAS,IAChB;AACR,YAAM,QACJ,uBAAuB,QACnB,YAAY,UACZ,eAAe,OACb,OAAO,WAAW,IAClB;AAER,YAAM,IAAI;AAAA,QACR,wDAAwD,+BAA+B,SAAS,aAAa,WAAW,oBAAoB,iCAAiC,SAAS,KAAK;AAAA,MAC7L;AAAA,IACF,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAsBA,MAAM,yBAAyB,QAc5B;AACD,UAAM,MAAM,MAAM,KAAK,kBAAkB,MAAM;AAE/C,UAAM,SAAmB,CAAC;AAC1B,UAAM,UAAU,IAAI,qBAAqB;AAAA,MACvC,QAAQ;AAAA,MACR,QAAQ,KAAK;AAAA,MACb,mBAAmB,CAAC,EAAE,OAAO,MAAM;AACjC,eAAO,KAAK,MAAM;AAAA,MACpB;AAAA,IACF,CAAC;AAED,YAAQ,KAAK,GAAG;AAEhB,UAAM,QAAQ,QAAQ,SAAS;AAE/B,WAAO;AAAA,MACL,QAAQ,OAAO,OAAO,MAAM;AAAA,MAC5B,WAAW,MAAM;AAAA,MACjB,OAAO;AAAA,QACL,SAAS,MAAM;AAAA,QACf,UAAU,MAAM;AAAA,QAChB,SAAS,MAAM;AAAA,QACf,cAAc,MAAM;AAAA,QACpB,cAAc,MAAM;AAAA,QACpB,WAAW,MAAM;AAAA,MACnB;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAsBA,MAAM,kBACJ,QAIkC;AAClC,UAAM,MAAM,MAAM,KAAK,kBAAkB,MAAM;AAE/C,UAAM,cAA0D,CAAC;AACjE,UAAM,cAAwB,CAAC;AAC/B,QAAI,aAAsC;AAE1C,UAAM,UAAU,IAAI,qBAAqB;AAAA,MACvC,QAAQ;AAAA,MACR,QAAQ,KAAK;AAAA,MACb,mBAAmB,CAAC,EAAE,QAAQ,aAAa,MAAM;AAC/C,oBAAY,KAAK,EAAE,QAAQ,aAAa,CAAC;AAAA,MAC3C;AAAA,MACA,cAAc,CAAC,EAAE,WAAW,KAAK,MAAM;AACrC,YAAI,cAAc,KAAM,cAAa;AACrC,oBAAY,KAAK,IAAI;AAAA,MACvB;AAAA,IACF,CAAC;AAED,YAAQ,KAAK,GAAG;AAEhB,UAAM,eAAe,QAAQ,SAAS;AACtC,UAAM,aAAa,aAAa,aAAa;AAM7C,QAAI;AACJ,QAAI;AAEJ,QAAI,YAAY,UAAU,GAAG;AAC3B,YAAM,UAAU,YAAY,CAAC,EAAG;AAChC,YAAM,SAAS,YAAY,YAAY,SAAS,CAAC,EAAG;AACpD,YAAM,aAAa,SAAS;AAE5B,UAAI,aAAa,GAAG;AAElB,0BAAkB,aAAa;AAC/B,eAAO,YAAY,SAAS,KAAK;AAAA,MACnC,OAAO;AAEL,cAAM,UAAU,aAAa,MAAM,CAAC,GAAG;AACvC,cAAM,WAAW,UAAU,IAAI,UAAU;AACzC,0BAAkB,YAAY,SAAS;AAAA,MACzC;AAAA,IACF,OAAO;AAEL,YAAM,UAAU,aAAa,MAAM,CAAC,GAAG;AACvC,YAAM,WAAW,UAAU,IAAI,UAAU;AACzC,wBAAkB,YAAY,SAAS;AAAA,IACzC;AAGA,QAAI,MAAM,MAAM,MAAM,GAAI,OAAM;AAAA,aACvB,MAAM,MAAM,MAAM,GAAI,OAAM;AAAA,aAC5B,MAAM,MAAM,MAAM,GAAI,OAAM;AAAA,QAChC,OAAM,KAAK,MAAM,MAAM,GAAG,IAAI;AAEnC,UAAM,YAAY,OAAO,OAAO,YAAY,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC;AAChE,UAAM,YACJ,YAAY,SAAS,IAAI,OAAO,OAAO,WAAW,IAAI;AACxD,UAAM,WAAW,aAAa,QAAQ,UAAU,SAAS;AAGzD,UAAM,MAAM,MAAM,KAAK,SAAS;AAAA,MAC9B;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,cAAc;AAAA,MACd,GAAI,OAAO,aAAa,EAAE,YAAY,OAAO,WAAW,IAAI,CAAC;AAAA,IAC/D,CAAC;AAED,WAAO;AAAA,MACL;AAAA,MACA,OAAO;AAAA,QACL,SAAS,aAAa;AAAA,QACtB,eAAe,aAAa;AAAA,QAC5B,eAAe,aAAa;AAAA,QAC5B,cAAc,aAAa;AAAA,QAC3B,cAAc,aAAa;AAAA,QAC3B,WAAW,aAAa;AAAA,QACxB;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAuBA,MAAM,sBAAsB,QASR;AAClB,UAAM,UAAU,KAAK,iBAAiB,OAAO,WAAW,CAAC;AAEzD,UAAM,aAAa,KAAK,gCAAgC,OAAO,QAAQ;AACvE,UAAM,YAAY,OAAO,aAAa;AACtC,UAAM,aAAa,OAAO,cAAc;AAGxC,UAAM,WAAW,aAAa,OAAO,IAAI,OAAO,QAAQ;AAGxD,WAAO,KAAK,uBAAuB,YAAY;AAC7C,WAAK,QAAQ;AAAA,QACX,uEAAuE,OAAO,UAAU,OAAO,QAAQ,gBAAgB,UAAU;AAAA,MACnI;AAGA,YAAM,EAAE,QAAQ,KAAK,IAAI,MAAM,KAAK,2BAA2B;AAAA,QAC7D;AAAA,QACA,UAAU,OAAO;AAAA,QACjB;AAAA,MACF,CAAC;AAED,UAAI;AAEF,cAAM,WAAW,MAAM,IAAI,QAGxB,CAAC,SAAS,WAAW;AACtB,gBAAM,UAAU,WAAW,MAAM;AAC/B;AAAA,cACE,IAAI,MAAM,kDAAkD;AAAA,YAC9D;AAAA,UACF,GAAG,SAAS;AAEZ,gBAAM,mBAAmB,CAAC,SAA0B;AAElD,gBAAI,SAAS;AACb,gBAAI,SAAS;AACb,gBAAI,IAAI;AACR,mBAAO,IAAI,KAAK,SAAS,GAAG;AAE1B,kBAAI,KAAK,CAAC,MAAM,KAAK,KAAK,IAAI,CAAC,MAAM,GAAG;AACtC,oBAAI,WAAW;AACf,oBAAI,KAAK,IAAI,CAAC,MAAM,KAAK,KAAK,IAAI,CAAC,MAAM,GAAG;AAC1C,6BAAW,IAAI;AAAA,gBACjB,WAAW,KAAK,IAAI,CAAC,MAAM,GAAG;AAC5B,6BAAW,IAAI;AAAA,gBACjB;AACA,oBAAI,YAAY,KAAK,WAAW,KAAK,QAAQ;AAC3C,wBAAM,UAAU,KAAK,QAAQ;AAC7B,sBAAI,YAAY,QAAW;AACzB,0BAAM,UAAU,UAAU;AAC1B,wBAAI,YAAY,EAAG,UAAS;AAC5B,wBAAI,YAAY,EAAG,UAAS;AAC5B,wBAAI,UAAU,OAAQ,QAAO;AAAA,kBAC/B;AACA,sBAAI;AACJ;AAAA,gBACF;AAAA,cACF;AACA;AAAA,YACF;AACA,mBAAO,UAAU;AAAA,UACnB;AAEA,gBAAM,mBAAmB,CAAC,SAA0B;AAElD,gBAAI,SAAS;AACb,gBAAI,SAAS;AACb,gBAAI,SAAS;AACb,gBAAI,IAAI;AACR,mBAAO,IAAI,KAAK,SAAS,GAAG;AAC1B,kBAAI,KAAK,CAAC,MAAM,KAAK,KAAK,IAAI,CAAC,MAAM,GAAG;AACtC,oBAAI,WAAW;AACf,oBAAI,KAAK,IAAI,CAAC,MAAM,KAAK,KAAK,IAAI,CAAC,MAAM,GAAG;AAC1C,6BAAW,IAAI;AAAA,gBACjB,WAAW,KAAK,IAAI,CAAC,MAAM,GAAG;AAC5B,6BAAW,IAAI;AAAA,gBACjB;AACA,oBAAI,YAAY,KAAK,WAAW,KAAK,QAAQ;AAC3C,wBAAM,UAAU,KAAK,QAAQ;AAC7B,sBAAI,YAAY,QAAW;AACzB,0BAAM,UAAW,WAAW,IAAK;AACjC,wBAAI,YAAY,GAAI,UAAS;AAC7B,wBAAI,YAAY,GAAI,UAAS;AAC7B,wBAAI,YAAY,GAAI,UAAS;AAC7B,wBAAI,UAAU,UAAU,OAAQ,QAAO;AAAA,kBACzC;AACA,sBAAI;AACJ;AAAA,gBACF;AAAA,cACF;AACA;AAAA,YACF;AACA,mBAAO,UAAU,UAAU;AAAA,UAC7B;AAEA,gBAAM,UAAU,CAAC,OAIX;AACJ,gBAAI,CAAC,GAAG,WAAY;AAGpB,kBAAM,YACJ,GAAG,cAAc,SACb,iBAAiB,GAAG,IAAI,IACxB,iBAAiB,GAAG,IAAI;AAE9B,gBAAI,CAAC,WAAW;AAEd,mBAAK,QAAQ;AAAA,gBACX,oFAAoF,GAAG,SAAS,UAAU,GAAG,KAAK,MAAM;AAAA,cAC1H;AACA;AAAA,YACF;AAEA,yBAAa,OAAO;AACpB,mBAAO,IAAI,mBAAmB,OAAO;AACrC,oBAAQ;AAAA,cACN,MAAM,GAAG;AAAA,cACT,WAAW,GAAG;AAAA,YAChB,CAAC;AAAA,UACH;AAEA,iBAAO,GAAG,mBAAmB,OAAO;AACpC,iBAAO,KAAK,SAAS,CAAC,QAAQ;AAC5B,yBAAa,OAAO;AACpB,mBAAO,IAAI,mBAAmB,OAAO;AACrC,mBAAO,GAAG;AAAA,UACZ,CAAC;AACD,iBAAO,KAAK,SAAS,MAAM;AACzB,yBAAa,OAAO;AACpB,mBAAO,IAAI,mBAAmB,OAAO;AACrC,mBAAO,IAAI,MAAM,wCAAwC,CAAC;AAAA,UAC5D,CAAC;AAAA,QACH,CAAC;AAED,aAAK,QAAQ;AAAA,UACX,qDAAqD,SAAS,KAAK,MAAM,iBAAiB,SAAS,SAAS;AAAA,QAC9G;AAGA,cAAM,OAAO,MAAM,KAAK,mBAAmB;AAAA,UACzC,WAAW,SAAS;AAAA,UACpB,YAAY,SAAS;AAAA,UACrB;AAAA,QACF,CAAC;AAED,aAAK,QAAQ;AAAA,UACX,gDAAgD,KAAK,MAAM;AAAA,QAC7D;AAEA,eAAO;AAAA,MACT,UAAE;AACA,cAAM,KAAK,EAAE,MAAM,MAAM;AAAA,QAAC,CAAC;AAAA,MAC7B;AAAA,IACF,GAAG,QAAQ;AAAA,EACb;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,mBAAmB,QAIb;AAClB,UAAM,EAAE,OAAAA,OAAM,IAAI,MAAM,OAAO,eAAoB;AACnD,UAAM,SAAS,OAAO,cAAc;AACpC,UAAM,cAAc,OAAO,eAAe,SAAS,SAAS;AAE5D,WAAO,IAAI,QAAgB,CAAC,SAAS,WAAW;AAC9C,YAAM,OAAO;AAAA,QACX;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAEA,YAAM,OAAOA,OAAM,QAAQ,MAAM;AAAA,QAC/B,OAAO,CAAC,QAAQ,QAAQ,MAAM;AAAA,MAChC,CAAC;AAED,YAAM,SAAmB,CAAC;AAC1B,UAAI,SAAS;AAEb,WAAK,OAAO,GAAG,QAAQ,CAAC,UAAkB,OAAO,KAAK,KAAK,CAAC;AAC5D,WAAK,OAAO,GAAG,QAAQ,CAAC,UAAkB;AACxC,kBAAU,MAAM,SAAS;AAAA,MAC3B,CAAC;AAED,WAAK,GAAG,SAAS,CAAC,SAAS;AACzB,YAAI,SAAS,KAAK,OAAO,WAAW,GAAG;AACrC;AAAA,YACE,IAAI;AAAA,cACF,gDAAgD,IAAI,MAAM,MAAM;AAAA,YAClE;AAAA,UACF;AACA;AAAA,QACF;AACA,gBAAQ,OAAO,OAAO,MAAM,CAAC;AAAA,MAC/B,CAAC;AAED,WAAK,GAAG,SAAS,MAAM;AAEvB,WAAK,MAAM,MAAM,OAAO,SAAS;AACjC,WAAK,MAAM,IAAI;AAAA,IACjB,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,MAAM,8BAA8B,QAkBjC;AACD,UAAM,UAAU,KAAK,iBAAiB,OAAO,WAAW,CAAC;AACzD,UAAM,gBAAgB,OAAO,iBAAiB;AAC9C,UAAM,gBAAgB,OAAO,iBAAiB;AAE9C,SAAK,QAAQ;AAAA,MACX,4DAA4D,OAAO,UAAU,OAAO,QAAQ;AAAA,IAC9F;AAEA,UAAM,EAAE,QAAQ,KAAK,IAAI,MAAM,KAAK,2BAA2B;AAAA,MAC7D;AAAA,MACA,UAAU,OAAO;AAAA,MACjB,WAAW;AAAA,IACb,CAAC;AAED,UAAM,cAAwB,CAAC;AAC/B,QAAI,aAA+B;AACnC,QAAI,YAAY;AAChB,QAAI,cAAc,KAAK,IAAI;AAC3B,UAAM,YAAY,KAAK,IAAI;AAG3B,UAAM,IAAI,QAAc,CAAC,YAAY;AACnC,UAAI,WAAW;AACf,YAAM,SAAS,MAAM;AACnB,YAAI,SAAU;AACd,mBAAW;AACX,sBAAc,aAAa;AAC3B,gBAAQ;AAAA,MACV;AAGA,aAAO,GAAG,mBAAmB,CAAC,UAAU;AACtC,sBAAc,KAAK,IAAI;AACvB,oBAAY,KAAK,MAAM,IAAI;AAC3B,qBAAa,MAAM;AACnB,YAAI,MAAM,WAAY;AAGtB,YAAI,YAAY,SAAS,QAAQ,GAAG;AAClC,eAAK,QAAQ;AAAA,YACX,6CAA6C,YAAY,MAAM,YAAY,SAAS;AAAA,UACtF;AAAA,QACF;AAAA,MACF,CAAC;AAGD,aAAO,GAAG,SAAS,MAAM;AACvB,aAAK,QAAQ;AAAA,UACX,uDAAuD,YAAY,MAAM;AAAA,QAC3E;AACA,eAAO;AAAA,MACT,CAAC;AAGD,aAAO,GAAG,SAAS,CAAC,QAAQ;AAC1B,aAAK,QAAQ;AAAA,UACX,iDAAiD,IAAI,OAAO;AAAA,QAC9D;AACA,eAAO;AAAA,MACT,CAAC;AAGD,YAAM,gBAAgB,YAAY,MAAM;AACtC,cAAM,MAAM,KAAK,IAAI;AACrB,cAAM,UAAU,MAAM;AACtB,cAAM,WAAW,MAAM;AAGvB,YAAI,UAAU,eAAe;AAC3B,eAAK,QAAQ;AAAA,YACX,yDAAyD,OAAO;AAAA,UAClE;AACA,iBAAO;AACP;AAAA,QACF;AAGA,YAAI,YAAY,SAAS,KAAK,WAAW,eAAe;AACtD,eAAK,QAAQ;AAAA,YACX,iDAAiD,QAAQ;AAAA,UAC3D;AACA,iBAAO;AACP;AAAA,QACF;AAAA,MACF,GAAG,GAAI;AAAA,IACT,CAAC;AAGD,QAAI;AACF,YAAM,KAAK;AAAA,IACb,QAAQ;AAAA,IAER;AAEA,QAAI,YAAY,WAAW,GAAG;AAC5B,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAGA,UAAM,kBAAkB,KAAK,IAAI,IAAI,aAAa;AAClD,UAAM,eACJ,iBAAiB,IAAI,KAAK,MAAM,YAAY,SAAS,cAAc,IAAI;AAEzE,UAAM,MACJ,gBAAgB,KACZ,KACA,gBAAgB,KACd,KACA,gBAAgB,KACd,KACA;AAEV,SAAK,QAAQ;AAAA,MACX,6CAA6C,YAAY,MAAM,YAAY,SAAS,yBAAyB,GAAG;AAAA,IAClH;AAEA,UAAM,YAAY,OAAO,OAAO,WAAW;AAC3C,UAAM,kBAAkB,YAAY,SAAS;AAG7C,UAAM,MAAM,MAAM,KAAK,SAAS;AAAA,MAC9B;AAAA,MACA;AAAA,MACA,WAAW;AAAA,MACX,YAAY;AAAA,MACZ;AAAA,MACA,GAAI,OAAO,aAAa,EAAE,YAAY,OAAO,WAAW,IAAI,CAAC;AAAA,IAC/D,CAAC;AAED,WAAO;AAAA,MACL;AAAA,MACA,OAAO;AAAA,QACL,cAAc,YAAY;AAAA,QAC1B;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,SAAS,QAQH;AAClB,UAAM,EAAE,OAAAA,OAAM,IAAI,MAAM,OAAO,eAAoB;AACnD,UAAM,EAAE,YAAAC,YAAW,IAAI,MAAM,OAAO,QAAa;AACjD,UAAM,KAAK,MAAM,OAAO,aAAkB;AAC1C,UAAM,KAAK,MAAM,OAAO,IAAS;AACjC,UAAM,OAAO,MAAM,OAAO,MAAW;AAErC,UAAM,SAAS,OAAO,cAAc;AACpC,UAAM,SAAS,GAAG,OAAO;AACzB,UAAM,KAAKA,YAAW;AAEtB,UAAM,cAAc,OAAO,eAAe,SAAS,SAAS;AAC5D,UAAM,YAAY,KAAK,KAAK,QAAQ,WAAW,EAAE,IAAI,WAAW,EAAE;AAClE,UAAM,aAAa,KAAK,KAAK,QAAQ,WAAW,EAAE,MAAM;AAExD,QAAI,YAA2B;AAC/B,QAAI,OAAO,aAAa,OAAO,UAAU,SAAS,KAAK,OAAO,YAAY;AACxE,YAAM,WAAW,OAAO,eAAe,QAAQ,QAAQ;AACvD,kBAAY,KAAK,KAAK,QAAQ,WAAW,EAAE,IAAI,QAAQ,EAAE;AAAA,IAC3D;AAEA,QAAI;AAEF,YAAM,GAAG,UAAU,WAAW,OAAO,SAAS;AAC9C,UAAI,aAAa,OAAO,WAAW;AACjC,cAAM,GAAG,UAAU,WAAW,OAAO,SAAS;AAAA,MAChD;AAGA,YAAM,OAAiB,CAAC,gBAAgB,aAAa,SAAS,IAAI;AAIlE,UAAI,OAAO,MAAM,GAAG;AAClB,aAAK,KAAK,MAAM,OAAO,OAAO,GAAG,CAAC;AAAA,MACpC;AACA,WAAK,KAAK,MAAM,aAAa,MAAM,SAAS;AAG5C,UAAI,aAAa,OAAO,YAAY;AAClC,YAAI,OAAO,eAAe,OAAO;AAE/B,eAAK,KAAK,MAAM,OAAO,MAAM,SAAS;AAAA,QACxC,OAAO;AAEL,eAAK,KAAK,MAAM,SAAS,OAAO,QAAQ,OAAO,KAAK,MAAM,SAAS;AAAA,QACrE;AAAA,MACF;AAGA,WAAK,KAAK,QAAQ,MAAM;AACxB,UAAI,aAAa,OAAO,YAAY;AAClC,YAAI,OAAO,eAAe,OAAO;AAE/B,eAAK,KAAK,QAAQ,QAAQ,UAAU,eAAe;AAAA,QACrD,OAAO;AAEL,eAAK,KAAK,QAAQ,KAAK;AAAA,QACzB;AAAA,MAEF;AAGA,UAAI,OAAO,MAAM,GAAG;AAClB,aAAK,KAAK,MAAM,OAAO,OAAO,GAAG,CAAC;AAAA,MACpC;AAEA,WAAK;AAAA,QACH;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAGA,YAAM,IAAI,QAAc,CAAC,SAAS,WAAW;AAC3C,cAAM,IAAID,OAAM,QAAQ,MAAM,EAAE,OAAO,CAAC,UAAU,UAAU,MAAM,EAAE,CAAC;AACrE,YAAI,SAAS;AAEb,UAAE,OAAO,GAAG,QAAQ,CAAC,MAAc;AACjC,oBAAU,EAAE,SAAS;AAAA,QACvB,CAAC;AAED,UAAE,GAAG,SAAS,CAAC,MAAM;AACnB,iBAAO,IAAI,MAAM,uBAAuB,EAAE,OAAO,EAAE,CAAC;AAAA,QACtD,CAAC;AAED,UAAE,GAAG,SAAS,CAAC,SAAS;AACtB,cAAI,SAAS,GAAG;AACd,oBAAQ;AAAA,UACV,OAAO;AACL;AAAA,cACE,IAAI;AAAA,gBACF,2BAA2B,IAAI,KAAK,OAAO,MAAM,IAAK,CAAC;AAAA,cACzD;AAAA,YACF;AAAA,UACF;AAAA,QACF,CAAC;AAAA,MACH,CAAC;AAGD,aAAO,MAAM,GAAG,SAAS,UAAU;AAAA,IACrC,UAAE;AAEA,YAAM,GAAG,OAAO,SAAS,EAAE,MAAM,MAAM;AAAA,MAAC,CAAC;AACzC,UAAI,UAAW,OAAM,GAAG,OAAO,SAAS,EAAE,MAAM,MAAM;AAAA,MAAC,CAAC;AACxD,YAAM,GAAG,OAAO,UAAU,EAAE,MAAM,MAAM;AAAA,MAAC,CAAC;AAAA,IAC5C;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,kBAAiC;AACrC,UAAM,KAAK,OAAO,MAAM;AAGxB,UAAM,UAAU,KAAK,OAAO,uBAAuB,KAAK;AAExD,UAAM,WAGD;AAAA,MACH;AAAA,QACE,OAAO,aAAa,OAAO;AAAA,QAC3B,QAAQ;AAAA,UACN,OAAO;AAAA,UACP,mBAAmB;AAAA,UACnB,cAAc;AAAA,QAChB;AAAA,MACF;AAAA,MACA;AAAA;AAAA,QAEE,OAAO;AAAA,QACP,QAAQ;AAAA,UACN,OAAO;AAAA,UACP,mBAAmB;AAAA,UACnB,cAAc;AAAA,QAChB;AAAA,MACF;AAAA,MACA;AAAA,QACE,OAAO;AAAA,QACP,QAAQ;AAAA,UACN,OAAO;AAAA,UACP,mBAAmB;AAAA,UACnB,cAAc;AAAA,QAChB;AAAA,MACF;AAAA,MACA;AAAA,QACE,OAAO;AAAA,QACP,QAAQ;AAAA,UACN,OAAO;AAAA,UACP,mBAAmB;AAAA,UACnB,cAAc;AAAA,UACd,cAAc;AAAA,QAChB;AAAA,MACF;AAAA,IACF;AAEA,QAAI;AACJ,eAAW,KAAK,UAAU;AACxB,YAAM,QAAQ,MAAM,KAAK,OAAO,UAAU;AAAA,QACxC,GAAG,EAAE;AAAA,QACL,WAAW;AAAA,MACb,CAAC;AACD,iBAAW,MAAM,OAAO;AACxB,UAAI,MAAM,OAAO,iBAAiB,KAAK;AACrC,aAAK,OAAO,aAAa;AACzB,aAAK,OAAO,mBAAmB;AAC/B;AAAA,MACF;AAEA,OAAC,KAAK,OAAO,SAAS,KAAK,OAAO,KAAK;AAAA,QACrC,KAAK;AAAA,QACL,kDAAkD,EAAE,KAAK,kBAAkB,MAAM,OAAO,YAAY;AAAA,MACtG;AAAA,IACF;AAEA,SAAK,OAAO,aAAa;AACzB,SAAK,OAAO,mBAAmB;AAC/B,UAAM,IAAI;AAAA,MACR,uEAAuE,YAAY,SAAS;AAAA,IAC9F;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,MAAM,qBAAqB,SAKT;AAChB,UAAM,KAAK,OAAO,MAAM;AAExB,UAAM,cAAc,SAAS,eAAe;AAC5C,UAAM,YAAY,SAAS,aAAa;AAIxC,UAAM,WAAW,MAAM,KAAK,KAAK,gBAAgB,QAAQ,CAAC,EACvD,OAAO,CAAC,CAAC,EAAE,CAAC,OAAO,EAAE,cAAc,EAAE,OAAO,YAAY,MAAM,MAAM,EACpE,IAAI,CAAC,CAAC,EAAE,MAAM,EAAE,EAChB,OAAO,CAAC,OAAO,OAAO,SAAS,EAAE,CAAC,EAClC,KAAK,CAAC,GAAG,MAAM,IAAI,CAAC,EACpB,MAAM,GAAG,WAAW;AAEvB,QAAI,UAAU;AACd,QAAI;AACJ,eAAW,WAAW,UAAU;AAC9B,YAAM,QAAQ,MAAM,KAAK,OAAO,UAAU;AAAA,QACxC,OAAO;AAAA,QACP,mBAAmB,OAAO,OAAO;AAAA,QACjC,cAAc;AAAA,QACd;AAAA,MACF,CAAC;AACD,iBAAW,MAAM,OAAO;AACxB,UAAI,MAAM,OAAO,iBAAiB,KAAK;AACrC;AAAA,MACF,OAAO;AACL,SAAC,KAAK,OAAO,SAAS,KAAK,OAAO,KAAK;AAAA,UACrC,KAAK;AAAA,UACL,+DAA+D,OAAO,kBAAkB,MAAM,OAAO,YAAY;AAAA,QACnH;AAAA,MACF;AAAA,IACF;AAEA,QAAI,UAAU,GAAG;AACf,WAAK,OAAO,aAAa;AACzB,WAAK,OAAO,mBAAmB;AAC/B;AAAA,IACF;AAGA,QAAI;AACF,YAAM,KAAK,gBAAgB;AAC3B;AAAA,IACF,QAAQ;AAAA,IAER;AAEA,SAAK,OAAO,aAAa;AACzB,SAAK,OAAO,mBAAmB;AAC/B,UAAM,IAAI;AAAA,MACR,6FAA6F,YAAY,SAAS;AAAA,IACpH;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,oBAAmC;AAGvC,SAAK,OAAO,aAAa;AAEzB,SAAK,OAAO,mBAAmB;AAAA,EACjC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAc,6BACZ,UAAkB,GACH;AACf,QAAI;AACF,YAAM,KAAK,4BAA4B,OAAO;AAC9C,YAAM,KAAK,wBAAwB,OAAO;AAAA,IAC5C,SAAS,GAAG;AAEV,YAAM,MAAM,kBAAkB,CAAC;AAC/B,OAAC,KAAK,OAAO,QAAQ,KAAK,OAAO,QAAQ;AAAA,QACvC,KAAK;AAAA,QACL,yDAAyD,OAAO,IAAI,qBAAqB,IAAI,CAAC,KAAK,GAAG;AAAA,MACxG;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAc,4BAA4B,SAAgC;AACxE,QAAI;AACJ,QAAI;AACF,oBAAc,MAAM,KAAK,eAAe,OAAO;AAAA,IACjD,SAAS,OAAO;AAGd,YAAM,MAAM,kBAAkB,KAAK;AACnC,OAAC,KAAK,OAAO,QAAQ,KAAK,OAAO,QAAQ;AAAA,QACvC,KAAK;AAAA,QACL,2DAA2D,OAAO,IAAI,qBAAqB,IAAI,CAAC,KAAK,GAAG;AAAA,MAC1G;AACA;AAAA,IACF;AAEA,QAAI,gBAAgB,KAAK,iBAAiB;AACxC,WAAK,kBAAkB;AACvB,UAAI,aAAa;AACf,cAAM,QAA4B;AAAA,UAChC,MAAM;AAAA,UACN;AAAA,UACA,WAAW,KAAK,IAAI;AAAA,QACtB;AACA,aAAK,oBAAoB,KAAK;AAAA,MAChC;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAc,wBAAwB,SAAgC;AACpE,QAAI,KAAK,uBAAwB;AAEjC,QAAI;AACF,YAAM,UAAU,MAAM,KAAK,WAAW,OAAO;AAC7C,UAAI,CAAC,WAAW,QAAQ,gBAAgB,OAAW;AAEnD,YAAM,iBACJ,CAAC,KAAK,eACN,KAAK,YAAY,gBAAgB,QAAQ,eACzC,KAAK,YAAY,YAAY,QAAQ;AAEvC,UAAI,CAAC,eAAgB;AAErB,WAAK,cAAc;AACnB,UAAI,QAAQ,gBAAgB,GAAG;AAC7B,cAAM,QAA4B;AAAA,UAChC,MAAM;AAAA,UACN;AAAA,UACA,WAAW,KAAK,IAAI;AAAA,QACtB;AACA,aAAK,oBAAoB,KAAK;AAAA,MAChC;AAAA,IACF,SAAS,OAAO;AAGd,WAAK,yBAAyB;AAE9B,YAAM,MAAM,kBAAkB,KAAK;AACnC,UAAI,CAAC,IAAI,SAAS,eAAe,KAAK,CAAC,IAAI,SAAS,aAAa,GAAG;AAClE,SAAC,KAAK,OAAO,SAAS,KAAK,OAAO,MAAM;AAAA,UACtC,KAAK;AAAA,UACL;AAAA,UACA;AAAA,QACF;AAAA,MACF;AAEA,UAAI,CAAC,KAAK,8BAA8B;AACtC,aAAK,+BAA+B;AACpC,aAAK,OAAO;AAAA,UACV;AAAA,UACA;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,oBAA0B;AAGhC,UAAM,iBAAiB,KAAK,OAAO,wBAAwB,KAAK;AAChE,QAAI,CAAC,gBAAgB;AACnB,WAAK,iBAAiB;AACtB;AAAA,IACF;AAGA,QAAI,KAAK,qBAAqB,SAAS,GAAG;AACxC;AAAA,IACF;AAIA,QAAI,KAAK,gBAAgB,OAAO,GAAG;AACjC;AAAA,IACF;AAGA,UAAM,QAAQ,KAAK,OAAO,eAAe,MAAM;AAC/C,QAAI,OAAO;AACT;AAAA,IACF;AAGA,QAAI,KAAK,sBAAsB;AAC7B;AAAA,IACF;AAGA,SAAK,uBAAuB,YAAY,YAAY;AAClD,UAAI;AAEF,YAAI,KAAK,qBAAqB,SAAS,GAAG;AACxC,eAAK,iBAAiB;AACtB;AAAA,QACF;AAEA,cAAM,UAAU,KAAK,OAAO,uBAAuB,KAAK;AACxD,cAAM,KAAK,6BAA6B,OAAO;AAAA,MACjD,SAAS,GAAG;AAEV,cAAM,MAAM,kBAAkB,CAAC;AAC/B,aAAK,OAAO;AAAA,UACV,iDAAiD,qBAAqB,IAAI,CAAC,KAAK,GAAG;AAAA,QACrF;AAAA,MACF;AAAA,IACF,GAAG,GAAI;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKQ,mBAAyB;AAC/B,QAAI,KAAK,sBAAsB;AAC7B,oBAAc,KAAK,oBAAoB;AACvC,WAAK,uBAAuB;AAAA,IAC9B;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,yBAA+B;AACrC,QAAI,KAAK,qBAAqB,SAAS,GAAG;AACxC,WAAK,sBAAsB;AAC3B;AAAA,IACF;AAEA,UAAM,QAAQ,KAAK,OAAO,eAAe,MAAM;AAC/C,QAAI,CAAC,OAAO;AACV,WAAK,sBAAsB;AAC3B;AAAA,IACF;AAEA,QAAI,KAAK,0BAA2B;AAEpC,UAAM,WAAW,MAAM;AAErB,UACE,KAAK,qBAAqB,SAAS,KACnC,KAAK,OAAO,eAAe,MAAM,OACjC;AACA,aAAK,sBAAsB;AAC3B;AAAA,MACF;AAEA,YAAM,UAAU,KAAK,OAAO,uBAAuB,KAAK;AACxD,YAAM,SAAS,KAAK,eAAe,EAAE,QAAQ,CAAC;AAC9C,UAAI,OAAO,UAAU,UAAW;AAEhC,YAAM,OAAO,KAAK,mCAAmC,IAAI,OAAO;AAChE,WAAK,mCAAmC,IAAI,SAAS,OAAO,KAAK;AAGjE,UAAI,SAAS,QAAW;AACtB,YAAI,OAAO,UAAU,YAAY;AAC/B,eAAK,oBAAoB;AAAA,YACvB,MAAM;AAAA,YACN;AAAA,YACA,WAAW,KAAK,IAAI;AAAA,UACtB,CAAC;AAAA,QACH;AACA;AAAA,MACF;AAEA,UAAI,SAAS,OAAO,OAAO;AACzB,aAAK,oBAAoB;AAAA,UACvB,MAAM,OAAO,UAAU,aAAa,aAAa;AAAA,UACjD;AAAA,UACA,WAAW,KAAK,IAAI;AAAA,QACtB,CAAC;AAAA,MACH;AAAA,IACF;AAGA,aAAS;AAET,SAAK,4BAA4B,YAAY,MAAM;AACjD,UAAI;AACF,iBAAS;AAAA,MACX,SAAS,GAAG;AAEV,aAAK,OAAO;AAAA,UACV,uDAAuD,qBAAqB,IAAI,CAAC,KAAK,kBAAkB,CAAC,CAAC;AAAA,QAC5G;AAAA,MACF;AAAA,IACF,GAAG,KAAK,2BAA2B;AAAA,EACrC;AAAA,EAEQ,wBAA8B;AACpC,QAAI,KAAK,2BAA2B;AAClC,oBAAc,KAAK,yBAAyB;AAC5C,WAAK,4BAA4B;AAAA,IACnC;AACA,SAAK,mCAAmC,MAAM;AAAA,EAChD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,UAAU,SAAmC;AAIjD,UAAM,QAAQ;AACd,UAAM,MAAM,MAAM,KAAK,QAAQ;AAAA,MAC7B;AAAA,MACA,GAAI,YAAY,SAAY,EAAE,QAAQ,IAAI,CAAC;AAAA,IAC7C,CAAC;AACD,UAAM,KAAK,KAAK,iBAAiB,OAAO;AACxC,UAAM,QAAQ,KAAK,IAAI;AAEvB,WAAO,4BAA4B,EAAE,KAAK,SAAS,IAAI,MAAM,CAAC;AAAA,EAChE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAM,qBAAqB,SAA8C;AACvE,UAAM,QAAQ;AACd,UAAM,MAAM,MAAM,KAAK,QAAQ;AAAA,MAC7B;AAAA,MACA,GAAI,YAAY,SAAY,EAAE,QAAQ,IAAI,CAAC;AAAA,IAC7C,CAAC;AAED,UAAM,kBAAkB,WAAW,KAAK,iBAAiB;AAEzD,UAAM,UACJ,oBAAoB,oBACpB,oBAAoB;AAEtB,UAAM,SAA4B;AAAA,MAChC,SAAS,WAAW;AAAA,MACpB;AAAA,IACF;AACA,QAAI,iBAAiB;AACnB,aAAO,OAAO;AAAA,IAChB;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgBA,MAAM,iBACJ,SACA,UAAyB,OACzB,SAUe;AACf,UAAM,KAAK,KAAK,iBAAiB,OAAO;AAExC,UAAM,YAAY;AAElB,UAAM,UAAoC,SAAS,WAAW;AAG9D,UAAM,eAAe,SAAS,UAAU,KAAK;AAS7C,UAAM,gBAGF;AAAA,MACF,MAAM;AAAA,QACJ,QAAQ;AAAA,QACR,YAAY,YAAY,YAAY,IAAI;AAAA;AAAA,QAExC,YAAY;AAAA,MACd;AAAA,MACA,KAAK;AAAA,QACH,QAAQ;AAAA,QACR,YAAY,YAAY,YAAY,IAAI;AAAA,QACxC,YAAY;AAAA,MACd;AAAA,MACA,KAAK,EAAE,QAAQ,MAAM,YAAY,GAAG,YAAY,eAAe;AAAA,IACjE;AAEA,QAAI,YAAY,aAAa,YAAY,OAAO;AAC9C,YAAM,IAAI;AAAA,QACR,8CAA8C,OAAO,aAAa,OAAO;AAAA,MAC3E;AAAA,IACF;AAEA,UAAM,SAAS,cAAc,OAAO;AACpC,QAAI,CAAC,QAAQ;AACX,YAAM,IAAI,MAAM,2BAA2B,OAAO,EAAE;AAAA,IACtD;AACA,QAAI,CAAC,OAAO,YAAY;AACtB,YAAM,IAAI;AAAA,QACR,sCAAsC,OAAO,aAAa,KAAK,UAAU,MAAM,CAAC;AAAA,MAClF;AAAA,IACF;AAMA,UAAM,aAAa,OAAO;AAE1B,QAAI,OAAO,eAAe,UAAU;AAClC,YAAM,IAAI;AAAA,QACR,+BAA+B,OAAO,UAAU,YAAY,UAAU,aAAa,KAAK,UAAU,MAAM,CAAC;AAAA,MAC3G;AAAA,IACF;AACA,UAAM,aAAa,gBAAgB,OAAO,QAAQ,YAAY,SAAS;AAOvE,UAAM,wBACJ,YAAY,eAAe,YAAY,SACnC,iBACA,YAAY,eAAe,YAAY,QACrC,iBACA;AACR,UAAM,iBACJ,YAAY,eAAe,YAAY,SACnC,OACA,YAAY,eAAe,YAAY,QACrC,MACA;AACR,UAAM,0BACJ,YAAY,eACZ,yBACA,mBAAmB,SACf,MAAM;AAAA,MACJ,IAAI;AAAA,QACF,CAAC,WAAW,YAAY,CAAC,EAAE;AAAA,UACzB,CAAC,MAAM,OAAO,SAAS,CAAC,KAAK,KAAK;AAAA,QACpC;AAAA,MACF;AAAA,IACF,IACA,CAAC;AAgBP,UAAM,QAAQ,aAAa,eAAe,MAAM;AAChD,UAAM,cAAc,QAAQ,IAAI;AAChC,QAAI;AAEJ,aAAS,UAAU,GAAG,WAAW,aAAa,WAAW;AAGvD,YAAM,SAAS,aAAa,kBAAkB;AAC9C,mBAAa,qBAAqB,iBAAiB,MAAM;AAIzD,WAAK,mBAAmB,IAAI,GAAG,EAAE,IAAI,OAAO,IAAI,OAAO,IAAI,MAAM;AAEjE,UAAI;AACF,cAAM,aAA2D;AAAA,UAC/D,OAAO;AAAA,UACP,SAAS;AAAA,UACT,mBAAmB;AAAA,UACnB,gBAAgB;AAAA,UAChB,cAAc,yBAAyB,SAAS;AAAA,UAChD;AAAA,UACA,cAAc;AAAA,UACd,YAAY,OAAO;AAAA;AAAA;AAAA,UAGnB,WAAW;AAAA,QACb;AAIA,YAAI;AAGJ,YACE,wBAAwB,SAAS,KACjC,yBACA,mBAAmB,QACnB;AACA,qBAAW,oBAAoB,yBAAyB;AACtD,gBAAI;AACF,sBAAQ,MAAM,aAAa,UAAU;AAAA,gBACnC,GAAG;AAAA;AAAA,gBAEH,cAAc;AAAA,gBACd,YAAY,mBAAmB;AAAA,kBAC7B,WAAW;AAAA,kBACX,QAAQ,iBAAiB;AAAA,kBACzB,YAAY;AAAA,gBACd,CAAC;AAAA,gBACD,YAAY;AAAA,cACd,CAAC;AACD;AAAA,YACF,QAAQ;AAAA,YAER;AAAA,UACF;AAAA,QACF;AACA,YAAI,CAAC,MAAO,SAAQ,MAAM,aAAa,UAAU,UAAU;AAY3D,YAAI,MAAM,OAAO,iBAAiB,KAAK;AACrC,gBAAM,IAAI;AAAA,YACR,gDAAgD,MAAM,OAAO,YAAY,kDAAkD,MAAM,OAAO,YAAY;AAAA,UACtJ;AAAA,QACF;AAGA,aAAK,mBAAmB;AAAA,UACtB,GAAG,EAAE,IAAI,OAAO,IAAI,OAAO;AAAA,UAC3B,MAAM,OAAO;AAAA,QACf;AAGA;AAAA,MACF,SAAS,OAAO;AACd,oBAAY;AACZ,YAAI;AACF,uBAAa,uBAAuB,iBAAiB,MAAM;AAAA,QAC7D,QAAQ;AAAA,QAER;AAGA,aAAK,mBAAmB,OAAO,GAAG,EAAE,IAAI,OAAO,IAAI,OAAO,EAAE;AAE5D,YAAI,UAAU,aAAa;AACzB,gBAAM,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,GAAG,CAAC;AACvD;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,UAAM,qBAAqB,QAAQ,YAAY,IAAI,MAAM,OAAO,SAAS,CAAC;AAAA,EAS5E;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,qBACE,SACA,UAAyB,OACL;AACpB,UAAM,KAAK,KAAK,iBAAiB,OAAO;AACxC,WAAO,KAAK,mBAAmB,IAAI,GAAG,EAAE,IAAI,OAAO,UAAU;AAAA,EAC/D;AAAA,EAEA,gCACE,SACA,SACA,UAAoC,WAChB;AACpB,UAAM,KAAK,KAAK,iBAAiB,OAAO;AACxC,WAAO,KAAK,mBAAmB,IAAI,GAAG,EAAE,IAAI,OAAO,IAAI,OAAO,EAAE;AAAA,EAClE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,MAAM,gBACJ,SACA,UAAyB,OACzB,SAUe;AACf,UAAM,KAAK,KAAK,iBAAiB,OAAO;AACxC,UAAM,YAAY;AAElB,UAAM,UAAoC,SAAS,WAAW;AAG9D,UAAM,eAAe,SAAS,UAAU,KAAK;AAG7C,UAAM,gBAGF;AAAA,MACF,MAAM,EAAE,QAAQ,GAAG,YAAY,YAAY,YAAY,IAAI,EAAE;AAAA,MAC7D,KAAK,EAAE,QAAQ,KAAK,YAAY,YAAY,YAAY,IAAI,EAAE;AAAA,MAC9D,KAAK,EAAE,QAAQ,MAAM,YAAY,EAAE;AAAA,IACrC;AAEA,QAAI,YAAY,aAAa,YAAY,OAAO;AAC9C,YAAM,IAAI;AAAA,QACR,8CAA8C,OAAO,aAAa,OAAO;AAAA,MAC3E;AAAA,IACF;AAEA,UAAM,SAAS,cAAc,OAAO;AAEpC,UAAM,iBACJ,YAAY,eAAe,YAAY,SACnC,OACA,YAAY,eAAe,YAAY,QACrC,MACA;AACR,UAAM,0BACJ,YAAY,eAAe,mBAAmB,SAC1C,MAAM;AAAA,MACJ,IAAI;AAAA,QACF,CAAC,WAAW,YAAY,CAAC,EAAE;AAAA,UACzB,CAAC,MAAM,OAAO,SAAS,CAAC,KAAK,KAAK;AAAA,QACpC;AAAA,MACF;AAAA,IACF,IACA,CAAC;AAEP,UAAM,MAAM,GAAG,EAAE,IAAI,OAAO,IAAI,OAAO;AACvC,UAAM,SAAS,KAAK,mBAAmB,IAAI,GAAG;AAC9C,SAAK,mBAAmB,OAAO,GAAG;AAIlC,QAAI;AACF,YAAM,WAID,CAAC;AAGN,UAAI,wBAAwB,SAAS,KAAK,mBAAmB,QAAW;AACtE,mBAAW,oBAAoB,yBAAyB;AACtD,gBAAM,SAAS,iBAAiB;AAChC,mBAAS,KAAK;AAAA,YACZ,cAAc;AAAA,YACd,YAAY,uBAAuB;AAAA,cACjC,WAAW;AAAA,cACX;AAAA,YACF,CAAC;AAAA,YACD,YAAY;AAAA,UACd,CAAC;AAED,mBAAS,KAAK;AAAA,YACZ,cAAc;AAAA,YACd,YAAY,oBAAoB,QAAQ,gBAAgB;AAAA,YACxD,YAAY;AAAA,UACd,CAAC;AAAA,QACH;AAAA,MACF;AAGA,eAAS,KAAK;AAAA,QACZ,cAAc,yBAAyB,SAAS;AAAA,QAChD,YAAY,oBAAoB,OAAO,QAAQ,SAAS;AAAA,QACxD,YAAY,OAAO;AAAA,MACrB,CAAC;AAGD,UAAI,YAAY,eAAe,YAAY,OAAO;AAChD,iBAAS,KAAK;AAAA,UACZ,cAAc,yBAAyB,SAAS;AAAA,UAChD,YAAY,oBAAoB,OAAO,QAAQ,SAAS;AAAA,UACxD,YAAY;AAAA,QACd,CAAC;AAAA,MACH;AAEA,iBAAW,KAAK,UAAU;AACxB,YAAI;AACF,gBAAM,aAAa,UAAU;AAAA,YAC3B,OAAO;AAAA,YACP,SAAS;AAAA,YACT,mBAAmB;AAAA,YACnB,cAAc,EAAE;AAAA,YAChB,YAAY,EAAE;AAAA,YACd,cAAc;AAAA,YACd,YAAY,EAAE;AAAA,YACd,GAAI,WAAW,SAAY,EAAE,gBAAgB,OAAO,IAAI,CAAC;AAAA,YACzD,WAAW;AAAA,UACb,CAAC;AACD;AAAA,QACF,QAAQ;AAAA,QAER;AAAA,MACF;AAAA,IACF,QAAQ;AAAA,IAER,UAAE;AAIA,UAAI;AACF,YAAI,WAAW;AACb,uBAAa,uBAAuB,iBAAiB,MAAM;AAAA,MAC/D,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,MAAM,cAAc,SAAwC;AAC1D,UAAM,KAAK,KAAK,iBAAiB,OAAO;AAGxC,UAAM,YAAY;AAClB,QAAI,MAAM;AACV,QAAI;AACF,YAAM,MAAM,KAAK,QAAQ;AAAA,QACvB,OAAO;AAAA,QACP,SAAS;AAAA,QACT,mBAAmB;AAAA,QACnB,cAAc,yBAAyB,SAAS;AAAA,QAChD,cAAc;AAAA,QACd,YAAY;AAAA,MACd,CAAC;AAAA,IACH,SAAS,GAAG;AAIV,YAAM,MAAM,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC;AACrD,UAAI,IAAI,SAAS,8BAA8B,EAAG,QAAO,CAAC;AAC1D,YAAM;AAAA,IACR;AAEA,UAAM,SAAS,KAAK,mBAAmB,GAAG;AAC1C,UAAM,UAAuB,OAE1B,OAAO,CAAC,MAAM,EAAE,WAAW,UAAa,EAAE,WAAW,GAAG,EAExD,OAAO,CAAC,MAAM,EAAE,SAAS,UAAa,OAAO,EAAE,IAAI,EAAE,KAAK,MAAM,EAAE,EAClE,IAAI,CAAC,OAAO;AAAA,MACX,IAAI,EAAE;AAAA,MACN,MAAM,EAAE,QAAQ,UAAU,EAAE,EAAE;AAAA,IAChC,EAAE;AAEJ,WAAO;AAAA,EACT;AAAA,EAEQ,mBACN,KACuD;AACvD,UAAM,SAAgE,CAAC;AACvE,UAAM,gBAAgB,IAAI,SAAS,uCAAuC;AAC1E,eAAW,SAAS,eAAe;AACjC,YAAM,YAAY,MAAM,CAAC,KAAK;AAC9B,YAAM,SAAS,qBAAqB,KAAK,SAAS,IAAI,CAAC;AACvD,UAAI,CAAC,OAAQ;AACb,YAAM,KAAK,OAAO,MAAM;AACxB,UAAI,CAAC,OAAO,SAAS,EAAE,EAAG;AAC1B,YAAM,YAAY,yBAAyB,KAAK,SAAS;AACzD,YAAM,cAAc,6BAA6B,KAAK,SAAS;AAE/D,YAAM,QAAwD,EAAE,GAAG;AACnE,YAAM,OAAO,YAAY,CAAC;AAC1B,YAAM,SAAS,cAAc,CAAC;AAC9B,UAAI,SAAS,OAAW,OAAM,OAAO;AACrC,UAAI,WAAW,OAAW,OAAM,SAAS;AACzC,aAAO,KAAK,KAAK;AAAA,IACnB;AACA,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,iBACZ,SACgE;AAChE,UAAM,KAAK,KAAK,iBAAiB,OAAO;AACxC,UAAM,YAAY;AAClB,QAAI,MAAM;AACV,QAAI;AACF,YAAM,MAAM,KAAK,QAAQ;AAAA,QACvB,OAAO;AAAA,QACP,SAAS;AAAA,QACT,mBAAmB;AAAA,QACnB,cAAc,yBAAyB,SAAS;AAAA,QAChD,cAAc;AAAA,QACd,YAAY;AAAA,MACd,CAAC;AAAA,IACH,SAAS,GAAG;AACV,YAAM,MAAM,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC;AACrD,UAAI,IAAI,SAAS,8BAA8B,EAAG,QAAO,CAAC;AAC1D,YAAM;AAAA,IACR;AACA,WAAO,KAAK,mBAAmB,GAAG;AAAA,EACpC;AAAA,EAYA,MAAM,IACJ,kBACA,SACe;AACf,UAAM,KACJ,OAAO,qBAAqB,WACxB,KAAK,iBAAiB,gBAAgB,IACtC;AACN,UAAM,kBACJ,OAAO,qBAAqB,WAAW,UAAW;AAGpD,UAAM,YAAY;AAElB,UAAM,YAAY,oBAAoB,eAAe;AACrD,UAAM,QAAQ,gBAAgB,WAAW,gBAAgB,KAAK;AAE9D,UAAM,aAAa,mBAAmB,WAAW,WAAW,KAAK;AAGjE,UAAM,eAAe,yBAAyB,SAAS;AAQvD,UAAM,QAAQ,MAAM,KAAK,OAAO,UAAU;AAAA,MACxC,OAAO;AAAA,MACP,SAAS;AAAA,MACT,mBAAmB;AAAA,MACnB;AAAA,MACA;AAAA,MACA,cAAc;AAAA,MACd,YAAY;AAAA,IACd,CAAC;AAED,QAAI,MAAM,OAAO,iBAAiB,KAAK;AACrC,YAAM,eAAe,yBAAyB;AAAA,QAC5C,QAAQ,KAAK;AAAA,QACb;AAAA,MACF,CAAC;AACD,YAAM,IAAI;AAAA,QACR,uCAAuC,MAAM,OAAO,YAAY,IAAI,YAAY;AAAA,MAClF;AAAA,IACF;AAIA,QAAI,gBAAgB,WAAW,WAAW,cAAc,QAAQ;AAC9D,YAAM,aAAa,gBAAgB,cAAc;AACjD,UAAI,aAAa,GAAG;AAClB,mBAAW,MAAM;AACf,eAAK,IAAI,IAAI;AAAA,YACX,QAAQ;AAAA,YACR,SAAS,gBAAgB;AAAA,UAC3B,CAAC,EAAE,MAAM,MAAM;AAAA,UAEf,CAAC;AAAA,QACH,GAAG,UAAU;AAAA,MACf;AAAA,IACF;AAAA,EACF;AAAA,EAWA,MAAM,gBAAgB,MAAc,MAA8B;AAEhE,UAAM,KACJ,SAAS,SACL,KAAK,iBAAiB,MAAS,IAC/B,KAAK,iBAAiB,IAAI;AAChC,UAAM,WAAW,SAAS,SAAY,OAAO;AAC7C,UAAM,YAAY;AAClB,UAAM,aAAa,kBAAkB,WAAW,UAAU,OAAO;AAGjE,UAAM,eAAe,yBAAyB,SAAS;AAEvD,UAAM,QAAQ,MAAM,KAAK,OAAO,UAAU;AAAA,MACxC,OAAO;AAAA,MACP,SAAS;AAAA,MACT,mBAAmB;AAAA,MACnB;AAAA,MACA;AAAA,MACA,cAAc;AAAA,MACd,YAAY;AAAA,IACd,CAAC;AAED,QAAI,MAAM,OAAO,iBAAiB,KAAK;AACrC,YAAM,IAAI;AAAA,QACR,2CAA2C,MAAM,OAAO,YAAY;AAAA,MACtE;AAAA,IACF;AAAA,EACF;AAAA,EAoBA,MAAM,aACJ,MACA,MACA,MACe;AACf,UAAM,KACJ,OAAO,SAAS,WACZ,KAAK,iBAAiB,IAA0B,IAChD,KAAK,iBAAiB,IAAI;AAChC,UAAM,WAAW,OAAO,SAAS,WAAW,OAAQ;AACpD,UAAM,OAAO,OAAO,SAAS,WAAW,OAAQ;AAChD,UAAM,YAAY;AAGlB,UAAM,aAAa,oBAAoB,WAAW,UAAU,UAAU;AAAA,MACpE;AAAA,MACA,QAAQ;AAAA,IACV,CAAC;AAGD,UAAM,eAAe,yBAAyB,SAAS;AAEvD,UAAM,QAAQ,MAAM,KAAK,OAAO,UAAU;AAAA,MACxC,OAAO;AAAA,MACP,SAAS;AAAA,MACT,mBAAmB;AAAA,MACnB;AAAA,MACA;AAAA,MACA,cAAc;AAAA,MACd,YAAY;AAAA,IACd,CAAC;AAED,QAAI,MAAM,OAAO,iBAAiB,KAAK;AACrC,YAAM,IAAI;AAAA,QACR,2CAA2C,MAAM,OAAO,YAAY;AAAA,MACtE;AAAA,IACF;AAAA,EACF;AAAA,EAUA,MAAM,gBAAgB,MAAc,MAA8B;AAEhE,UAAM,KACJ,SAAS,SACL,KAAK,iBAAiB,MAAS,IAC/B,KAAK,iBAAiB,IAAI;AAChC,UAAM,WAAW,SAAS,SAAY,OAAO;AAC7C,UAAM,YAAY;AAElB,UAAM,eAAe,yBAAyB,SAAS;AAGvD,QAAI;AACJ,QAAI;AACF,YAAM,SAAS,MAAM,KAAK,iBAAiB,EAAE;AAC7C,oBAAc,OAAO,KAAK,CAAC,MAAM,EAAE,OAAO,QAAQ,GAAG;AAAA,IACvD,QAAQ;AAAA,IAER;AAEA,UAAM,WAAW,6BAA6B;AAAA,MAC5C;AAAA,MACA;AAAA,MACA,GAAI,cAAc,EAAE,YAAY,IAAI,CAAC;AAAA,IACvC,CAAC;AAED,UAAM,EAAE,QAAQ,UAAU,IAAI,MAAM,2BAA2B;AAAA,MAC7D;AAAA,MACA,MAAM,OAAO,YAAY,WAAW;AAClC,cAAM,QAAQ,MAAM,KAAK,OAAO,UAAU;AAAA,UACxC,OAAO;AAAA,UACP,SAAS;AAAA,UACT,mBAAmB;AAAA,UACnB;AAAA,UACA;AAAA,UACA,cAAc;AAAA,UACd,YAAY;AAAA,QACd,CAAC;AAED,eAAO,EAAE,cAAc,MAAM,OAAO,aAAa;AAAA,MACnD;AAAA,MACA,QAAQ,YAAY;AAGlB,cAAM,QAAQ,MAAM,KAAK,iBAAiB,EAAE;AAC5C,cAAM,QAAQ,MAAM,KAAK,CAAC,MAAM,EAAE,OAAO,QAAQ;AACjD,eAAO,2BAA2B,KAAK;AAAA,MACzC;AAAA,IACF,CAAC;AAID,QAAI,QAAQ;AACV,WAAK,OAAO;AAAA,QACV;AAAA,QACA;AAAA,UACE,SAAS;AAAA,UACT;AAAA,UACA;AAAA,QACF;AAAA,MACF;AACA;AAAA,IACF;AAEA,UAAM,qBAAqB,QACvB,YACA,IAAI,MAAM,0BAA0B;AAAA,EAC1C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,eAAe,SAAwC;AAC3D,UAAM,KAAK,KAAK,iBAAiB,OAAO;AACxC,UAAM,MAAM,MAAM,KAAK,QAAQ;AAAA,MAC7B,OAAO;AAAA,MACP,SAAS;AAAA,IACX,CAAC;AAED,UAAM,UAAU,WAAW,KAAK,MAAM;AACtC,UAAM,WAAW,WAAW,KAAK,MAAM;AAEvC,UAAM,SAAsB,CAAC;AAC7B,QAAI,YAAY,QAAW;AACzB,aAAO,MAAM,OAAO,OAAO;AAAA,IAC7B;AACA,QAAI,aAAa,QAAW;AAC1B,aAAO,OAAO,OAAO,QAAQ;AAAA,IAC/B;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,aAAa,SAA4C;AAC7D,UAAM,KAAK,KAAK,iBAAiB,OAAO;AACxC,UAAM,YAAY;AAClB,UAAM,MAAM,MAAM,KAAK,QAAQ;AAAA,MAC7B,OAAO;AAAA,MACP,SAAS;AAAA,MACT,mBAAmB;AAAA,MACnB,cAAc,yBAAyB,SAAS;AAAA,MAChD,cAAc;AAAA,MACd,YAAY;AAAA,IACd,CAAC;AAED,UAAM,eAAe,CAAC,eAAqD;AACzE,YAAM,eAAe,IAAI;AAAA,QACvB,IAAI,UAAU,kBAAkB,UAAU;AAAA,MAC5C,EAAE,KAAK,GAAG;AACV,YAAM,aAAa,eAAe,CAAC;AACnC,UAAI,CAAC,WAAY,QAAO;AACxB,YAAM,SAAS,WAAW,YAAY,QAAQ;AAC9C,YAAM,SAAS,WAAW,YAAY,QAAQ;AAC9C,YAAM,SAAS,WAAW,YAAY,QAAQ;AAC9C,UAAI,WAAW,UAAa,WAAW,UAAa,WAAW;AAC7D,eAAO;AACT,aAAO;AAAA,QACL,QAAQ,OAAO,MAAM;AAAA,QACrB,QAAQ,OAAO,MAAM;AAAA,QACrB,QAAQ,OAAO,MAAM;AAAA,MACvB;AAAA,IACF;AAEA,UAAM,MAAuB,CAAC;AAC9B,UAAM,OAAO,aAAa,MAAM;AAChC,UAAM,QAAQ,aAAa,OAAO;AAClC,QAAI,KAAM,KAAI,OAAO;AACrB,QAAI,MAAO,KAAI,QAAQ;AACvB,WAAO;AAAA,EACT;AAAA,EASA,MAAM,aAAa,MAAc,MAA8B;AAC7D,UAAM,KAAK,SAAS,SAAY,IAAI,KAAK,iBAAiB,IAAI;AAC9D,UAAM,aAAa,SAAS,SAAY,OAAO;AAC/C,UAAM,YAAY;AAClB,UAAM,UAAU,MAAM,KAAK,aAAa,EAAE;AAC1C,UAAM,OAAO,QAAQ;AACrB,QAAI,CAAC,MAAM;AACT,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAEA,UAAM,eAAe,KAAK,MAAM,aAAa,GAAI;AACjD,UAAM,UAAU,KAAK,IAAI,KAAK,QAAQ,KAAK,IAAI,KAAK,QAAQ,YAAY,CAAC;AAEzE,UAAM,aAAa,uBAAuB,WAAW,OAAO;AAC5D,UAAM,eAAe,yBAAyB,SAAS;AAEvD,UAAM,QAAQ,MAAM,KAAK,OAAO,UAAU;AAAA,MACxC,OAAO;AAAA,MACP,SAAS;AAAA,MACT,mBAAmB;AAAA,MACnB;AAAA,MACA;AAAA,MACA,cAAc;AAAA,MACd,YAAY;AAAA,IACd,CAAC;AAED,QAAI,MAAM,OAAO,iBAAiB,KAAK;AACrC,YAAM,IAAI;AAAA,QACR,gCAAgC,MAAM,OAAO,YAAY;AAAA,MAC3D;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAMQ,oBACN,KACA,SACsB;AACtB,UAAM,WAAW,CAAC,MAA8C;AAC9D,UAAI,MAAM,OAAW,QAAO;AAC5B,YAAM,IAAI,OAAO,CAAC;AAClB,aAAO,OAAO,SAAS,CAAC,IAAI,IAAI;AAAA,IAClC;AAGA,UAAM,oBAAoB,aAAa,KAAK,aAAa;AACzD,UAAM,iBACJ,kBAAkB;AAAA,MAChB,CAAC,MAAM,WAAW,GAAG,WAAW,MAAM,OAAO,OAAO;AAAA,IACtD,KACA,kBAAkB,CAAC,KACnB;AAEF,UAAM,MAA4B,CAAC;AAEnC,UAAM,iBAAiB;AAAA,MACrB,WAAW,gBAAgB,gBAAgB;AAAA,IAC7C;AACA,QAAI,mBAAmB,OAAW,KAAI,iBAAiB;AAEvD,UAAM,eAAe,WAAW,gBAAgB,cAAc;AAC9D,QAAI,iBAAiB,OAAW,KAAI,eAAe;AAEnD,UAAM,gBAAgB,WAAW,gBAAgB,eAAe;AAChE,QAAI,kBAAkB,OAAW,KAAI,gBAAgB;AAErD,UAAM,UAAU,SAAS,WAAW,gBAAgB,SAAS,CAAC;AAC9D,QAAI,YAAY,OAAW,KAAI,UAAU;AAEzC,UAAM,UAAU,SAAS,WAAW,gBAAgB,SAAS,CAAC;AAC9D,QAAI,YAAY,OAAW,KAAI,UAAU;AAEzC,UAAM,cAAc,SAAS,WAAW,gBAAgB,aAAa,CAAC;AACtE,QAAI,gBAAgB,OAAW,KAAI,cAAc;AAEjD,UAAM,WAAW,SAAS,WAAW,gBAAgB,UAAU,CAAC;AAChE,QAAI,aAAa,OAAW,KAAI,WAAW;AAE3C,UAAM,iBAAiB;AAAA,MACrB,WAAW,gBAAgB,gBAAgB;AAAA,IAC7C;AACA,QAAI,mBAAmB,OAAW,KAAI,iBAAiB;AAEvD,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,eAAe,MAUC;AACd,UAAM,WAAW,MAAM,YAAY,MAAM,UAAU;AACnD,UAAM,kBAAkB,IAAI;AAAA,MAC1B,MAAM,mBACJ,MAAM,gBAAgB;AAAA,QACpB;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACJ;AACA,UAAM,YAAY,KAAK,OAAO,eAAe;AAC7C,QAAI,cAAc,OAAO;AACvB,aAAO;AAAA,QACL,OAAO;AAAA,QACP,QAAQ;AAAA,MACV;AAAA,IACF;AAIA,QACE,KAAK,mBAAmB,OAAO,KAC/B,KAAK,YAAY,OAAO,KACxB,KAAK,OAAO,0BAA0B,GACtC;AACA,aAAO,EAAE,OAAO,SAAS,QAAQ,mBAAmB;AAAA,IACtD;AAEA,UAAM,kBAAkB,KAAK,OAAO,oBAAoB,KAAK;AAE7D,UAAM,MAAM,KAAK,IAAI;AACrB,UAAM,SAAS,MAAM;AAErB,UAAM,MAAM,KAAK,OAAO,eAAe,KAAK,CAAC,GAAG;AAAA,MAC9C,CAAC,MAAM,EAAE,QAAQ;AAAA,IACnB;AACA,UAAM,MAAM,KAAK,OAAO,eAAe,KAAK,CAAC,GAAG;AAAA,MAC9C,CAAC,MAAM,EAAE,QAAQ;AAAA,IACnB;AAIA,QAAI,GAAG,WAAW,KAAK,GAAG,WAAW,GAAG;AACtC,aAAO;AAAA,QACL,OAAO;AAAA,QACP,QAAQ,6BAA6B,QAAQ,KAAK,kBAAkB,KAAK,wBAAwB;AAAA,QACjG,QAAQ;AAAA,MACV;AAAA,IACF;AAEA,UAAM,gBAAgB,GAAG,KAAK,CAAC,MAAM,CAAC,gBAAgB,IAAI,EAAE,KAAK,CAAC;AAClE,QAAI,eAAe;AACjB,aAAO;AAAA,QACL,OAAO;AAAA,QACP,QAAQ,mBAAmB,cAAc,KAAK,iBAAiB,cAAc,YAAY,SAAS,MAAM,cAAc,IAAI;AAAA,QAC1H,YAAY,cAAc;AAAA,QAC1B,QAAQ,MAAM,cAAc;AAAA,MAC9B;AAAA,IACF;AAEA,UAAM,gBAAgB,GAAG,KAAK,CAAC,MAAM,CAAC,gBAAgB,IAAI,EAAE,KAAK,CAAC;AAClE,QAAI,eAAe;AACjB,aAAO;AAAA,QACL,OAAO;AAAA,QACP,QAAQ,mBAAmB,cAAc,KAAK,SAAS,MAAM,cAAc,IAAI;AAAA,QAC/E,QAAQ,MAAM,cAAc;AAAA,MAC9B;AAAA,IACF;AAEA,WAAO;AAAA,MACL,OAAO;AAAA,MACP,QAAQ,2CAA2C,QAAQ,mBAAmB,MAAM,KAAK,eAAe,EAAE,KAAK,GAAG,CAAC;AAAA,MACnH,QAAQ;AAAA,IACV;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,MAAM,iBAAiB,MAUE;AACvB,UAAM,YAAY,KAAK,OAAO,eAAe;AAC7C,QAAI,cAAc,OAAO;AACvB,aAAO;AAAA,QACL,OAAO;AAAA,QACP,QAAQ;AAAA,MACV;AAAA,IACF;AAIA,QACE,KAAK,mBAAmB,OAAO,KAC/B,KAAK,YAAY,OAAO,KACxB,KAAK,OAAO,0BAA0B,GACtC;AACA,aAAO,EAAE,OAAO,SAAS,QAAQ,mBAAmB;AAAA,IACtD;AAEA,UAAM,MAAM,KAAK,IAAI;AACrB,UAAM,gBAAgB,MAAM,iBAAiB;AAC7C,QAAI,KAAK,kBAAkB,MAAM,KAAK,eAAe,OAAO,eAAe;AACzE,aAAO;AAAA,QACL,GAAG,KAAK,eAAe;AAAA,QACvB,QAAQ,GAAG,KAAK,eAAe,OAAO,MAAM;AAAA,MAC9C;AAAA,IACF;AAGA,QAAI,CAAC,KAAK,OAAO,kBAAkB,GAAG;AACpC,YAAM,SAAsB;AAAA,QAC1B,OAAO;AAAA,QACP,QAAQ;AAAA,MACV;AACA,WAAK,iBAAiB,EAAE,MAAM,KAAK,OAAO;AAC1C,aAAO;AAAA,IACT;AACA,QAAI,CAAC,KAAK,OAAO,UAAU;AACzB,YAAM,SAAsB,EAAE,OAAO,WAAW,QAAQ,gBAAgB;AACxE,WAAK,iBAAiB,EAAE,MAAM,KAAK,OAAO;AAC1C,aAAO;AAAA,IACT;AAEA,UAAM,KAAK,KAAK,iBAAiB,MAAM,OAAO;AAC9C,UAAM,YAAY,MAAM,aAAa;AACrC,UAAM,WAAW,KAAK,IAAI,GAAG,MAAM,YAAY,CAAC;AAChD,UAAM,QAAQ,MAAM,SAAS;AAE7B,aAAS,IAAI,GAAG,IAAI,UAAU,KAAK;AACjC,UAAI;AACF,cAAM,QAAQ,MAAM,KAAK,OAAO,UAAU;AAAA,UACxC;AAAA,UACA,SAAS;AAAA,UACT;AAAA,QACF,CAAC;AACD,cAAM,SAAsB;AAAA,UAC1B,OAAO,MAAM,OAAO,iBAAiB,MAAM,UAAU;AAAA,UACrD,QAAQ,eAAe,KAAK,iBAAiB,MAAM,OAAO,YAAY;AAAA,QACxE;AACA,aAAK,iBAAiB,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO;AACjD,eAAO;AAAA,MACT,SAAS,GAAG;AAEV,cAAM,MAAM,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC;AACrD,cAAM,YACJ,IAAI,SAAS,kBAAkB,KAC/B,IAAI,YAAY,EAAE,SAAS,SAAS;AACtC,YAAI,WAAW;AACb,gBAAM,SAAsB;AAAA,YAC1B,OAAO;AAAA,YACP,QAAQ,uBAAuB,KAAK,cAAc,SAAS;AAAA,UAC7D;AACA,eAAK,iBAAiB,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO;AACjD,iBAAO;AAAA,QACT;AAEA,YAAI,MAAM,WAAW,GAAG;AACtB,gBAAM,SAAsB;AAAA,YAC1B,OAAO;AAAA,YACP,QAAQ,qBAAqB,KAAK,KAAK,GAAG;AAAA,UAC5C;AACA,eAAK,iBAAiB,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO;AACjD,iBAAO;AAAA,QACT;AAAA,MACF;AAAA,IACF;AAEA,UAAM,WAAwB;AAAA,MAC5B,OAAO;AAAA,MACP,QAAQ;AAAA,IACV;AACA,SAAK,iBAAiB,EAAE,MAAM,KAAK,IAAI,GAAG,QAAQ,SAAS;AAC3D,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAM,iBAAiB,SAAwC;AAC7D,UAAM,KAAK,KAAK,iBAAiB,OAAO;AAIxC,UAAM,cAAc,KAAK,eAAe,EAAE,SAAS,GAAG,CAAC;AACvD,QAAI,YAAY,UAAU,YAAY;AACpC,aAAO,EAAE,SAAS,IAAI,UAAU,KAAK;AAAA,IACvC;AAEA,QAAI;AAGF,YAAM,MAAM,MAAM,QAAQ,KAAK;AAAA,QAC7B,KAAK,QAAQ,EAAE,OAAO,4BAA4B,SAAS,GAAG,CAAC;AAAA,QAC/D,IAAI;AAAA,UAAgB,CAAC,GAAG,WACtB,WAAW,MAAM,OAAO,IAAI,MAAM,SAAS,CAAC,GAAG,GAAI;AAAA,QACrD;AAAA,MACF,CAAC;AAED,YAAM,SAAsB;AAAA,QAC1B,SAAS;AAAA,QACT,UAAU;AAAA;AAAA,MACZ;AAEA,aAAO,OAAO,QAAQ,KAAK,oBAAoB,KAAK,EAAE,CAAC;AAEvD,aAAO;AAAA,IACT,SAAS,OAAO;AAEd,YAAM,SAAsB;AAAA,QAC1B,SAAS;AAAA,MACX;AAEA,YAAM,WAAW,KAAK,eAAe,EAAE,SAAS,GAAG,CAAC;AACpD,UAAI,SAAS,UAAU,WAAY,QAAO,WAAW;AAIrD,UAAI,iBAAiB,SAAS,MAAM,YAAY,WAAW;AAEzD,YAAI,OAAO,YAAY,KAAM,QAAO,WAAW;AAAA,MACjD,OAAO;AAAA,MAEP;AAEA,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,MAAM,eAAe,SAAwC;AAC3D,UAAM,KAAK,KAAK,iBAAiB,OAAO;AAKxC,UAAM,YAAY,KAAK,OAAO,eAAe;AAC7C,QAAI,cAAc,OAAO;AACvB,UAAI,CAAC,KAAK,OAAO,oBAAoB,KAAK,CAAC,KAAK,OAAO,UAAU;AAC/D,eAAO,EAAE,SAAS,IAAI,UAAU,KAAK;AAAA,MACvC;AAAA,IACF;AAGA,UAAM,MAAM,MAAM,KAAK,OAAO,QAAQ;AAAA,MACpC,OAAO;AAAA,MACP,SAAS;AAAA,IACX,CAAC;AAED,UAAM,SAAsB;AAAA,MAC1B,SAAS;AAAA,IACX;AAEA,WAAO,OAAO,QAAQ,KAAK,oBAAoB,KAAK,EAAE,CAAC;AAEvD,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,OACJ,SACA,SACe;AACf,UAAM,KAAK,KAAK,iBAAiB,OAAO;AACxC,UAAM,OACJ,OAAO,YAAY,WACf,EAAE,iBAAiB,QAAQ,IAC1B,WAAW,CAAC;AAEnB,UAAM,YAAY,KAAK,aAAa;AACpC,UAAM,WAAW,KAAK,YAAY;AAClC,UAAM,kBAAkB,KAAK,mBAAmB;AAChD,UAAM,YAAY,KAAK,aAAa;AAEpC,UAAM,QAAQ,KAAK,OAAO,eAAe,MAAM;AAC/C,UAAM,YAAY,KAAK,aAAa;AAEpC,QAAI;AACJ,aAAS,UAAU,GAAG,WAAW,UAAU,WAAW;AACpD,UAAI;AAGF,cAAM,KAAK,UAAU,IAAI,EAAE,UAAU,CAAC;AAEtC,YAAI,kBAAkB,EAAG,OAAM,QAAQ,eAAe;AACtD;AAAA,MACF,SAAS,GAAG;AACV,oBAAY;AAKZ,cAAM,MAAM,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC;AACrD,cAAM,mBAAmB,IAAI,SAAS,kBAAkB;AACxD,cAAM,kBACJ,IAAI,YAAY,EAAE,SAAS,eAAe,KAC1C,IAAI,YAAY,EAAE,SAAS,eAAe;AAE5C,YAAI,UAAU,UAAU;AACtB,cAAI,cAAc,oBAAoB,kBAAkB;AACtD,gBAAI;AAEF,mBAAK,OAAO,WAAW;AACvB,oBAAM,KAAK,OAAO,MAAM;AAAA,YAC1B,QAAQ;AAAA,YAER;AAAA,UACF;AAEA,cAAI,YAAY,EAAG,OAAM,QAAQ,SAAS;AAC1C;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,UAAM,qBAAqB,QAAQ,YAAY,IAAI,MAAM,OAAO,SAAS,CAAC;AAAA,EAC5E;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAeA,MAAM,WAAW,SAAoC;AACnD,UAAM,KAAK,KAAK,iBAAiB,OAAO;AACxC,QAAI;AAGF,YAAM,QAAQ,KAAK;AAAA,QACjB,KAAK,eAAe,EAAE;AAAA,QACtB,IAAI;AAAA,UAAQ,CAAC,GAAG,WACd,WAAW,MAAM,OAAO,IAAI,MAAM,SAAS,CAAC,GAAG,GAAI;AAAA,QACrD;AAAA,MACF,CAAC;AAED,aAAO;AAAA,IACT,SAAS,OAAO;AAId,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,MAAM,WAAW,SAAqC;AACpD,UAAM,KAAK,KAAK,iBAAiB,OAAO;AACxC,UAAM,MAAM,MAAM,KAAK,QAAQ;AAAA,MAC7B,OAAO;AAAA,MACP,SAAS;AAAA,IACX,CAAC;AACD,WAAO,oBAAoB,EAAE,KAAK,SAAS,GAAG,CAAC;AAAA,EACjD;AAAA,EA2BA,MAAM,WACJ,MAQA,MAQe;AACf,UAAM,UACJ,OAAO,SAAS,WAAW,OAAQ;AACrC,UAAM,SACJ,OAAO,SAAS,WACX,OAMD;AACN,UAAM,KAAK,KAAK,iBAAiB,OAAO;AAExC,UAAM,sBAAsB,CAAC,aAA6B;AAExD,aAAO,KAAK,MAAM,MAAM,QAAQ;AAAA,IAClC;AAEA,UAAM,kBAAkB,CAAC,MAAmC;AAC1D,UAAI,MAAM,UAAa,MAAM,KAAM,QAAO;AAC1C,UAAI,OAAO,MAAM,UAAW,QAAO,IAAI,IAAI;AAC3C,YAAM,IAAI,OAAO,CAAC;AAClB,aAAO,OAAO,SAAS,CAAC,IAAI,IAAI;AAAA,IAClC;AAGA,UAAM,aAAa,MAAM,KAAK,QAAQ;AAAA,MACpC,OAAO;AAAA,MACP,SAAS;AAAA,IACX,CAAC;AAGD,QAAI,cAAc;AAElB,QAAI,OAAO,WAAW,QAAW;AAC/B,oBAAc,YAAY;AAAA,QACxB;AAAA,QACA,WAAW,OAAO,MAAM;AAAA,MAC1B;AAAA,IACF;AACA,QAAI,OAAO,cAAc,QAAW;AAClC,YAAM,MAAM,oBAAoB,OAAO,SAAS;AAChD,oBAAc,YAAY;AAAA,QACxB;AAAA,QACA,eAAe,GAAG;AAAA,MACpB;AAAA,IACF;AACA,QAAI,OAAO,gBAAgB,QAAW;AACpC,YAAM,IAAI,gBAAgB,OAAO,WAAW;AAC5C,UAAI,MAAM,QAAW;AACnB,sBAAc,YAAY;AAAA,UACxB;AAAA,UACA,qBAAqB,CAAC;AAAA,QACxB;AAAA,MACF;AAAA,IACF;AACA,QAAI,OAAO,aAAa,QAAW;AACjC,oBAAc,YAAY;AAAA,QACxB;AAAA,QACA,aAAa,OAAO,QAAQ;AAAA,MAC9B;AAAA,IACF;AAEA,UAAM,KAAK,QAAQ;AAAA,MACjB,OAAO;AAAA,MACP,SAAS;AAAA,MACT,YAAY;AAAA,IACd,CAAC;AAAA,EACH;AAAA,EAwBA,MAAM,mBACJ,MACA,MACA,MACe;AACf,UAAM,UAAU,OAAO,SAAS,WAAW,OAAO;AAClD,UAAM,UAAU,OAAO,SAAS,WAAY,OAAmB;AAC/D,UAAM,cACJ,OAAO,SAAS,WAAW,OAAQ;AACrC,UAAM,KAAK,KAAK,iBAAiB,OAAO;AAExC,UAAM,aAAa,MAAM,KAAK,QAAQ;AAAA,MACpC,OAAO;AAAA,MACP,SAAS;AAAA,IACX,CAAC;AAID,QAAI,cAAc;AAElB,QAAI,YAAY,QAAW;AACzB,oBAAc,YAAY;AAAA,QACxB;AAAA,QACA,WAAW,UAAU,MAAM,GAAG;AAAA,MAChC;AAAA,IACF;AACA,QAAI,gBAAgB,QAAW;AAC7B,oBAAc,YAAY;AAAA,QACxB;AAAA,QACA,uBAAuB,WAAW;AAAA,MACpC;AAAA,IACF;AAEA,UAAM,KAAK,QAAQ;AAAA,MACjB,OAAO;AAAA,MACP,SAAS;AAAA,MACT,YAAY;AAAA,IACd,CAAC;AAAA,EACH;AAAA,EA2BA,MAAM,eACJ,MACA,MACA,MACA,MACe;AACf,UAAM,UAAU,OAAO,SAAS,WAAW,OAAO;AAClD,UAAM,SAAS,OAAO,SAAS,WAAY,OAAkB;AAC7D,UAAM,cACJ,OAAO,SAAS,WAAW,OAAQ;AACrC,UAAM,WAAW,OAAO,SAAS,WAAW,OAAO;AACnD,UAAM,KAAK,KAAK,iBAAiB,OAAO;AAExC,UAAM,iBAAiB,MAAM,KAAK;AAAA,MAChC;AAAA,MACA;AAAA,IACF;AAIA,UAAM,SAAS;AAAA;AAAA;AAAA,SAGV,EAAE;AAAA,UACD,UAAU,cAAc,CAAC;AAAA;AAAA;AAI/B,UAAM,aAAa,MAAM,KAAK,QAAQ;AAAA,MACpC,OAAO;AAAA,MACP,SAAS;AAAA,MACT,YAAY;AAAA,IACd,CAAC;AAGD,QAAI,cAAc;AAElB,QAAI,gBAAgB,QAAW;AAC7B,oBAAc,YAAY;AAAA,QACxB;AAAA,QACA,gBAAgB,WAAW;AAAA,MAC7B;AAAA,IACF;AACA,QAAI,aAAa,QAAW;AAC1B,oBAAc,YAAY;AAAA,QACxB;AAAA,QACA,aAAa,QAAQ;AAAA,MACvB;AAAA,IACF;AAEA,UAAM,KAAK,QAAQ;AAAA,MACjB,OAAO;AAAA,MACP,SAAS;AAAA,MACT,YAAY;AAAA,IACd,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgBA,MAAM,SAAS,SAAuC;AAGpD,QAAI;AACF,YAAM,MAAM,MAAM,KAAK,QAAQ;AAAA,QAC7B,OAAO;AAAA,QACP,GAAI,YAAY,SAAY,EAAE,QAAQ,IAAI,CAAC;AAAA,MAC7C,CAAC;AAID,YAAM,SAAS,WAAW,KAAK,QAAQ;AACvC,aAAO;AAAA,QACL,SAAS,WAAW;AAAA,MACtB;AAAA,IACF,QAAQ;AAEN,aAAO,EAAE,SAAS,MAAM;AAAA,IAC1B;AAAA,EACF;AAAA,EAoBA,MAAM,SACJ,MACA,MACA,MACe;AACf,UAAM,UACJ,OAAO,SAAS,YAAa,QAAQ,IAAM;AAC7C,UAAM,KAAK,OAAO,SAAS,YAAY,OAAQ;AAC/C,UAAM,WACJ,OAAO,SAAS,YAAa,OAA8B;AAE7D,UAAM,YAAY,YAAY,SAAY,UAAU,IAAI;AACxD,QAAI;AAEJ,QAAI,aAAa,QAAW;AAE1B,mBAAa,mBAAmB,WAAW,QAAQ;AAAA,IACrD,OAAO;AAEL,YAAM,SAAS,KAAK,IAAI;AACxB,mBAAa,oBAAoB,WAAW,MAAM;AAAA,IACpD;AAEA,QAAI;AACF,YAAM,KAAK,QAAQ;AAAA,QACjB,OAAO;AAAA,QACP,GAAI,YAAY,SAAY,EAAE,QAAQ,IAAI,CAAC;AAAA,QAC3C;AAAA,MACF,CAAC;AAAA,IACH,SAAS,OAAO;AAEd,UAAI,OAAO,QAAQ,aAAa,QAAW;AACzC,qBAAa,mBAAmB,WAAW,CAAC;AAC5C,cAAM,KAAK,QAAQ;AAAA,UACjB,OAAO;AAAA,UACP,GAAI,YAAY,SAAY,EAAE,QAAQ,IAAI,CAAC;AAAA,UAC3C;AAAA,QACF,CAAC;AAAA,MACH,OAAO;AACL,cAAM;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,MAAM,iBAAiB,SAA0C;AAC/D,UAAM,KAAK,KAAK,iBAAiB,OAAO;AACxC,UAAM,MAAM,MAAM,KAAK,QAAQ;AAAA,MAC7B,OAAO;AAAA,MACP,SAAS;AAAA,IACX,CAAC;AACD,WAAO,0BAA0B,GAAG;AAAA,EACtC;AAAA,EAoBA,MAAM,iBACJ,MACA,MACA,MACe;AACf,UAAM,UAAU,OAAO,SAAS,WAAW,OAAQ,QAAQ;AAC3D,UAAM,KACJ,OAAO,SAAS,WACX,OACA;AACP,UAAM,aACJ,OAAO,SAAS,WAAW,OAAQ;AACrC,UAAM,KAAK,KAAK,iBAAiB,OAAO;AAMxC,QAAI,OAAO,QAAW;AACpB,UAAI;AACF,cAAM,aAAa,8BAA8B,IAAI,EAAE;AACvD,cAAM,KAAK,QAAQ;AAAA,UACjB,OAAO;AAAA,UACP,SAAS;AAAA,UACT;AAAA,QACF,CAAC;AAAA,MACH,QAAQ;AAEN,cAAM,aAAa,MAAM,KAAK,QAAQ;AAAA,UACpC,OAAO;AAAA,UACP,SAAS;AAAA,QACX,CAAC;AACD,cAAM,cAAc,wBAAwB,YAAY,EAAE;AAE1D,cAAM,KAAK,QAAQ;AAAA,UACjB,OAAO;AAAA,UACP,SAAS;AAAA,UACT,YAAY;AAAA,QACd,CAAC;AAAA,MACH;AAAA,IACF;AAEA,QAAI,eAAe,QAAW;AAC5B,YAAM,aAAa,MAAM,KAAK,QAAQ;AAAA,QACpC,OAAO;AAAA,QACP,SAAS;AAAA,MACX,CAAC;AACD,YAAM,cAAc,6BAA6B,YAAY,UAAU;AAEvE,YAAM,KAAK,QAAQ;AAAA,QACjB,OAAO;AAAA,QACP,SAAS;AAAA,QACT,YAAY;AAAA,MACd,CAAC;AAAA,IACH;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAeA,MAAM,sBAAsB,SAAgD;AAC1E,UAAM,KAAK,KAAK,iBAAiB,OAAO;AACxC,UAAM,MAAM,MAAM,KAAK,QAAQ;AAAA,MAC7B,OAAO;AAAA,MACP,SAAS;AAAA,IACX,CAAC;AACD,WAAO,2BAA2B,GAAG;AAAA,EACvC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAiBA,MAAM,sBAAsB,IAAa,SAAiC;AACxE,UAAM,KAAK,KAAK,iBAAiB,OAAO;AAGxC,UAAM,aAAa,MAAM,KAAK,QAAQ;AAAA,MACpC,OAAO;AAAA,MACP,SAAS;AAAA,IACX,CAAC;AAGD,UAAM,cAAc,6BAA6B,YAAY,EAAE;AAG/D,UAAM,KAAK,QAAQ;AAAA,MACjB,OAAO;AAAA,MACP,SAAS;AAAA,MACT,YAAY;AAAA,IACd,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAiBA,MAAM,sBACJ,SACA,UAKe;AACf,UAAM,KAAK,KAAK,iBAAiB,OAAO;AAGxC,UAAM,aAAa,MAAM,KAAK,QAAQ;AAAA,MACpC,OAAO;AAAA,MACP,SAAS;AAAA,IACX,CAAC;AAGD,UAAM,cAAc,6BAA6B,YAAY,QAAQ;AAGrE,UAAM,KAAK,QAAQ;AAAA,MACjB,OAAO;AAAA,MACP,SAAS;AAAA,MACT,YAAY;AAAA,IACd,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAmBA,MAAM,iBAIJ;AAEA,UAAM,OAAO,KAAK,OAAO;AACzB,UAAM,eAAe,6BAA6B,IAAI;AAEtD,UAAM,MAAM,MAAM,KAAK,QAAQ;AAAA,MAC7B,OAAO;AAAA,MACP;AAAA,IACF,CAAC;AAED,WAAO,oBAAoB,GAAG;AAAA,EAChC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,MAAM,kBACJ,YACA,SACiB;AACjB,UAAM,YAAY,MAAM,KAAK,eAAe;AAC5C,UAAM,aACJ,YAAY,UAAa,YAAY,OAAO,UAAU;AACxD,UAAM,mBAAmB,UAAU,UAAU;AAE7C,QAAI,CAAC,kBAAkB;AACrB,aAAO;AAAA,IACT;AAEA,UAAM,QAAQ,iBAAiB,UAAU;AACzC,QAAI,OAAO,UAAU,UAAU;AAC7B,aAAO;AAAA,IACT;AAGA,QAAI,OAAO,UAAU,UAAU;AAC7B,YAAM,WAAW,OAAO,KAAK;AAC7B,aAAO,OAAO,MAAM,QAAQ,IAAI,IAAI;AAAA,IACtC;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,eAAe,SAGgB;AACnC,UAAM,MAAM,MAAM,KAAK,QAAQ;AAAA,MAC7B,OAAO;AAAA,MACP,GAAI,SAAS,aAAa,OAAO,EAAE,WAAW,QAAQ,UAAU,IAAI,CAAC;AAAA,MACrE,GAAI,SAAS,gBAAgB,OACzB,EAAE,cAAc,QAAQ,aAAa,IACrC,CAAC;AAAA,IACP,CAAC;AACD,WAAO,gBAAgB,GAAG;AAAA,EAC5B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,+BACJ,SACA,SACkB;AAClB,UAAM,KAAK,KAAK,iBAAiB,OAAO;AAExC,UAAM,YAAY,SAAS,aAAa;AAExC,QAAI;AACF,YAAM,MAAM,MAAM,KAAK,QAAQ;AAAA,QAC7B,OAAO;AAAA,QACP,SAAS;AAAA,QACT;AAAA,MACF,CAAC;AACD,WAAK,OAAO;AAAA,QACV,4DAA4D,EAAE;AAAA,EAAM,GAAG;AAAA,MACzE;AAEA,aAAO,+EAA+E;AAAA,QACpF;AAAA,MACF;AAAA,IACF,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,MAAM,yBACJ,SACA,SACkB;AAClB,UAAM,KAAK,KAAK,iBAAiB,OAAO;AACxC,UAAM,YAAY,SAAS,aAAa;AAExC,QAAI;AACF,YAAM,MAAM,MAAM,KAAK,QAAQ,EAAE,OAAO,KAAK,SAAS,IAAI,UAAU,CAAC;AACrE,YAAM,oBAAoB,WAAW,KAAK,gBAAgB;AAC1D,YAAM,iBAAiB,OAAO,qBAAqB,CAAC;AACpD,aAAO,iBAAiB;AAAA,IAC1B,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,iBACJ,SACA,SAC+B;AAC/B,UAAM,KAAK,KAAK,iBAAiB,OAAO;AACxC,UAAM,YAAY,SAAS,aAAa;AAExC,QAAI;AACF,YAAM,MAAM,MAAM,KAAK,QAAQ,EAAE,OAAO,KAAK,SAAS,IAAI,UAAU,CAAC;AACrE,YAAM,iBAAiB,WAAW,KAAK,YAAY,KAAK,IAAI,KAAK;AACjE,UAAI,CAAC,cAAe,QAAO;AAE3B,YAAM,OAAO,cACV,MAAM,GAAG,EACT,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EACnB,OAAO,OAAO;AAEjB,aAAO,KAAK,SAAS,IAAI,OAAO;AAAA,IAClC,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgBA,MAAM,sBACJ,SACmC;AACnC,UAAM,KAAK,KAAK,iBAAiB,OAAO;AAGxC,UAAM,SAAS,KAAK,wBAAwB,IAAI,EAAE;AAClD,QACE,UACA,KAAK,IAAI,IAAI,OAAO,aAClB,oBAAmB,2BACrB;AACA,aAAO,OAAO;AAAA,IAChB;AAGA,UAAM,CAAC,eAAe,eAAe,IAAI,MAAM,QAAQ,WAAW;AAAA,MAChE,KAAK,eAAe,EAAE,WAAW,IAAK,CAAC;AAAA,MACvC,KAAK,eAAe;AAAA,IACtB,CAAC;AAED,UAAM,UACJ,cAAc,WAAW,cAAc,cAAc,QAAQ;AAC/D,UAAM,YACJ,gBAAgB,WAAW,cACvB,gBAAgB,QAChB;AAGN,UAAM,cAAc,KAAK,oBAAoB,SAAS,EAAE;AAGxD,UAAM,eAAe,KAAK;AAAA,MACxB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAOA,UAAM,OAAO;AACb,UAAM,YAAY,MAAM;AACxB,UAAM,UAAU,MAAM;AACtB,UAAM,UAAU,MAAM;AACtB,UAAM,gBAAgB,MAAM;AAC5B,UAAM,gBAAgB,MAAM;AAG5B,UAAM,QAAQ,MAAM,KAAK,YAAY;AAIrC,QAAI,OAAO;AAET,mBAAa,iBAAiB,WAAW,KAAK;AAAA,IAChD,WAAW,cAAc,QAAW;AAElC,YAAM,SAAS,MAAM,KAAK,+BAA+B,IAAI;AAAA,QAC3D,WAAW;AAAA,MACb,CAAC;AACD,mBAAa,gBAAgB;AAAA,IAC/B;AAIA,UAAM,WAAW,KAAK,yBAAyB,OAAO;AAGtD,UAAM,UAAU,MAAM,KAAK,iBAAiB,IAAI,EAAE,WAAW,KAAK,CAAC;AAMnE,UAAM,qBAAqB,MAAM,KAAK,yBAAyB,IAAI;AAAA,MACjE,WAAW;AAAA,IACb,CAAC;AACD,iBAAa,kBAAkB;AAG/B,QAAI;AACJ,QAAI,aAAa,YAAY;AAC3B,UAAI;AACF,kBAAU,MAAM,KAAK,cAAc,EAAE;AAErC,qBAAa,aAAa,QAAQ,SAAS;AAAA,MAC7C,QAAQ;AACN,qBAAa,aAAa;AAAA,MAC5B;AAAA,IACF;AAGA,UAAM,QAAqC;AAAA,MACzC,SAAS;AAAA,MACT,iBAAiB,KAAK;AAAA,MACtB,WAAW,KAAK,OAAO,eAAe,KAAK;AAAA,MAC3C,gBAAgB,KAAK,OAAO,KAAK,QAAQ;AAAA,MACzC,UAAU,KAAK,OAAO;AAAA,MACtB,YAAY,KAAK,OAAO;AAAA,MACxB,oBAAoB,QAAQ,SAAS;AAAA,MACrC,kBAAkB,QAAQ,OAAO;AAAA,MACjC;AAAA,MACA,GAAI,cAAc,UAAa,EAAE,UAAU;AAAA,MAC3C,GAAI,YAAY,UAAa,EAAE,QAAQ;AAAA,MACvC,GAAI,YAAY,UAAa,EAAE,QAAQ;AAAA,MACvC,GAAI,kBAAkB,UAAa,EAAE,cAAc;AAAA,MACnD,GAAI,kBAAkB,UAAa,EAAE,cAAc;AAAA,MACnD,GAAI,aAAa,UAAU,UAAa;AAAA,QACtC,kBAAkB,YAAY;AAAA,MAChC;AAAA,MACA,GAAI,aAAa;AAAA,QACf,uBAAuB,OAAO,KAAK,SAAS,EAAE;AAAA,MAChD;AAAA,MACA,GAAI,SAAS,SAAS,EAAE,kBAAkB,QAAQ,MAAM,OAAO;AAAA,IACjE;AAEA,UAAM,SAAmC;AAAA,MACvC;AAAA,MACA;AAAA,MACA,GAAI,aAAa,EAAE,UAAU;AAAA,MAC7B,GAAI,WAAW,EAAE,QAAQ;AAAA,MACzB,GAAI,WAAW,EAAE,QAAQ;AAAA,MACzB,GAAI,WAAW,EAAE,QAAQ;AAAA,MACzB,GAAI,YAAY,EAAE,SAAS;AAAA,IAC7B;AAGA,SAAK,wBAAwB,IAAI,IAAI;AAAA,MACnC;AAAA,MACA,YAAY,KAAK,IAAI;AAAA,IACvB,CAAC;AAED,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,uBAAuB,SAAwB;AAC7C,QAAI,YAAY,QAAW;AACzB,WAAK,wBAAwB,OAAO,OAAO;AAAA,IAC7C,OAAO;AACL,WAAK,wBAAwB,MAAM;AAAA,IACrC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,oBACN,SACA,SACyB;AACzB,QAAI,CAAC,SAAS,OAAO,OAAQ,QAAO;AAEpC,UAAM,aAAa,QAAQ,MAAM,OAAO,CAAC,MAAM,EAAE,UAAU,OAAO;AAClE,QAAI,CAAC,WAAW,OAAQ,QAAO;AAG/B,UAAM,QAAQ,CAAC,SAA8B;AAC3C,YAAM,UAAU;AAChB,UAAI,SAAS;AAEb,UAAI,QAAQ,QAAQ,KAAM,WAAU;AAEpC,YAAM,iBAAiB;AAAA,QACrB;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AACA,iBAAW,KAAK,gBAAgB;AAC9B,YAAI,QAAQ,CAAC,MAAM,OAAW,WAAU;AAAA,MAC1C;AACA,aAAO;AAAA,IACT;AAEA,WAAO,WAAW,KAAK,CAAC,GAAG,MAAM,MAAM,CAAC,IAAI,MAAM,CAAC,CAAC,EAAE,CAAC;AAAA,EACzD;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,6BACN,SACA,aACA,SACA,WACsC;AACtC,UAAM,SAAS,CAAC,MAAwB;AACtC,UAAI,OAAO,MAAM,SAAU,QAAO,IAAI;AACtC,UAAI,OAAO,MAAM,UAAU;AACzB,cAAM,IAAI,OAAO,CAAC;AAClB,eAAO,OAAO,SAAS,CAAC,IAAI,IAAI,IAAI,EAAE,SAAS,KAAK,MAAM;AAAA,MAC5D;AACA,aAAO,QAAQ,CAAC;AAAA,IAClB;AAEA,UAAM,OAAO;AACb,UAAM,UAAU,SAAS,SAAS,YAAY;AAG9C,UAAM,UAAU,OAAO,OAAO,KAAK,OAAO,IAAI;AAC9C,UAAM,aAAa,OAAO,OAAO,KAAK,UAAU,IAAI;AACpD,UAAM,iBAAiB,WAAW;AAClC,UAAM,iBAAiB,UACnB,YAAY,UAAU,YAAY,MAClC;AAGJ,UAAM,aAAa,UACf,QAAQ,SAAS,IAAI,KAAK,YAAY,QACtC;AACJ,UAAM,UAAU,UAAU,QAAQ,SAAS,GAAG,IAAI;AAGlD,UAAM,aAAa,OAAO,OAAO,KAAK,SAAS,IAAI;AAGnD,UAAM,aAAa,OAAO,OAAO,KAAK,OAAO,IAAI;AAIjD,UAAM,WAAW,OAAO,OAAO,KAAK,YAAY,IAAI;AAIpD,UAAM,YAAY,MAAM;AACxB,UAAM,gBACJ,OAAO,cAAc,WAAW,aAAa,IAAI;AAGnD,UAAM,SAAS,OACX,OAAO,KAAK,KAAK,KAAK,OAAO,KAAK,QAAQ,KAAK,OAAO,KAAK,SAAS,IACpE;AAGJ,UAAM,aAAa,OAAO,OAAO,KAAK,eAAe,IAAI;AAGzD,UAAM,cACJ,OAAO,SAAS,SAAS,MAAM,OAAO,OAAO,KAAK,YAAY,IAAI;AAEpE,WAAO;AAAA,MACL;AAAA,MACA,GAAI,WAAW,EAAE,QAAQ;AAAA,MACzB,QAAQ;AAAA,MACR,SAAS;AAAA,MACT;AAAA,MACA;AAAA,MACA,QAAQ,kBAAkB,kBAAkB,cAAc;AAAA,MAC1D;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA;AAAA;AAAA,MAIA,iBAAiB,OACb,OAAO,KAAK,MAAM,KAAK,OAAO,KAAK,OAAO,IAC1C;AAAA,IACN;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,yBACN,SACgC;AAChC,QAAI,CAAC,QAAS,QAAO;AAErB,UAAM,SAAS,CAAC,MAAwB;AACtC,UAAI,OAAO,MAAM,SAAU,QAAO,IAAI;AACtC,UAAI,OAAO,MAAM,UAAU;AACzB,cAAM,IAAI,OAAO,CAAC;AAClB,eAAO,OAAO,SAAS,CAAC,IAAI,IAAI,IAAI,EAAE,SAAS,KAAK,MAAM;AAAA,MAC5D;AACA,aAAO,QAAQ,CAAC;AAAA,IAClB;AAEA,WAAO;AAAA,MACL,MAAM,OAAO,QAAQ,IAAI;AAAA,MACzB,OAAO,OAAO,QAAQ,KAAK;AAAA,MAC3B,MAAM,OAAO,QAAQ,IAAI;AAAA,MACzB,QAAQ,OAAO,QAAQ,MAAM;AAAA,MAC7B,KAAK,OAAO,QAAQ,GAAG;AAAA,MACvB,OAAO,OAAO,QAAQ,KAAK;AAAA,MAC3B,WAAW,OAAO,QAAQ,SAAS;AAAA,MACnC,WAAW,OAAO,QAAQ,SAAS;AAAA,IACrC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgCA,MAAM,uBACJ,SACA,SAIkC;AAClC,UAAM,QAAQ,SAAS,UAAU;AACjC,UAAM,cAAc,KAAK,iBAAiB,OAAO;AAGjD,QAAI;AACJ,QAAI;AACJ,QAAI;AAEJ,QAAI;AACF,YAAM,aAAa,MAAM,KAAK,QAAQ,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC;AACjE,cAAQ,WAAW,MAAM,KAAK;AAAA,IAChC,QAAQ;AAAA,IAER;AAEA,QAAI;AACF,YAAM,eAAe,MAAM,KAAK,sBAAsB,OAAO;AAC7D,mBAAa,aAAa,SAAS;AACnC,oBAAc,aAAa;AAAA,IAC7B,QAAQ;AAAA,IAER;AAIA,QAAI,kBAAkB,QAAQ,MAAM,KAAK,IAAI;AAG7C,QAAI,CAAC,mBAAmB,aAAa;AAEnC,iBAAW,QAAQ,YAAY,SAAS,CAAC,GAAG;AAC1C,cAAM,WAAW,KAAK,UAAU;AAChC,YAAI,OAAO,aAAa,YAAY,SAAS,KAAK,GAAG;AACnD,4BAAkB,SAAS,KAAK;AAChC;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAGA,UAAM,kBAAkB,CACtB,aACA,iBACY;AACZ,UAAI,CAAC,gBAAgB,aAAa,WAAW,EAAG,QAAO;AACvD,YAAM,QAAQ,aAAa,YAAY,EAAE,KAAK;AAC9C,iBAAW,SAAS,aAAa;AAC/B,cAAM,aAAa,MAAM,YAAY,EAAE,KAAK;AAE5C,YAAI,UAAU,WAAY,QAAO;AAGjC,YAAI,MAAM,SAAS,UAAU,KAAK,WAAW,SAAS,KAAK;AACzD,iBAAO;AAET,YAAI,MAAM,SAAS,UAAU,KAAK,WAAW,SAAS,UAAU;AAC9D,iBAAO;AACT,YAAI,MAAM,SAAS,KAAK,KAAK,WAAW,SAAS,KAAK,EAAG,QAAO;AAAA,MAClE;AACA,aAAO;AAAA,IACT;AAEA,UAAM,oBAAoB,kBACtB,gBAAgB,8BAA8B,eAAe,IAC7D;AACJ,UAAM,sBAAsB,kBACxB,gBAAgB,gCAAgC,eAAe,IAC/D;AAIJ,UAAM,kBACJ,OAAO,eAAe,WAClB,OAAO,SAAS,YAAY,EAAE,IAC9B;AACN,UAAM,0BACJ,oBAAoB,KAAK,OAAO,SAAS,eAAe;AAG1D,UAAM,aACJ,qBAAqB,uBAAuB;AAE9C,QAAI,CAAC,YAAY;AACf,aAAO;AAAA,QACL,YAAY;AAAA,QACZ,OAAO;AAAA,QACP,oBAAoB;AAAA,QACpB,qBAAqB;AAAA,QACrB,UAAU,CAAC;AAAA,QACX,oBAAoB;AAAA,UAClB,KAAK,CAAC;AAAA,UACN,MAAM,CAAC;AAAA,UACP,MAAM,CAAC;AAAA,UACP,QAAQ,CAAC;AAAA,UACT,UAAU,CAAC;AAAA,UACX,SAAS,CAAC;AAAA,QACZ;AAAA,MACF;AAAA,IACF;AAIA,QAAI,eACF,oBACI,gBACA,sBACE,kBACA;AAIR,QAAI,CAAC,gBAAgB,yBAAyB;AAC5C,YAAM,aAAa,iBAAiB,YAAY,KAAK;AACrD,UAAI,WAAW,SAAS,UAAU,GAAG;AACnC,uBAAe;AAAA,MACjB,WAAW,WAAW,SAAS,KAAK,GAAG;AACrC,uBAAe;AAAA,MACjB,WAAW,oBAAoB,GAAG;AAEhC,uBAAe;AAAA,MACjB;AAAA,IACF;AAIA,UAAM,iBAA2B,CAAC;AAClC,UAAM,kBAA4B,CAAC;AAEnC,QAAI,iBAAiB,iBAAiB;AAEpC,UAAI,OAAO;AAET,uBAAe,KAAK,WAAW;AAAA,MACjC,OAAO;AACL,uBAAe,KAAK,GAAG,CAAC;AAAA,MAC1B;AACA,sBAAgB,KAAK,QAAQ,cAAc,CAAC;AAAA,IAC9C,WAAW,iBAAiB,eAAe;AAEzC,UAAI,oBAAoB,GAAG;AACzB,uBAAe,KAAK,GAAG,CAAC;AACxB,wBAAgB,KAAK,GAAG,CAAC;AAAA,MAC3B,OAAO;AACL,uBAAe,KAAK,CAAC;AACrB,wBAAgB,KAAK,CAAC;AAAA,MACxB;AAAA,IACF,OAAO;AAEL,UACE,mBACA,OAAO,SAAS,eAAe,KAC/B,mBAAmB,GACnB;AACA,iBAAS,IAAI,GAAG,IAAI,iBAAiB,KAAK;AACxC,yBAAe,KAAK,CAAC;AACrB,0BAAgB,KAAK,CAAC;AAAA,QACxB;AAAA,MACF,OAAO;AAEL,uBAAe,KAAK,GAAG,CAAC;AACxB,wBAAgB,KAAK,CAAC;AAAA,MACxB;AAAA,IACF;AAGA,UAAM,eAAsC,CAAC;AAE7C,eAAW,MAAM,gBAAgB;AAC/B,UAAI;AAEF,cAAM,iBAAiB,MAAM,KAAK,sBAAsB,EAAE;AAC1D,cAAM,OAAO,eAAe;AAC5B,cAAM,YAAY,eAAe;AACjC,cAAM,aAAa,eAAe;AAKlC,YAAI,YAAY;AAChB,YAAI,iBAAiB,iBAAiB;AACpC,sBAAY,QAAQ,QAAQ,cAAc;AAAA,QAC5C,WAAW,iBAAiB,eAAe;AACzC,sBAAY,gBAAgB,SAAS,EAAE;AAAA,QACzC,OAAO;AAEL,sBAAY,OAAO;AAAA,QACrB;AAGA,cAAM,mBAAmB;AAAA,UACvB,MAAM;AAAA,UACN,MAAM;AAAA,UACN,QAAQ;AAAA;AAAA,QACV;AAGA,YAAI,YAAY,QAAQ,WAAW,MAAM;AACvC,2BAAiB,OAAO;AAAA,QAC1B,OAAO;AAEL,cAAI;AAEF,kBAAM,cAAc,WAAW;AAC/B,gBAAI,OAAO,gBAAgB,YAAY,cAAc,GAAG;AACtD,+BAAiB,OAAO;AAAA,YAC1B;AAAA,UACF,QAAQ;AAAA,UAER;AAAA,QACF;AAGA,cAAM,UAAU,YAAY,UAAU,MAAM,IAAI;AAChD,YAAI,OAAO,YAAY,YAAY,UAAU,GAAG;AAC9C,2BAAiB,OAAO;AAAA,QAC1B;AAEA,cAAM,kBAAkB,CACtB,aAC6B;AAC7B,cAAI,aAAa,OAAQ,QAAO;AAGhC,iBAAO;AAAA,QACT;AAIA,YAAI,UAAU,KAAK,WAAW;AAC9B,YAAI,CAAC,SAAS,iBAAiB,mBAAmB,OAAO,GAAG;AAE1D,oBAAU;AAAA,QACZ;AAEA,cAAM,WAAW,CAAC,aAA0C;AAC1D,uBAAa,KAAK;AAAA,YAChB,SAAS;AAAA,YACT,QAAQ,KAAK,UAAU;AAAA,YACvB,SAAS,KAAK,WAAW;AAAA,YACzB;AAAA,YACA;AAAA,YACA,aAAa,KAAK,eAAe;AAAA,YACjC,YAAY,KAAK,cAAc;AAAA,YAC/B,GAAI,WAAW,EAAE,SAAS,IAAI,CAAC;AAAA,YAC/B,GAAI,WAAW,EAAE,aAAa,gBAAgB,QAAQ,EAAE,IAAI,CAAC;AAAA,YAC7D;AAAA,UACF,CAAC;AAAA,QACH;AAGA,YAAI,SAAS,iBAAiB,mBAAmB,OAAO,aAAa;AACnE,mBAAS,MAAM;AAEf,uBAAa,KAAK;AAAA,YAChB,SAAS;AAAA,YACT,QAAQ,KAAK,UAAU;AAAA,YACvB,SAAS,KAAK,WAAW;AAAA,YACzB,SAAS;AAAA,YACT;AAAA,YACA,aAAa,KAAK,eAAe;AAAA,YACjC,YAAY,KAAK,cAAc;AAAA,YAC/B,UAAU;AAAA,YACV,aAAa,gBAAgB,WAAW;AAAA,YACxC;AAAA,UACF,CAAC;AAAA,QACH,WAAW,OAAO,GAAG;AACnB,mBAAS,MAAM;AAAA,QACjB,WAAW,OAAO,GAAG;AACnB,mBAAS,WAAW;AAAA,QACtB,OAAO;AACL,mBAAS,MAAS;AAAA,QACpB;AAAA,MACF,SAAS,KAAK;AAEZ,SAAC,KAAK,OAAO,QAAQ,KAAK,OAAO,KAAK;AAAA,UACpC,KAAK;AAAA,UACL,iEAAiE,EAAE,KAAK,GAAG;AAAA,QAC7E;AAAA,MACF;AAAA,IACF;AAGA,UAAM,OAAO,CAAC,OAA2B,MAAM,KAAK,IAAI,IAAI,EAAE,CAAC;AAC/D,UAAM,qBAAqB;AAAA,MACzB,KAAK,KAAK,aAAa,OAAO,CAAC,OAAO,GAAG,MAAM,EAAE,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;AAAA,MACxE,MAAM;AAAA,QACJ,aAAa,OAAO,CAAC,OAAO,GAAG,OAAO,EAAE,IAAI,CAAC,OAAO,GAAG,OAAO;AAAA,MAChE;AAAA,MACA,MAAM;AAAA,QACJ,aAAa,OAAO,CAAC,OAAO,GAAG,OAAO,EAAE,IAAI,CAAC,OAAO,GAAG,OAAO;AAAA,MAChE;AAAA,MACA,QAAQ;AAAA,QACN,aAAa,OAAO,CAAC,OAAO,GAAG,SAAS,EAAE,IAAI,CAAC,OAAO,GAAG,OAAO;AAAA,MAClE;AAAA,MACA,UAAU;AAAA,QACR,aAAa,OAAO,CAAC,OAAO,GAAG,WAAW,EAAE,IAAI,CAAC,OAAO,GAAG,OAAO;AAAA,MACpE;AAAA,MACA,SAAS;AAAA,QACP,aAAa,OAAO,CAAC,OAAO,GAAG,UAAU,EAAE,IAAI,CAAC,OAAO,GAAG,OAAO;AAAA,MACnE;AAAA,IACF;AAEA,WAAO;AAAA,MACL,YAAY;AAAA,MACZ;AAAA,MACA,OAAO;AAAA,MACP,oBAAoB,eAAe;AAAA,MACnC,qBAAqB,gBAAgB;AAAA,MACrC,UAAU;AAAA,MACV;AAAA,IACF;AAAA,EACF;AAAA,EA4BA,MAAM,iBACJ,kBACA,kBACA,cAC6B;AAC7B,UAAM,oBAAoB,OAAO,qBAAqB;AACtD,UAAM,KAAK,oBAAoB,KAAK,iBAAiB,gBAAgB,IAAI;AAEzE,QAAI;AACJ,QAAI;AACJ,QAAI,mBAAmB;AACrB,UAAI,OAAO,qBAAqB,UAAU;AACxC,cAAM,IAAI;AAAA,UACR;AAAA,QACF;AAAA,MACF;AACA,gBAAU;AACV,gBAAU;AAAA,IACZ,OAAO;AACL,gBAAU;AACV,gBACE,OAAO,qBAAqB,YAAY,qBAAqB,OACzD,mBACA;AAAA,IACR;AAGA,QAAI;AACJ,QAAI;AACF,YAAM,WAAW,MAAM,KAAK,kBAAkB,EAAE;AAChD,YAAM,SAAS,SAAS,QAAQ,KAAK,CAAC,MAAM,EAAE,YAAY,OAAO;AACjE,UAAI,QAAQ,aAAc,cAAa,OAAO;AAAA,IAChD,SAAS,OAAO;AAEd,WAAK,OAAO;AAAA,QACV,iFAAiF,iBAAiB,QAAQ,MAAM,UAAU,KAAK;AAAA,MACjI;AAAA,IACF;AAEA,UAAM,cAAyC;AAAA,MAC7C,KAAK;AAAA,MACL,SAAS;AAAA,MACT;AAAA,MACA,GAAI,SAAS,YAAY,SAAY,EAAE,SAAS,QAAQ,QAAQ,IAAI,CAAC;AAAA,MACrE,GAAI,SAAS,eAAe,SACxB,EAAE,YAAY,QAAQ,WAAW,IACjC,CAAC;AAAA,MACL,GAAI,SAAS,eAAe,SACxB,EAAE,YAAY,QAAQ,WAAW,IACjC,CAAC;AAAA,MACL,GAAI,SAAS,SAAS,SAAY,EAAE,MAAM,QAAQ,KAAK,IAAI,CAAC;AAAA,MAC5D,QAAQ,KAAK;AAAA,IACf;AAEA,UAAM,SAAS,IAAI,mBAAmB,WAAW;AACjD,UAAM,OAAO,MAAM;AAGnB,SAAK,YAAY,IAAI,MAAM;AAG3B,WAAO,KAAK,SAAS,MAAM;AACzB,WAAK,YAAY,OAAO,MAAM;AAAA,IAChC,CAAC;AAED,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,wBAAwB,SAKe;AAC3C,UAAM,QAAQ,SAAS,UAAU;AACjC,UAAM,UAAU,SAAS;AACzB,UAAM,gBAAgB,SAAS,kBAAkB;AAEjD,UAAM,cAAwC,SAAS,QAAQ;AAC/D,UAAM,WAAW,KAAK,UAAU;AAAA,MAC9B,SAAS,WAAW;AAAA,MACpB;AAAA,MACA;AAAA,MACA,MAAM;AAAA,IACR,CAAC;AAED,UAAM,SAAS,KAAK,wBAAwB,IAAI,QAAQ;AAExD,UAAM,aAAa,CAAC,MAClB,EAAE,cAAc,SAAS,KACzB,EAAE,YAAY,SAAS,KACvB,EAAE,YAAY,SAAS;AAEzB,UAAM,kBAAkB,CAACE,YAA4C;AAEnE,UAAI,WAAWA,OAAM,GAAG;AACtB,aAAK,wBAAwB,IAAI,UAAUA,OAAM;AACjD,eAAOA;AAAA,MACT;AAEA,UAAI,UAAU,WAAW,MAAM,GAAG;AAChC,eAAO;AAAA,MACT;AAEA,aAAOA;AAAA,IACT;AAEA,UAAM,WAAW,CAAC,KAAa,SAAyB;AACtD,WAAK,OAAO,MAAM,KAAK,IAAI;AAAA,IAC7B;AAEA,UAAM,WAAW,gBAAgB;AACjC,UAAM,WAAW,gBAAgB;AAEjC,UAAM,cAAwC,CAAC;AAC/C,UAAM,cAAwC,CAAC;AAC/C,UAAM,gBAA0C,CAAC;AAEjD,UAAM,KAAK,KAAK,iBAAiB,OAAO;AAIxC,QAAI,eAAe;AACnB,QAAI;AACJ,QAAI,aAAa;AACjB,QAAI;AAGF,YAAM,OAAO,MAAM,KAAK,QAAQ,QAAQ,KAAK,QAAW;AAAA,QACtD,MAAM,CAAC,MAAM;AAAA,MACf,CAAC;AACD,cAAQ,OAAO,KAAK,SAAS,WAAW,KAAK,KAAK,YAAY,IAAI;AAClE,qBAAe,iBAAiB,KAAK;AACrC,mBAAa,MAAM,SAAS,UAAU;AAAA,IACxC,SAAS,GAAG;AACV;AAAA,QACE;AAAA,QACA;AAAA,UACE,MAAM,KAAK;AAAA,UACX;AAAA,UACA;AAAA,UACA,mBAAmB;AAAA,UACnB,KAAK,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC;AAAA,QAChD;AAAA,MACF;AAAA,IACF;AAEA,aAAS,wDAAwD;AAAA,MAC/D,MAAM,KAAK;AAAA,MACX;AAAA,MACA;AAAA,MACA,mBAAmB;AAAA,MACnB;AAAA,MACA,MAAM,SAAS,QAAQ;AAAA,MACvB;AAAA,MACA;AAAA,MACA,UAAU,EAAE,cAAc,YAAY,MAAM;AAAA,IAC9C,CAAC;AAID,UAAM,2BAA2B;AAKjC,QAAI,iBAAiB,CAAC,cAAc;AAClC;AAAA,QACE;AAAA,QACA;AAAA,UACE,MAAM,KAAK;AAAA,UACX;AAAA,UACA,mBAAmB;AAAA,UACnB;AAAA,UACA;AAAA,QACF;AAAA,MACF;AACA,YAAMA,UAAS;AAAA,QACb;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAEA,aAAO,gBAAgBA,OAAM;AAAA,IAC/B;AAEA,QAAI,iBAAiB,iBAAiB,YAAY,SAAY;AAC5D,UAAI;AACJ,UAAI;AACF,wBAAgB,MAAM,KAAK,kBAAkB,CAAC;AAAA,MAChD,SAAS,GAAG;AACV;AAAA,UACE;AAAA,UACA;AAAA,YACE,MAAM,KAAK;AAAA,YACX,KAAK,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC;AAAA,UAChD;AAAA,QACF;AAAA,MACF;AAEA,YAAM,eAAe,eAAe,WAAW,CAAC;AAChD,YAAM,YAAY,aAAa,KAAK,CAAC,MAAM,EAAE,YAAY,MAAM;AAC/D,YAAM,kBACJ,OAAO,WAAW,iBAAiB,WAC/B,UAAU,aAAa,YAAY,EAAE,SAAS,KAAK,IACnD;AACN;AAAA,QACE;AAAA,QACA;AAAA,UACE,MAAM,KAAK;AAAA,UACX,mBAAmB,aAAa;AAAA,UAChC,UAAU,aAAa,IAAI,CAAC,MAAM,EAAE,OAAO;AAAA,UAC3C;AAAA,QACF;AAAA,MACF;AAMA,YAAM,kBAAiC,aAAa;AAAA,QAClD,CAAC,MAAM,EAAE,YAAY;AAAA,MACvB,IACI,QACE,aAAa,CAAC,GAAG,WAA6B;AACpD,YAAM,uBACJ,mBAAmB,aAAa,KAAK,CAAC,MAAM,EAAE,YAAY,MAAM,IAC5D,SACA;AAEN,YAAM,oBAAqC,CAAC,QAAQ,KAAK;AACzD,iBAAW,eAAe,mBAAmB;AAC3C,cAAM,wBACJ,gBAAgB,UAAU,uBACtB,uBACA;AACN,cAAM,wBACJ,aAAa,KAAK,CAAC,MAAM,EAAE,YAAY,qBAAqB,KAC5D,aAAa,CAAC;AAEhB,cAAM,eAAe,IAAI;AAAA,UACvB,cAAc,KAAK,IAAI,sBAAsB,WAAW;AAAA,QAC1D;AACA,cAAM,uBAAuB,IAAI;AAAA,UAC/B,cAAc,KAAK,IAAI,sBAAsB,WAAW;AAAA,QAC1D;AACA,6BAAqB,WAAW,KAAK;AACrC,6BAAqB,WAAW,KAAK;AAKrC,sBAAc,KAAK;AAAA,UACjB,MAAM,oBAAoB,WAAW;AAAA,UACrC,IAAI,oBAAoB,WAAW,IAAI,qBAAqB,IAAI,WAAW;AAAA,UAC3E,WAAW;AAAA,UACX,SAAS;AAAA,UACT,MAAM;AAAA,UACN,KAAK,aAAa,SAAS;AAAA,UAC3B,aAAa,qBAAqB,SAAS;AAAA,UAC3C,GAAI,wBAAwB,EAAE,UAAU,sBAAsB,IAAI,CAAC;AAAA,QACrE,CAAC;AAKD,YAAIC,eAAc;AAClB,YAAI;AACF,gBAAMC,WAAU,MAAM,KAAK,WAAW;AACtC,UAAAD,eAAcC,SAAQ,MAAM,WAAW;AAAA,QACzC,QAAQ;AACN,UAAAD,eAAc;AAAA,QAChB;AAKA,cAAM,uBACJ,SAAS,cAAc,gBAAgB,QAAQ,SAAS;AAE1D,cAAM,cACJ,CAAC,CAAC,yBACF,OAAO,sBAAsB,gBAAgB,EAAE,EAC5C,YAAY,EACZ,SAAS,KAAK;AAGnB,cAAM,UAAU;AAChB,YAAIA,gBAAe,SAAS;AAC1B,wBAAc,KAAK;AAAA,YACjB,MAAM,kBAAkB,WAAW;AAAA,YACnC,IAAI,kBAAkB,WAAW,IAAI,qBAAqB,IAAI,oBAAoB;AAAA,YAClF,WAAW;AAAA,YACX,SAAS;AAAA,YACT,MAAM;AAAA,YACN,KAAK,aAAa,SAAS;AAAA,YAC3B,aAAa,qBAAqB,SAAS;AAAA,YAC3C,GAAI,wBACA,EAAE,UAAU,sBAAsB,IAClC,CAAC;AAAA,UACP,CAAC;AAAA,QACH;AAAA,MACF;AAIA;AAAA,QACE;AAAA,QACA;AAAA,UACE,MAAM,KAAK;AAAA,UACX,eAAe,cAAc,IAAI,CAAC,MAAM,EAAE,EAAE;AAAA,QAC9C;AAAA,MACF;AAEA,aAAO;AAAA,QACL;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAEA,UAAM,0BAA0B,CAAC,MAAwC;AACvE,YAAM,MACJ,OAAO,GAAG,iBAAiB,WAAW,EAAE,aAAa,YAAY,IAAI;AACvE,UAAI,IAAI,SAAS,KAAK,EAAG,QAAO;AAChC,UAAI,IAAI,SAAS,KAAK,EAAG,QAAO;AAChC,aAAO;AAAA,IACT;AAEA,UAAM,WAAW,CAAC,WAQN;AAIV,YAAM,aAAa,OAAO,OAAO,UAAU,CAAC,EAAE,SAAS,GAAG,GAAG;AAC7D,YAAM,WAAW,OAAO,wBACpB,KACA,wBAAwB,OAAO,QAAQ;AAC3C,YAAM,SAAS,WAAW,GAAG,QAAQ,KAAK;AAC1C,YAAM,SAAS,GAAG,MAAM,WAAW,UAAU,IAAI,OAAO,UAAU;AAClE,YAAM,WAAW,IAAI,MAAM;AAE3B,YAAM,UAAU,IAAI,IAAI,UAAU,KAAK,IAAI,IAAI,QAAQ,GAAG,QAAQ,EAAE;AACpE,YAAM,kBAAkB,IAAI;AAAA,QAC1B,UAAU,KAAK,IAAI,IAAI,QAAQ,GAAG,QAAQ;AAAA,MAC5C;AACA,sBAAgB,WAAW,KAAK;AAChC,sBAAgB,WAAW,KAAK;AAEhC,kBAAY,KAAK;AAAA,QACf,MAAM,QAAQ,OAAO,OAAO;AAAA,QAC5B,IAAI;AAAA,QACJ,WAAW;AAAA,QACX,SAAS,OAAO;AAAA,QAChB,SAAS,OAAO;AAAA,QAChB,YAAY,OAAO;AAAA,QACnB,GAAI,OAAO,OAAO,EAAE,MAAM,OAAO,KAAK,IAAI,CAAC;AAAA,QAC3C,KAAK,QAAQ,SAAS;AAAA,QACtB,aAAa,gBAAgB,SAAS;AAAA,QACtC,MAAM;AAAA,QACN,MAAM;AAAA,QACN,GAAI,OAAO,WAAW,EAAE,UAAU,OAAO,SAAS,IAAI,CAAC;AAAA,MACzD,CAAC;AAAA,IACH;AAEA,UAAM,WAAW,CAAC,WAMN;AAEV,YAAM,aAAa,OAAO,YAAY,QAAQ,IAAI;AAClD,YAAM,SAAS,GAAG,OAAO,UAAU;AACnC,YAAM,WAAW,eAAe,OAAO,OAAO,IAAI,OAAO,UAAU;AAEnE,YAAM,UAAU,IAAI,IAAI,UAAU,KAAK,IAAI,IAAI,QAAQ,GAAG,QAAQ,EAAE;AACpE,cAAQ,aAAa,IAAI,WAAW,OAAO,QAAQ,SAAS,CAAC;AAC7D,cAAQ,aAAa,IAAI,UAAU,WAAW,SAAS,CAAC;AAExD,YAAM,kBAAkB,IAAI;AAAA,QAC1B,UAAU,KAAK,IAAI,IAAI,QAAQ,GAAG,QAAQ;AAAA,MAC5C;AACA,sBAAgB,aAAa,IAAI,WAAW,OAAO,QAAQ,SAAS,CAAC;AACrE,sBAAgB,aAAa,IAAI,UAAU,WAAW,SAAS,CAAC;AAChE,sBAAgB,aAAa,IAAI,QAAQ,KAAK,QAAQ;AACtD,sBAAgB,aAAa,IAAI,YAAY,KAAK,QAAQ;AAE1D,kBAAY,KAAK;AAAA,QACf,MAAM,QAAQ,OAAO,OAAO;AAAA,QAC5B,IAAI;AAAA,QACJ,WAAW;AAAA,QACX,SAAS,OAAO;AAAA,QAChB,SAAS,OAAO;AAAA,QAChB,YAAY,OAAO;AAAA,QACnB,GAAI,OAAO,OAAO,EAAE,MAAM,OAAO,KAAK,IAAI,CAAC;AAAA,QAC3C,KAAK,QAAQ,SAAS;AAAA,QACtB,aAAa,gBAAgB,SAAS;AAAA,QACtC,MAAM;AAAA,QACN,MAAM;AAAA,QACN;AAAA,QACA,GAAI,OAAO,WAAW,EAAE,UAAU,OAAO,SAAS,IAAI,CAAC;AAAA,MACzD,CAAC;AAAA,IACH;AAGA,UAAM,UAAU,MAAM,KAAK,WAAW;AACtC,UAAM,cAAc,QAAQ,MAAM,WAAW;AAC7C,UAAM,eACH,2BAA2B,OAAO,CAAC,iBACpC,QAAQ,MAAM,WAAW;AAC3B,UAAM,WAAW,QAAQ,MAAM,QAAQ;AACvC,UAAM,WAAW,QAAQ,MAAM,QAAQ;AAGvC,UAAM,iBAAiB,MAAM,KAAK,kBAAkB,EAAE;AACtD,UAAM,UAAU,gBAAgB,WAAW,CAAC;AAK5C,UAAM,0BACJ,YAAY,gBAAgB,CAAC,SAAS,OAAO;AAK/C,QAAI,cAAgC,CAAC;AACrC,QAAI,gBAAgB,CAAC,SAAS,OAAO,GAAG;AACtC,UAAI;AACF,cAAM,eAAe,MAAM,KAAK,kBAAkB,CAAC;AACnD,sBAAc,cAAc,WAAW,CAAC;AAAA,MAC1C,QAAQ;AACN,sBAAc,CAAC;AAAA,MACjB;AAAA,IACF;AAEA,UAAM,aAAa,CAAC,WAQR;AACV,YAAM,YAAY,IAAI;AAAA,QACpB,cAAc,KAAK,IAAI,YAAY,OAAO,OAAO,YAAY,OAAO,OAAO;AAAA,MAC7E;AACA,YAAM,oBAAoB,IAAI;AAAA,QAC5B,cAAc,KAAK,IAAI,YAAY,OAAO,OAAO,YAAY,OAAO,OAAO;AAAA,MAC7E;AACA,UAAI,OAAO,eAAe;AACxB,kBAAU,aAAa,IAAI,WAAW,OAAO,aAAa;AAC1D,0BAAkB,aAAa,IAAI,WAAW,OAAO,aAAa;AAAA,MACpE;AACA,wBAAkB,WAAW,KAAK;AAClC,wBAAkB,WAAW,KAAK;AAElC,oBAAc,KAAK;AAAA;AAAA,QAEjB,MAAM,UAAU,OAAO,OAAO;AAAA,QAC9B,IAAI,OAAO;AAAA,QACX,WAAW;AAAA,QACX,SAAS,OAAO;AAAA,QAChB,SAAS,OAAO;AAAA,QAChB,YAAY,OAAO;AAAA,QACnB,MAAM,OAAO;AAAA,QACb,GAAI,OAAO,gBACP,EAAE,eAAe,OAAO,cAAc,IACtC,CAAC;AAAA,QACL,KAAK,UAAU,SAAS;AAAA,QACxB,aAAa,kBAAkB,SAAS;AAAA,QACxC,GAAI,OAAO,WAAW,EAAE,UAAU,OAAO,SAAS,IAAI,CAAC;AAAA,MACzD,CAAC;AAAA,IACH;AAEA,UAAM,uBAAuB,CAAC,WAQlB;AACV,iBAAW,YAAY,OAAO,WAAW;AACvC,cAAM,UAAU,SAAS;AAGzB,YAAI,gBAAgB,YAAY,MAAO;AAEvC,YAAI,OAAO,eAAe,YAAY,OAAO;AAC3C,gBAAM,aAAa,YAAY,SAAS,SAAS;AACjD,mBAAS;AAAA,YACP,SAAS,OAAO;AAAA,YAChB;AAAA,YACA;AAAA,YACA;AAAA,YACA,MAAM,OAAO;AAAA,UACf,CAAC;AAAA,QACH;AAEA,YAAI,OAAO,aAAa;AACtB,gBAAM,aACJ,YAAY,SAAS,SAAS,YAAY,QAAQ,QAAQ;AAC5D,mBAAS;AAAA,YACP,SAAS,OAAO;AAAA,YAChB;AAAA,YACA;AAAA,YACA;AAAA,YACA,MAAM,OAAO;AAAA,UACf,CAAC;AAAA,QACH;AAEA,YAAI,OAAO,eAAe;AACxB,cAAI,gBAAgB,YAAY,UAAU,YAAY,MAAO;AAC7D,qBAAW;AAAA,YACT,SAAS,OAAO;AAAA,YAChB;AAAA,YACA;AAAA,YACA,MAAM,OAAO;AAAA;AAAA,YAEb,IAAI,WAAW,OAAO,OAAO,IAAI,OAAO;AAAA,YACxC,YAAY;AAAA,UACd,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF;AAEA,QAAI,UAAU;AAGZ,YAAM,qBACJ,eAAe,EAAE,gBAAgB,SAAS;AAE5C,2BAAqB;AAAA,QACnB,MAAM;AAAA,QACN,SAAS;AAAA,QACT,WAAW;AAAA,QACX,aAAa;AAAA,QACb,aAAa;AAAA,QACb,eAAe;AAAA,QACf,gBAAgB;AAAA,MAClB,CAAC;AAGD,UAAI,eAAe,gBAAgB,SAAS,YAAY;AACtD,cAAM,UAAU,QAAQ,KAAK,CAAC,MAAM,EAAE,YAAY,KAAK,KAAK,QAAQ,CAAC;AAErE,iBAAS;AAAA,UACP,SAAS;AAAA,UACT,SAAS;AAAA,UACT,YAAY;AAAA,UACZ,GAAI,UAAU,EAAE,UAAU,QAAQ,IAAI,CAAC;AAAA,UACvC,MAAM;AAAA,QACR,CAAC;AAAA,MACH;AAAA,IACF;AAGA,QAAI,yBAAyB;AAC3B,2BAAqB;AAAA,QACnB,MAAM;AAAA,QACN,SAAS;AAAA,QACT,WAAW;AAAA,QACX,aAAa;AAAA,QACb,aAAa;AAAA,QACb,eAAe;AAAA,QACf,gBAAgB;AAAA,MAClB,CAAC;AAAA,IACH;AAIA,QACE,CAAC,2BACD,YACA,gBACA,YAAY,SAAS,GACrB;AACA,2BAAqB;AAAA,QACnB,MAAM;AAAA,QACN,SAAS;AAAA,QACT,WAAW;AAAA,QACX,aAAa;AAAA,QACb,aAAa;AAAA,QACb,eAAe;AAAA;AAAA,QAEf,gBAAgB;AAAA,MAClB,CAAC;AAAA,IACH;AAIA,QAAI,YAAY,gBAAgB,SAAS,aAAa;AAGpD,YAAM;AAAA;AAAA;AAAA,QAGJ,aACI,cACA,gBAAgB,cACd,cACA;AAAA;AACR,YAAM,WAAW,QAAQ,KAAK,CAAC,MAAM,EAAE,YAAY,MAAM,KAAK,QAAQ,CAAC;AACvE,YAAM,UAAU,QAAQ,KAAK,CAAC,MAAM,EAAE,YAAY,KAAK,KAAK,QAAQ,CAAC;AAErE,UAAI,UAAU;AACZ,iBAAS;AAAA,UACP,SAAS;AAAA,UACT,SAAS;AAAA,UACT,YAAY;AAAA,UACZ,UAAU;AAAA,UACV,MAAM;AAAA,UACN,uBAAuB;AAAA,QACzB,CAAC;AAAA,MACH,OAAO;AACL,iBAAS;AAAA,UACP,SAAS;AAAA,UACT,SAAS;AAAA,UACT,YAAY;AAAA,UACZ,MAAM;AAAA,UACN,uBAAuB;AAAA,QACzB,CAAC;AAAA,MACH;AAAA,IAIF;AAEA,QAAI,YAAY,gBAAgB,SAAS,eAAe,YAAY;AAKlE,YAAM,UAAU,QAAQ,KAAK,CAAC,MAAM,EAAE,YAAY,KAAK,KAAK,QAAQ,CAAC;AAKrE,YAAM,eACJ,gBAAgB,cAAc,kBAAkB;AAClD,eAAS;AAAA,QACP,SAAS;AAAA,QACT,SAAS;AAAA,QACT,YAAY;AAAA,QACZ,GAAI,UAAU,EAAE,UAAU,QAAQ,IAAI,CAAC;AAAA,QACvC,MAAM;AAAA,MACR,CAAC;AAAA,IACH;AAIA,QAAI,YAAY,gBAAgB,OAAO;AACrC,YAAM,WAAW,QAAQ,KAAK,CAAC,MAAM,EAAE,YAAY,MAAM,KAAK,QAAQ,CAAC;AACvE,YAAM,UAAU,QAAQ,KAAK,CAAC,MAAM,EAAE,YAAY,KAAK,KAAK,QAAQ,CAAC;AAErE,YAAM,mBAEF,CAAC,gBAAgB,cAAc,cAAc,WAAW;AAE5D,iBAAW,iBAAiB,kBAAkB;AAC5C,mBAAW;AAAA,UACT,SAAS;AAAA,UACT,SAAS;AAAA,UACT,GAAI,WAAW,EAAE,UAAU,SAAS,IAAI,CAAC;AAAA,UACzC,MAAM;AAAA;AAAA,UAEN,IAAI,WAAW,EAAE,IAAI,aAAa;AAAA,UAClC,YAAY;AAAA,UACZ;AAAA,QACF,CAAC;AAED,mBAAW;AAAA,UACT,SAAS;AAAA,UACT,SAAS;AAAA,UACT,GAAI,UAAU,EAAE,UAAU,QAAQ,IAAI,CAAC;AAAA,UACvC,MAAM;AAAA;AAAA,UAEN,IAAI,WAAW,EAAE,IAAI,aAAa;AAAA,UAClC,YAAY;AAAA,UACZ;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF;AAEA,UAAM,SAAS;AAAA,MACb;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAEA,WAAO,gBAAgB,MAAM;AAAA,EAC/B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAM,mBACJ,SACA,QACkC;AAClC,UAAM,EAAE,mBAAmB,IAAI,MAAM,OAAO,gCAA8B;AAC1E,WAAO,MAAM,mBAAmB;AAAA,MAC9B,KAAK;AAAA,MACL,SAAS,KAAK,iBAAiB,OAAO;AAAA,MACtC,GAAI,WAAW,SAAY,EAAE,OAAO,IAAI,CAAC;AAAA,IAC3C,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAM,6BACJ,QACkC;AAClC,UAAM,EAAE,6BAA6B,IACnC,MAAM,OAAO,gCAA8B;AAC7C,WAAO,MAAM,6BAA6B;AAAA,MACxC,KAAK;AAAA,MACL;AAAA,IACF,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,MAAM,qBACJ,SACmD;AACnD,UAAM,KAAK,OAAO,MAAM;AACxB,WAAO,MAAM,KAAK,OAAO,qBAAqB,OAAO;AAAA,EACvD;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,0BACJ,SACwD;AACxD,UAAM,KAAK,OAAO,MAAM;AACxB,WAAO,MAAM,KAAK,OAAO,0BAA0B,OAAO;AAAA,EAC5D;AAAA;AAAA;AAAA;AAAA,EAMQ,8BAA8B,SAAqC;AACzE,UAAM,OAAO,KAAK,mCAAmC,OAAO;AAC5D,UAAM,MAAM,OAAO,MAAM,QAAQ,WAAW,KAAK,IAAI,KAAK,IAAI;AAC9D,WAAO,MAAM,MAAM;AAAA,EACrB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAoBA,MAAM,sBACJ,SACA,WAQA,SAQA,aAAqB,QACrB,SAIiB;AACjB,UAAM,KAAK,OAAO,MAAM;AACxB,WAAO,MAAM,KAAK,OAAO;AAAA,MACvB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,MAAM,UACJ,mBACA,SACA,SACiB;AACjB,UAAM,KAAK,OAAO,MAAM;AACxB,WAAO,MAAM,KAAK,OAAO,UAAU,mBAAmB,SAAS,OAAO;AAAA,EACxE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAM,YACJ,UACA,SAMiB;AACjB,UAAM,KAAK,OAAO,MAAM;AACxB,WAAO,MAAM,KAAK,OAAO,YAAY,UAAU,OAAO;AAAA,EACxD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAeA,MAAM,iBACJ,QAC0B;AAC1B,UAAM,KAAK,OAAO,MAAM;AACxB,WAAO,MAAM,KAAK,OAAO,cAAc,MAAM;AAAA,EAC/C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAM,6BAA6B,QAWf;AAClB,UAAM,KAAK,OAAO,MAAM;AACxB,WAAO,MAAM,KAAK,OAAO,0BAA0B,MAAM;AAAA,EAC3D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAM,sBAAsB,SAES;AACnC,UAAM,cAAc,MAAM,sBAAsB;AAAA,MAC9C,KAAK,KAAK;AAAA,MACV,QAAQ,QAAQ;AAAA,IAClB,CAAC;AACD,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAMQ,0BACN,OACA,KACA,iBACM;AACN,UAAM,MAAM,KAAK,IAAI;AACrB,UAAM,oBAAoB,KAAK,OAAO,uBAAuB,KAAK;AAClE,UAAM,oBACJ,mBAAmB,MACf,oBACA,KAAK,IAAI,GAAG,kBAAkB,CAAC;AAErC,UAAM,uBAAuB,CAC3B,cACuB;AACvB,UAAI,aAAa,KAAM,QAAO;AAG9B,UAAI,cAAc,oBAAoB,EAAG,QAAO;AAChD,aAAO;AAAA,IACT;AAEA,UAAM,WAAW,CAAC,YAAoD;AACpE,YAAM,WAAW,KAAK,kBAAkB,IAAI,OAAO;AACnD,UAAI,SAAU,QAAO;AACrB,YAAM,UAA0C,EAAE,QAAQ;AAC1D,WAAK,kBAAkB,IAAI,SAAS,OAAO;AAC3C,aAAO;AAAA,IACT;AAEA,QAAI,UAAU,4BAA4B;AACxC,YAAM,QAAQ,uBAAuB,GAAG;AACxC,YAAM,UACJ,qBAAqB,MAAM,SAAS,KAAK;AAC3C,eAAS,OAAO,EAAE,aAAa,EAAE,aAAa,KAAK,MAAM;AACzD;AAAA,IACF;AAEA,QAAI,UAAU,uBAAuB;AACnC,YAAM,QAAQ,mBAAmB,GAAG;AACpC,YAAM,UACJ,qBAAqB,MAAM,SAAS,KAAK;AAC3C,eAAS,OAAO,EAAE,SAAS,EAAE,aAAa,KAAK,MAAM;AACrD;AAAA,IACF;AAEA,QAAI,UAAU,yBAAyB;AACrC,YAAM,QAAQ,oBAAoB,GAAG;AACrC,eAAS,iBAAiB,EAAE,UAAU;AAAA,QACpC,aAAa;AAAA,QACb;AAAA,MACF;AACA;AAAA,IACF;AAEA,QAAI,UAAU,8BAA8B;AAC1C,YAAM,QAAQ,yBAAyB,GAAG;AAC1C,YAAM,UAAU,qBAAqB,MAAM,OAAO,KAAK;AACvD,eAAS,OAAO,EAAE,eAAe,EAAE,aAAa,KAAK,MAAM;AAC3D;AAAA,IACF;AAEA,QAAI,UAAU,6BAA6B;AACzC,YAAM,QAAQ,wBAAwB,GAAG;AACzC,eAAS,iBAAiB,EAAE,cAAc;AAAA,QACxC,aAAa;AAAA,QACb;AAAA,MACF;AACA;AAAA,IACF;AAEA,QAAI,UAAU,sCAAsC;AAClD,YAAM,QAAQ,gCAAgC,GAAG;AACjD,eAAS,iBAAiB,EAAE,sBAAsB;AAAA,QAChD,aAAa;AAAA,QACb;AAAA,MACF;AACA;AAAA,IACF;AAAA,EACF;AAAA;AAAA,EAGA,+BAA4E;AAC1E,UAAM,MAAM,oBAAI,IAA4C;AAC5D,eAAW,CAAC,SAAS,KAAK,KAAK,KAAK,kBAAkB,QAAQ,GAAG;AAC/D,UAAI,IAAI,SAAS;AAAA,QACf,SAAS,MAAM;AAAA,QACf,GAAI,MAAM,aACN;AAAA,UACE,YAAY;AAAA,YACV,GAAG,MAAM;AAAA,YACT,OAAO,EAAE,GAAG,MAAM,WAAW,MAAM;AAAA,UACrC;AAAA,QACF,IACA,CAAC;AAAA,QACL,GAAI,MAAM,SACN,EAAE,QAAQ,EAAE,GAAG,MAAM,QAAQ,OAAO,EAAE,GAAG,MAAM,OAAO,MAAM,EAAE,EAAE,IAChE,CAAC;AAAA,QACL,GAAI,MAAM,UACN,EAAE,SAAS,EAAE,GAAG,MAAM,SAAS,OAAO,EAAE,GAAG,MAAM,QAAQ,MAAM,EAAE,EAAE,IACnE,CAAC;AAAA,QACL,GAAI,MAAM,eACN;AAAA,UACE,cAAc;AAAA,YACZ,GAAG,MAAM;AAAA,YACT,OAAO,EAAE,GAAG,MAAM,aAAa,MAAM;AAAA,UACvC;AAAA,QACF,IACA,CAAC;AAAA,QACL,GAAI,MAAM,cACN;AAAA,UACE,aAAa;AAAA,YACX,GAAG,MAAM;AAAA,YACT,OAAO,EAAE,GAAG,MAAM,YAAY,MAAM;AAAA,UACtC;AAAA,QACF,IACA,CAAC;AAAA,QACL,GAAI,MAAM,sBACN;AAAA,UACE,qBAAqB;AAAA,YACnB,GAAG,MAAM;AAAA,YACT,OAAO,EAAE,GAAG,MAAM,oBAAoB,MAAM;AAAA,UAC9C;AAAA,QACF,IACA,CAAC;AAAA,MACP,CAAC;AAAA,IACH;AACA,WAAO;AAAA,EACT;AAAA,EAEA,2BACE,UAAU,GAC8C;AACxD,WAAO,KAAK,kBAAkB,IAAI,OAAO,GAAG;AAAA,EAC9C;AAAA,EACA,uBACE,UAAU,GAC0C;AACpD,WAAO,KAAK,kBAAkB,IAAI,OAAO,GAAG;AAAA,EAC9C;AAAA,EACA,wBACE,UAAU,GAC2C;AACrD,WAAO,KAAK,kBAAkB,IAAI,OAAO,GAAG;AAAA,EAC9C;AAAA,EACA,6BACE,UAAU,IACgD;AAC1D,WAAO,KAAK,kBAAkB,IAAI,OAAO,GAAG;AAAA,EAC9C;AAAA,EACA,4BACE,UAAU,GAC+C;AACzD,WAAO,KAAK,kBAAkB,IAAI,OAAO,GAAG;AAAA,EAC9C;AAAA,EAEA,oCACE,UAAU,GACuD;AACjE,WAAO,KAAK,kBAAkB,IAAI,OAAO,GAAG;AAAA,EAC9C;AAAA;AAAA;AAAA;AAAA,EAMQ,kBAA2B;AACjC,UAAM,oBAAoB,KAAK,OAAO,uBAAuB,KAAK;AAElE,QAAI,oBAAoB,EAAG,QAAO;AAElC,WAAO,KAAK,gBAAgB,OAAO;AAAA,EACrC;AAAA,EAEA,MAAc,8BAA8B,QAIxB;AAClB,UAAM,KACJ,OAAO,WAAW,OACd,KAAK,iBAAiB,OAAO,OAAO,IACpC;AAEN,UAAM,QAAQ,KAAK,gBAAgB;AAEnC,QAAI,MAAM,QAAQ,CAAC,OAAO;AACxB,aAAO,MAAM,KAAK,QAAQ;AAAA,QACxB,OAAO,OAAO;AAAA,QACd,GAAI,MAAM,OAAO,EAAE,SAAS,GAAG,IAAI,CAAC;AAAA,QACpC,GAAI,OAAO,aAAa,OAAO,EAAE,WAAW,OAAO,UAAU,IAAI,CAAC;AAAA,MACpE,CAAC;AAAA,IACH;AAEA,UAAM,aAGD;AAAA,MACH;AAAA,QACE,OAAO;AAAA,QACP,GAAG,EAAE,OAAO,OAAO,OAAO,SAAS,GAAG;AAAA,MACxC;AAAA,MACA;AAAA;AAAA,QAEE,OAAO;AAAA,QACP,GAAG,EAAE,OAAO,OAAO,OAAO,SAAS,IAAI,mBAAmB,IAAI;AAAA,MAChE;AAAA,MACA;AAAA,QACE,OAAO;AAAA,QACP,GAAG;AAAA,UACD,OAAO,OAAO;AAAA,UACd,SAAS;AAAA,UACT,mBAAmB;AAAA,UACnB,cAAc,yBAAyB,KAAK,CAAC;AAAA,QAC/C;AAAA,MACF;AAAA,MACA;AAAA,QACE,OAAO;AAAA,QACP,GAAG;AAAA,UACD,OAAO,OAAO;AAAA,UACd,mBAAmB;AAAA,UACnB,cAAc,yBAAyB,EAAE;AAAA,QAC3C;AAAA,MACF;AAAA,MACA;AAAA,QACE,OAAO;AAAA,QACP,GAAG;AAAA,UACD,OAAO,OAAO;AAAA,UACd,mBAAmB;AAAA,UACnB,cAAc,yBAAyB,KAAK,CAAC;AAAA,QAC/C;AAAA,MACF;AAAA,IACF;AAEA,QAAI;AACJ,eAAW,KAAK,YAAY;AAC1B,UAAI;AACF,cAAM,MAAM,MAAM,KAAK,QAAQ;AAAA,UAC7B,GAAG,EAAE;AAAA,UACL,GAAI,OAAO,aAAa,OAAO,EAAE,WAAW,OAAO,UAAU,IAAI,CAAC;AAAA,QACpE,CAAC;AACD,YAAI,QAAQ,GAAI,QAAO;AAAA,MAEzB,SAAS,GAAG;AACV,kBAAU;AAAA,MACZ;AAAA,IACF;AAEA,UAAM,mBAAmB,QACrB,UACA,IAAI;AAAA,MACF,8CAA8C,OAAO,KAAK,KAAK,OAAO,OAAO,CAAC;AAAA,IAChF;AAAA,EACN;AAAA,EAEA,MAAM,eACJ,SACA,SACuC;AACvC,UAAM,SAAS,MAAM,KAAK,8BAA8B;AAAA,MACtD,OAAO;AAAA,MACP;AAAA,MACA,GAAI,SAAS,aAAa,OAAO,EAAE,WAAW,QAAQ,UAAU,IAAI,CAAC;AAAA,IACvE,CAAC;AAED,UAAM,WAAW,aAAa,QAAQ,aAAa,EAAE,CAAC;AACtD,UAAM,YAAY,aAAa,QAAQ,gBAAgB,EAAE,CAAC;AAE1D,UAAM,cAA+C,YAChD,MAAM;AACL,YAAM,YAAY,YAAY,WAAW,UAAU,WAAW,CAAC;AAC/D,YAAM,SAAS,eAAe,WAAW,UAAU,QAAQ,CAAC;AAC5D,YAAM,WAAW,YAAY,WAAW,UAAU,UAAU,CAAC;AAC7D,YAAM,WAAW,YAAY,WAAW,UAAU,UAAU,CAAC;AAC7D,YAAM,QAAQ,YAAY,WAAW,UAAU,OAAO,CAAC;AACvD,YAAM,SAAS,YAAY,WAAW,UAAU,QAAQ,CAAC;AACzD,YAAM,WAAW,WAAW,UAAU,UAAU,GAAG,KAAK;AAExD,aAAO;AAAA,QACL,GAAI,aAAa,OAAO,EAAE,UAAU,IAAI,CAAC;AAAA,QACzC,GAAI,UAAU,OAAO,EAAE,OAAO,IAAI,CAAC;AAAA,QACnC,GAAI,YAAY,OAAO,EAAE,SAAS,IAAI,CAAC;AAAA,QACvC,GAAI,YAAY,OAAO,EAAE,SAAS,IAAI,CAAC;AAAA,QACvC,GAAI,SAAS,OAAO,EAAE,MAAM,IAAI,CAAC;AAAA,QACjC,GAAI,UAAU,OAAO,EAAE,OAAO,IAAI,CAAC;AAAA,QACnC,GAAI,WAAW,EAAE,SAAS,IAAI,CAAC;AAAA,MACjC;AAAA,IACF,GAAG,IACH;AAEJ,UAAM,iBAAqD,aACtD,MAAM;AACL,YAAM,YAAY,YAAY,WAAW,WAAW,WAAW,CAAC;AAChE,YAAM,OAAO,WAAW,WAAW,MAAM,GAAG,KAAK;AACjD,YAAM,SAAS,eAAe,WAAW,WAAW,QAAQ,CAAC;AAC7D,YAAM,WAAW,YAAY,WAAW,WAAW,UAAU,CAAC;AAC9D,YAAM,WAAW,YAAY,WAAW,WAAW,UAAU,CAAC;AAC9D,YAAM,cAAc;AAAA,QAClB,WAAW,WAAW,aAAa;AAAA,MACrC;AACA,YAAM,YAAY,eAAe,WAAW,WAAW,WAAW,CAAC;AAEnE,aAAO;AAAA,QACL,GAAI,aAAa,OAAO,EAAE,UAAU,IAAI,CAAC;AAAA,QACzC,GAAI,OAAO,EAAE,KAAK,IAAI,CAAC;AAAA,QACvB,GAAI,UAAU,OAAO,EAAE,OAAO,IAAI,CAAC;AAAA,QACnC,GAAI,YAAY,OAAO,EAAE,SAAS,IAAI,CAAC;AAAA,QACvC,GAAI,YAAY,OAAO,EAAE,SAAS,IAAI,CAAC;AAAA,QACvC,GAAI,eAAe,OAAO,EAAE,YAAY,IAAI,CAAC;AAAA,QAC7C,GAAI,aAAa,OAAO,EAAE,UAAU,IAAI,CAAC;AAAA,MAC3C;AAAA,IACF,GAAG,IACH;AAEJ,WAAO;AAAA,MACL,GAAI,cAAc,EAAE,YAAY,IAAI,CAAC;AAAA,MACrC,GAAI,iBAAiB,EAAE,eAAe,IAAI,CAAC;AAAA,IAC7C;AAAA,EACF;AAAA,EAEA,MAAM,aACJ,SACA,SAC4B;AAC5B,UAAM,SAAS,MAAM,KAAK,8BAA8B;AAAA,MACtD,OAAO;AAAA,MACP;AAAA,MACA,GAAI,SAAS,aAAa,OAAO,EAAE,WAAW,QAAQ,UAAU,IAAI,CAAC;AAAA,IACvE,CAAC;AAED,UAAM,QAAQ,aAAa,QAAQ,WAAW,EAAE,CAAC;AACjD,UAAM,iBAAiB,QACnB,aAAa,OAAO,WAAW,EAAE,CAAC,IAClC;AACJ,UAAM,YAAY,iBACd,aAAa,gBAAgB,MAAM,EAChC,IAAI,CAAC,MAAM,YAAY,EAAE,KAAK,CAAC,CAAC,EAChC,OAAO,CAAC,MAAmB,KAAK,IAAI,IACvC;AAEJ,UAAM,QAA2B,SAC5B,MAAM;AACL,YAAM,YAAY,YAAY,WAAW,OAAO,WAAW,CAAC;AAC5D,YAAM,QAAQ,YAAY,WAAW,OAAO,OAAO,CAAC;AACpD,YAAM,gBAAgB,YAAY,WAAW,OAAO,eAAe,CAAC;AACpE,YAAM,cAAc,YAAY,WAAW,OAAO,aAAa,CAAC;AAChE,YAAM,kBAAkB;AAAA,QACtB,WAAW,OAAO,iBAAiB;AAAA,MACrC;AACA,YAAM,gBAAgB,YAAY,WAAW,OAAO,eAAe,CAAC;AACpE,YAAM,cAAc,YAAY,WAAW,OAAO,aAAa,CAAC;AAEhE,aAAO;AAAA,QACL,GAAI,aAAa,OAAO,EAAE,UAAU,IAAI,CAAC;AAAA,QACzC,GAAI,SAAS,OAAO,EAAE,MAAM,IAAI,CAAC;AAAA,QACjC,GAAI,iBAAiB,OAAO,EAAE,cAAc,IAAI,CAAC;AAAA,QACjD,GAAI,eAAe,OAAO,EAAE,YAAY,IAAI,CAAC;AAAA,QAC7C,GAAI,mBAAmB,OAAO,EAAE,gBAAgB,IAAI,CAAC;AAAA,QACrD,GAAI,iBAAiB,OAAO,EAAE,cAAc,IAAI,CAAC;AAAA,QACjD,GAAI,eAAe,OAAO,EAAE,YAAY,IAAI,CAAC;AAAA,QAC7C,GAAI,aAAa,UAAU,SAAS,EAAE,UAAU,IAAI,CAAC;AAAA,MACvD;AAAA,IACF,GAAG,IACH,CAAC;AAEL,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,kBACJ,SACA,SACiC;AACjC,UAAM,SAAS,MAAM,KAAK,8BAA8B;AAAA,MACtD,OAAO;AAAA,MACP;AAAA,MACA,GAAI,SAAS,aAAa,OAAO,EAAE,WAAW,QAAQ,UAAU,IAAI,CAAC;AAAA,IACvE,CAAC;AAED,UAAM,QAAQ,aAAa,QAAQ,QAAQ,EAAE,CAAC;AAC9C,UAAM,YAAY,QACd,aAAa,OAAO,kBAAkB,EAAE,CAAC,IACzC;AACJ,UAAM,QAAQ,YACV,aAAa,WAAW,MAAM,EAC3B,IAAI,CAAC,MAAM;AACV,YAAM,QAAQ,WAAW,GAAG,MAAM,KAAK,IAAI,KAAK;AAChD,YAAM,cAAc,WAAW,GAAG,YAAY,KAAK,IAAI,KAAK;AAC5D,UAAI,CAAC,QAAQ,CAAC,WAAY,QAAO;AACjC,aAAO,EAAE,MAAM,WAAW;AAAA,IAC5B,CAAC,EACA,OAAO,CAAC,MAAiD,KAAK,IAAI,IACrE;AAEJ,UAAM,QAAgC,SACjC,MAAM;AACL,YAAM,YAAY,YAAY,WAAW,OAAO,WAAW,CAAC;AAC5D,YAAM,SAAS,eAAe,WAAW,OAAO,QAAQ,CAAC;AACzD,aAAO;AAAA,QACL,GAAI,aAAa,OAAO,EAAE,UAAU,IAAI,CAAC;AAAA,QACzC,GAAI,UAAU,OAAO,EAAE,OAAO,IAAI,CAAC;AAAA,QACnC,GAAI,SAAS,MAAM,SAAS,EAAE,kBAAkB,MAAM,IAAI,CAAC;AAAA,MAC7D;AAAA,IACF,GAAG,IACH,CAAC;AAEL,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,cACJ,SACA,SAC6B;AAC7B,UAAM,SAAS,MAAM,KAAK,8BAA8B;AAAA,MACtD,OAAO;AAAA,MACP;AAAA,MACA,GAAI,SAAS,aAAa,OAAO,EAAE,WAAW,QAAQ,UAAU,IAAI,CAAC;AAAA,IACvE,CAAC;AACD,UAAM,SAAS,YAAY,WAAW,QAAQ,QAAQ,CAAC;AACvD,WAAO;AAAA,MACL,GAAI,UAAU,OAAO,EAAE,OAAO,IAAI,CAAC;AAAA,IACrC;AAAA,EACF;AAAA,EAEA,MAAM,QACJ,SACA,SACuB;AACvB,UAAM,SAAS,MAAM,KAAK,8BAA8B;AAAA,MACtD,OAAO;AAAA,MACP;AAAA,MACA,GAAI,SAAS,aAAa,OAAO,EAAE,WAAW,QAAQ,UAAU,IAAI,CAAC;AAAA,IACvE,CAAC;AACD,UAAM,WAAW,YAAY,WAAW,QAAQ,UAAU,CAAC;AAC3D,UAAM,OAAO,WAAW,QAAQ,MAAM,GAAG,KAAK;AAC9C,UAAM,OAAO,WAAW,QAAQ,MAAM,GAAG,KAAK;AAC9C,UAAM,MAAM,WAAW,QAAQ,KAAK,GAAG,KAAK;AAC5C,UAAM,cAAc,YAAY,WAAW,QAAQ,SAAS,CAAC;AAC7D,UAAM,YAAY,YAAY,WAAW,QAAQ,WAAW,CAAC;AAE7D,WAAO;AAAA,MACL,GAAI,YAAY,OAAO,EAAE,SAAS,IAAI,CAAC;AAAA,MACvC,GAAI,OAAO,EAAE,KAAK,IAAI,CAAC;AAAA,MACvB,GAAI,OAAO,EAAE,KAAK,IAAI,CAAC;AAAA,MACvB,GAAI,MAAM,EAAE,IAAI,IAAI,CAAC;AAAA,MACrB,GAAI,eAAe,OAAO,EAAE,SAAS,YAAY,IAAI,CAAC;AAAA,MACtD,GAAI,aAAa,OAAO,EAAE,UAAU,IAAI,CAAC;AAAA,IAC3C;AAAA,EACF;AAAA,EAEA,MAAM,kBACJ,SACA,SACiC;AACjC,UAAM,SAAS,MAAM,KAAK,8BAA8B;AAAA,MACtD,OAAO;AAAA,MACP;AAAA,MACA,GAAI,SAAS,aAAa,OAAO,EAAE,WAAW,QAAQ,UAAU,IAAI,CAAC;AAAA,IACvE,CAAC;AAED,UAAM,YAAY,aAAa,QAAQ,gBAAgB,EAAE,CAAC,KAAK;AAC/D,UAAM,eAAe,aAAa,WAAW,YAAY;AAEzD,UAAM,eAAe,CAAC,MAAgD;AACpE,YAAM,OAAO,KAAK,IAAI,KAAK;AAC3B,UAAI,CAAC,IAAK,QAAO;AACjB,YAAM,OAAO,IACV,MAAM,GAAG,EACT,IAAI,CAAC,MAAM,OAAO,EAAE,KAAK,CAAC,CAAC,EAC3B,OAAO,CAAC,MAAM,OAAO,SAAS,CAAC,CAAC;AACnC,aAAO,KAAK,SAAS,OAAO;AAAA,IAC9B;AAEA,UAAM,UAAU,aAAa,IAAI,CAAC,OAAO;AACvC,YAAM,cAAc,YAAY,WAAW,IAAI,aAAa,CAAC;AAC7D,YAAM,YAAY,aAAa,IAAI,aAAa;AAChD,YAAM,eAAe,UAAU,IAAI,CAAC,OAAO;AACzC,cAAM,MAAM,aAAa,IAAI,YAAY,EAAE,CAAC;AAC5C,cAAM,QAAQ,MAAM,YAAY,WAAW,KAAK,OAAO,CAAC,IAAI;AAC5D,cAAM,SAAS,MAAM,YAAY,WAAW,KAAK,QAAQ,CAAC,IAAI;AAE9D,cAAM,mBAAmB,aAAa,IAAI,kBAAkB,EAAE,CAAC;AAC/D,cAAM,mBAAmB,mBACrB,aAAa,kBAAkB,cAAc,EAC1C,IAAI,CAAC,MAAM,YAAY,EAAE,KAAK,CAAC,CAAC,EAChC,OAAO,CAAC,MAAmB,KAAK,IAAI,IACvC;AAEJ,cAAM,KAAK,aAAa,WAAW,IAAI,gBAAgB,CAAC;AACxD,cAAM,KAAK,aAAa,WAAW,IAAI,cAAc,CAAC;AAEtD,cAAM,OAAO,WAAW,IAAI,MAAM,GAAG,KAAK;AAC1C,cAAM,eAAe,YAAY,WAAW,IAAI,cAAc,CAAC;AAC/D,cAAM,mBAAmB;AAAA,UACvB,WAAW,IAAI,kBAAkB;AAAA,QACnC;AACA,cAAM,iBAAiB,YAAY,WAAW,IAAI,gBAAgB,CAAC;AACnE,cAAM,aAAa,YAAY,WAAW,IAAI,YAAY,CAAC;AAE3D,eAAO;AAAA,UACL,GAAI,OAAO,EAAE,KAAK,IAAI,CAAC;AAAA,UACvB,GAAI,SAAS,OAAO,EAAE,MAAM,IAAI,CAAC;AAAA,UACjC,GAAI,UAAU,OAAO,EAAE,OAAO,IAAI,CAAC;AAAA,UACnC,GAAI,gBAAgB,OAAO,EAAE,aAAa,IAAI,CAAC;AAAA,UAC/C,GAAI,oBAAoB,iBAAiB,SACrC,EAAE,iBAAiB,IACnB,CAAC;AAAA,UACL,GAAI,oBAAoB,OAAO,EAAE,iBAAiB,IAAI,CAAC;AAAA,UACvD,GAAI,kBAAkB,OAAO,EAAE,eAAe,IAAI,CAAC;AAAA,UACnD,GAAI,KAAK,EAAE,gBAAgB,GAAG,IAAI,CAAC;AAAA,UACnC,GAAI,KAAK,EAAE,cAAc,GAAG,IAAI,CAAC;AAAA,UACjC,GAAI,cAAc,OAAO,EAAE,WAAW,IAAI,CAAC;AAAA,QAC7C;AAAA,MACF,CAAC;AAED,aAAO;AAAA,QACL,GAAI,eAAe,OAAO,EAAE,YAAY,IAAI,CAAC;AAAA,QAC7C;AAAA,MACF;AAAA,IACF,CAAC;AAED,WAAO,EAAE,QAAQ;AAAA,EACnB;AAAA,EAEA,MAAM,YACJ,SACA,SAC2B;AAC3B,UAAM,SAAS,MAAM,KAAK,8BAA8B;AAAA,MACtD,OAAO;AAAA,MACP;AAAA,MACA,GAAI,SAAS,aAAa,OAAO,EAAE,WAAW,QAAQ,UAAU,IAAI,CAAC;AAAA,IACvE,CAAC;AACD,UAAM,QAAQ,aAAa,QAAQ,UAAU,EAAE,CAAC;AAChD,UAAM,QAA0B,SAC3B,MAAM;AACL,YAAM,YAAY,YAAY,WAAW,OAAO,WAAW,CAAC;AAC5D,YAAM,aAAa,YAAY,WAAW,OAAO,YAAY,CAAC;AAC9D,YAAM,QAAQ,WAAW,OAAO,OAAO,GAAG,KAAK;AAC/C,YAAM,aAAa,WAAW,OAAO,YAAY,GAAG,KAAK;AACzD,aAAO;AAAA,QACL,GAAI,aAAa,OAAO,EAAE,UAAU,IAAI,CAAC;AAAA,QACzC,GAAI,cAAc,OAAO,EAAE,WAAW,IAAI,CAAC;AAAA,QAC3C,GAAI,QAAQ,EAAE,MAAM,IAAI,CAAC;AAAA,QACzB,GAAI,aAAa,EAAE,WAAW,IAAI,CAAC;AAAA,MACrC;AAAA,IACF,GAAG,IACH,CAAC;AACL,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,cACJ,SACA,SAC6B;AAC7B,UAAM,SAAS,MAAM,KAAK,8BAA8B;AAAA,MACtD,OAAO;AAAA,MACP;AAAA,MACA,GAAI,SAAS,aAAa,OAAO,EAAE,WAAW,QAAQ,UAAU,IAAI,CAAC;AAAA,IACvE,CAAC;AACD,UAAM,QAAQ,aAAa,QAAQ,YAAY,EAAE,CAAC;AAClD,UAAM,QAA4B,SAC7B,MAAM;AACL,YAAME,SAAQ,YAAY,WAAW,OAAO,OAAO,CAAC;AACpD,YAAM,OAAO,YAAY,WAAW,OAAO,MAAM,CAAC;AAClD,YAAM,SAAS,YAAY,WAAW,OAAO,QAAQ,CAAC;AACtD,YAAM,UAAU,YAAY,WAAW,OAAO,SAAS,CAAC;AACxD,YAAM,YAAY,WAAW,OAAO,WAAW,GAAG,KAAK;AACvD,aAAO;AAAA,QACL,GAAIA,UAAS,OAAO,EAAE,OAAAA,OAAM,IAAI,CAAC;AAAA,QACjC,GAAI,QAAQ,OAAO,EAAE,KAAK,IAAI,CAAC;AAAA,QAC/B,GAAI,UAAU,OAAO,EAAE,OAAO,IAAI,CAAC;AAAA,QACnC,GAAI,WAAW,OAAO,EAAE,QAAQ,IAAI,CAAC;AAAA,QACrC,GAAI,YAAY,EAAE,UAAU,IAAI,CAAC;AAAA,MACnC;AAAA,IACF,GAAG,IACH,CAAC;AACL,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,MAAM,kBACJ,SACA,SACuB;AACvB,UAAM,MAAM,MAAM,KAAK,8BAA8B;AAAA,MACnD,OAAO;AAAA,MACP,GAAI,WAAW,OAAO,EAAE,QAAQ,IAAI,CAAC;AAAA,MACrC,GAAI,SAAS,aAAa,OAAO,EAAE,WAAW,QAAQ,UAAU,IAAI,CAAC;AAAA,IACvE,CAAC;AACD,WAAO,uBAAuB,GAAG;AAAA,EACnC;AAAA,EAEA,MAAM,WACJ,SACA,SACwB;AACxB,UAAM,MAAM,MAAM,KAAK,8BAA8B;AAAA,MACnD,OAAO;AAAA,MACP,GAAI,WAAW,OAAO,EAAE,QAAQ,IAAI,CAAC;AAAA,MACrC,GAAI,SAAS,aAAa,OAAO,EAAE,WAAW,QAAQ,UAAU,IAAI,CAAC;AAAA,IACvE,CAAC;AACD,WAAO,uBAAsC,GAAG;AAAA,EAClD;AAAA,EAEA,MAAM,eAAe,SAEU;AAC7B,UAAM,MAAM,MAAM,KAAK,QAAQ;AAAA,MAC7B,OAAO;AAAA,MACP,GAAI,SAAS,aAAa,OAAO,EAAE,WAAW,QAAQ,UAAU,IAAI,CAAC;AAAA,IACvE,CAAC;AACD,WAAO,uBAA0C,GAAG;AAAA,EACtD;AAAA,EAEA,MAAM,cACJ,SACA,SACuB;AACvB,UAAM,MAAM,MAAM,KAAK,8BAA8B;AAAA,MACnD,OAAO;AAAA,MACP,GAAI,WAAW,OAAO,EAAE,QAAQ,IAAI,CAAC;AAAA,MACrC,GAAI,SAAS,aAAa,OAAO,EAAE,WAAW,QAAQ,UAAU,IAAI,CAAC;AAAA,IACvE,CAAC;AACD,WAAO,uBAAuB,GAAG;AAAA,EACnC;AAAA,EAEA,MAAM,aACJ,SACA,SAC0B;AAC1B,UAAM,MAAM,MAAM,KAAK,8BAA8B;AAAA,MACnD,OAAO;AAAA,MACP,GAAI,WAAW,OAAO,EAAE,QAAQ,IAAI,CAAC;AAAA,MACrC,GAAI,SAAS,aAAa,OAAO,EAAE,WAAW,QAAQ,UAAU,IAAI,CAAC;AAAA,IACvE,CAAC;AACD,WAAO,uBAAwC,GAAG;AAAA,EACpD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAiBA,MAAM,iBACJ,SACA,SAC0B;AAC1B,UAAM,MAAM,MAAM,KAAK,8BAA8B;AAAA,MACnD,OAAO;AAAA,MACP,GAAI,WAAW,OAAO,EAAE,QAAQ,IAAI,CAAC;AAAA,MACrC,GAAI,SAAS,aAAa,OAAO,EAAE,WAAW,QAAQ,UAAU,IAAI,CAAC;AAAA,IACvE,CAAC;AACD,WAAO,uBAAwC,GAAG;AAAA,EACpD;AAAA,EAEA,MAAM,YACJ,SACA,SACyB;AACzB,UAAM,MAAM,MAAM,KAAK,8BAA8B;AAAA,MACnD,OAAO;AAAA,MACP,GAAI,WAAW,OAAO,EAAE,QAAQ,IAAI,CAAC;AAAA,MACrC,GAAI,SAAS,aAAa,OAAO,EAAE,WAAW,QAAQ,UAAU,IAAI,CAAC;AAAA,IACvE,CAAC;AACD,WAAO,uBAAuC,GAAG;AAAA,EACnD;AAAA,EAEA,MAAM,qBACJ,SACA,SACkC;AAClC,UAAM,MAAM,MAAM,KAAK,8BAA8B;AAAA,MACnD,OAAO;AAAA,MACP,GAAI,WAAW,OAAO,EAAE,QAAQ,IAAI,CAAC;AAAA,MACrC,GAAI,SAAS,aAAa,OAAO,EAAE,WAAW,QAAQ,UAAU,IAAI,CAAC;AAAA,IACvE,CAAC;AACD,WAAO,uBAAgD,GAAG;AAAA,EAC5D;AAAA,EAEA,MAAM,gBACJ,SACA,SAC6B;AAC7B,UAAM,MAAM,MAAM,KAAK,8BAA8B;AAAA,MACnD,OAAO;AAAA,MACP,GAAI,WAAW,OAAO,EAAE,QAAQ,IAAI,CAAC;AAAA,MACrC,GAAI,SAAS,aAAa,OAAO,EAAE,WAAW,QAAQ,UAAU,IAAI,CAAC;AAAA,IACvE,CAAC;AACD,WAAO,uBAA2C,GAAG;AAAA,EACvD;AAAA,EAEA,MAAM,aACJ,SACA,SAC0B;AAC1B,UAAM,MAAM,MAAM,KAAK,8BAA8B;AAAA,MACnD,OAAO;AAAA,MACP,GAAI,WAAW,OAAO,EAAE,QAAQ,IAAI,CAAC;AAAA,MACrC,GAAI,SAAS,aAAa,OAAO,EAAE,WAAW,QAAQ,UAAU,IAAI,CAAC;AAAA,IACvE,CAAC;AACD,WAAO,uBAAwC,GAAG;AAAA,EACpD;AAAA,EAEA,MAAM,YAAY,SAAyD;AACzE,UAAM,MAAM,MAAM,KAAK,QAAQ;AAAA,MAC7B,OAAO;AAAA,MACP,GAAI,SAAS,aAAa,OAAO,EAAE,WAAW,QAAQ,UAAU,IAAI,CAAC;AAAA,IACvE,CAAC;AACD,WAAO,uBAAuB,GAAG;AAAA,EACnC;AAAA,EAEA,MAAM,aACJ,SACA,SACuB;AACvB,UAAM,MAAM,MAAM,KAAK,8BAA8B;AAAA,MACnD,OAAO;AAAA,MACP,GAAI,WAAW,OAAO,EAAE,QAAQ,IAAI,CAAC;AAAA,MACrC,GAAI,SAAS,aAAa,OAAO,EAAE,WAAW,QAAQ,UAAU,IAAI,CAAC;AAAA,IACvE,CAAC;AACD,WAAO,uBAAqC,GAAG;AAAA,EACjD;AAAA,EAEA,MAAM,kBAAkB,SAEU;AAChC,UAAM,MAAM,MAAM,KAAK,QAAQ;AAAA,MAC7B,OAAO;AAAA,MACP,GAAI,SAAS,aAAa,OAAO,EAAE,WAAW,QAAQ,UAAU,IAAI,CAAC;AAAA,IACvE,CAAC;AACD,WAAO,uBAA6C,GAAG;AAAA,EACzD;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,kBAAkB,SAEU;AAChC,UAAM,MAAM,MAAM,KAAK,QAAQ;AAAA,MAC7B,OAAO;AAAA,MACP,GAAI,SAAS,aAAa,OAAO,EAAE,WAAW,QAAQ,UAAU,IAAI,CAAC;AAAA,IACvE,CAAC;AACD,WAAO,uBAA6C,GAAG;AAAA,EACzD;AAAA;AAAA;AAAA,EAIA,MAAM,UACJ,SACA,SACuB;AACvB,UAAM,MAAM,MAAM,KAAK,8BAA8B;AAAA,MACnD,OAAO;AAAA,MACP,GAAI,WAAW,OAAO,EAAE,QAAQ,IAAI,CAAC;AAAA,MACrC,GAAI,SAAS,aAAa,OAAO,EAAE,WAAW,QAAQ,UAAU,IAAI,CAAC;AAAA,IACvE,CAAC;AACD,WAAO,uBAAuB,GAAG;AAAA,EACnC;AAAA,EAEA,MAAM,UACJ,SACA,SACuB;AACvB,UAAM,MAAM,MAAM,KAAK,8BAA8B;AAAA,MACnD,OAAO;AAAA,MACP,GAAI,WAAW,OAAO,EAAE,QAAQ,IAAI,CAAC;AAAA,MACrC,GAAI,SAAS,aAAa,OAAO,EAAE,WAAW,QAAQ,UAAU,IAAI,CAAC;AAAA,IACvE,CAAC;AACD,WAAO,uBAAuB,GAAG;AAAA,EACnC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,MAAM,cACJ,SACA,SAC2B;AAC3B,UAAM,MAAM,MAAM,KAAK,8BAA8B;AAAA,MACnD,OAAO;AAAA,MACP,GAAI,WAAW,OAAO,EAAE,QAAQ,IAAI,CAAC;AAAA,MACrC,GAAI,SAAS,aAAa,OAAO,EAAE,WAAW,QAAQ,UAAU,IAAI,CAAC;AAAA,IACvE,CAAC;AACD,WAAO,uBAAyC,GAAG;AAAA,EACrD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,MAAM,iBAAiB,SAEU;AAC/B,UAAM,MAAM,MAAM,KAAK,8BAA8B;AAAA,MACnD,OAAO;AAAA,MACP,GAAI,SAAS,aAAa,OAAO,EAAE,WAAW,QAAQ,UAAU,IAAI,CAAC;AAAA,IACvE,CAAC;AACD,WAAO,uBAA4C,GAAG;AAAA,EACxD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,MAAM,WAAW,SAA0D;AACzE,UAAM,MAAM,MAAM,KAAK,8BAA8B;AAAA,MACnD,OAAO;AAAA,MACP,GAAI,SAAS,aAAa,OAAO,EAAE,WAAW,QAAQ,UAAU,IAAI,CAAC;AAAA,IACvE,CAAC;AACD,WAAO,uBAAsC,GAAG;AAAA,EAClD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,MAAM,SACJ,SACA,SACmB;AACnB,UAAM,MAAM,MAAM,KAAK,8BAA8B;AAAA,MACnD,OAAO;AAAA,MACP,GAAI,WAAW,OAAO,EAAE,QAAQ,IAAI,CAAC;AAAA,MACrC,GAAI,SAAS,aAAa,OAAO,EAAE,WAAW,QAAQ,UAAU,IAAI,CAAC;AAAA,IACvE,CAAC;AACD,WAAO,uBAAiC,GAAG;AAAA,EAC7C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAkBA,MAAM,gBACJ,SACA,SAQC;AACD,UAAM,QAAQ,MAAM,KAAK,SAAS,SAAS,OAAO;AAClD,UAAM,MAAO,OAAe,MAAM,SAAU,OAAe,SAAS;AACpE,UAAM,aAAa,OAAO,KAAK,cAAc,CAAC;AAC9C,WAAO;AAAA,MACL,SAAS,eAAe;AAAA,MACxB;AAAA,MACA,gBACE,KAAK,kBAAkB,OAAO,OAAO,IAAI,cAAc,IAAI;AAAA,MAC7D,gBAAgB,KAAK,kBAAkB;AAAA,MACvC,YAAY,KAAK,cAAc;AAAA,MAC/B,KAAK;AAAA,IACP;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAkBA,MAAM,gBACJ,SACA,SACA,SACuB;AACvB,UAAM,KAAK,WAAW;AACtB,UAAM,aAAa,UAAU,IAAI;AAGjC,UAAM,aAAa,MAAM,KAAK,SAAS,IAAI,OAAO;AAClD,UAAM,MACH,YAAoB,MAAM,SAAU,YAAoB,SAAS,CAAC;AAGrE,UAAM,aAAa;AAAA;AAAA;AAAA,aAGV,EAAE;AAAA,cACD,UAAU;AAAA,EACtB,IAAI,kBAAkB,OAAO,mBAAmB,IAAI,cAAc,sBAAsB,EAAE;AAAA,EAC1F,IAAI,aAAa,eAAe,IAAI,UAAU,kBAAkB,EAAE;AAAA,EAClE,IAAI,iBAAiB,mBAAmB,IAAI,cAAc,sBAAsB,EAAE;AAAA;AAAA;AAIhF,UAAM,MAAM,MAAM,KAAK,QAAQ;AAAA,MAC7B,OAAO;AAAA,MACP,SAAS;AAAA,MACT;AAAA,MACA,GAAI,SAAS,aAAa,OAAO,EAAE,WAAW,QAAQ,UAAU,IAAI,CAAC;AAAA,IACvE,CAAC;AACD,WAAO,uBAAuB,GAAG;AAAA,EACnC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAkBA,MAAM,wBACJ,SACA,UAKA,SACuB;AACvB,UAAM,KAAK,WAAW;AAGtB,UAAM,aAAa,MAAM,KAAK,SAAS,IAAI,OAAO;AAClD,UAAM,MACH,YAAoB,MAAM,SAAU,YAAoB,SAAS,CAAC;AAGrE,UAAM,iBAAiB,SAAS,kBAAkB,IAAI;AACtD,UAAM,YACJ,SAAS,6BAA6B,IAAI,6BAA6B;AACzE,UAAM,iBACJ,SAAS,kCACT,IAAI,kCACJ;AAGF,UAAM,aAAa;AAAA;AAAA;AAAA,aAGV,EAAE;AAAA,cACD,IAAI,cAAc,CAAC;AAAA,EAC/B,IAAI,kBAAkB,OAAO,mBAAmB,IAAI,cAAc,sBAAsB,EAAE;AAAA,EAC1F,IAAI,aAAa,eAAe,IAAI,UAAU,kBAAkB,EAAE;AAAA,EAClE,iBAAiB,mBAAmB,cAAc,sBAAsB,EAAE;AAAA,6BAC/C,SAAS;AAAA,kCACJ,cAAc;AAAA;AAAA;AAI5C,UAAM,MAAM,MAAM,KAAK,QAAQ;AAAA,MAC7B,OAAO;AAAA,MACP,SAAS;AAAA,MACT;AAAA,MACA,GAAI,SAAS,aAAa,OAAO,EAAE,WAAW,QAAQ,UAAU,IAAI,CAAC;AAAA,IACvE,CAAC;AACD,WAAO,uBAAuB,GAAG;AAAA,EACnC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,eAAe,SAEU;AAC7B,UAAM,MAAM,MAAM,KAAK,8BAA8B;AAAA,MACnD,OAAO;AAAA,MACP,GAAI,SAAS,aAAa,OAAO,EAAE,WAAW,QAAQ,UAAU,IAAI,CAAC;AAAA,IACvE,CAAC;AACD,WAAO,uBAA0C,GAAG;AAAA,EACtD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAqBA,MAAM,iBACJ,WAOA,SACA,SACuB;AACvB,UAAM,KAAK,WAAW;AACtB,UAAM,eAAe,UAAU,oBAAoB;AAAA,MACjD,EAAE,MAAM,MAAM,YAAY,IAAI,OAAO,GAAG,EAAE;AAAA,MAC1C,EAAE,MAAM,UAAU,YAAY,IAAI,OAAO,GAAG,EAAE;AAAA,MAC9C,EAAE,MAAM,WAAW,YAAY,IAAI,OAAO,GAAG,EAAE;AAAA,IACjD;AACA,UAAM,gBAAgB,aACnB;AAAA,MACC,CAAC,SACC,eAAe,KAAK,IAAI,sBAAsB,KAAK,UAAU;AAAA,IACjE,EACC,KAAK,EAAE;AAEV,UAAM,aAAa;AAAA;AAAA;AAAA,aAGV,EAAE;AAAA,UACL,UAAU,MAAM;AAAA;AAAA,EAExB,aAAa;AAAA;AAAA;AAAA;AAKX,UAAM,MAAM,MAAM,KAAK,QAAQ;AAAA,MAC7B,OAAO;AAAA,MACP,SAAS;AAAA,MACT;AAAA,MACA,GAAI,SAAS,aAAa,OAAO,EAAE,WAAW,QAAQ,UAAU,IAAI,CAAC;AAAA,IACvE,CAAC;AACD,WAAO,uBAAuB,GAAG;AAAA,EACnC;AAAA,EAEA,MAAM,UACJ,SACA,SACuB;AACvB,UAAM,MAAM,MAAM,KAAK,8BAA8B;AAAA,MACnD,OAAO;AAAA,MACP,GAAI,WAAW,OAAO,EAAE,QAAQ,IAAI,CAAC;AAAA,MACrC,GAAI,SAAS,aAAa,OAAO,EAAE,WAAW,QAAQ,UAAU,IAAI,CAAC;AAAA,IACvE,CAAC;AACD,WAAO,uBAAuB,GAAG;AAAA,EACnC;AAAA,EAEA,MAAM,UACJ,SACA,SACuB;AACvB,UAAM,MAAM,MAAM,KAAK,8BAA8B;AAAA,MACnD,OAAO;AAAA,MACP,GAAI,WAAW,OAAO,EAAE,QAAQ,IAAI,CAAC;AAAA,MACrC,GAAI,SAAS,aAAa,OAAO,EAAE,WAAW,QAAQ,UAAU,IAAI,CAAC;AAAA,IACvE,CAAC;AACD,WAAO,uBAAuB,GAAG;AAAA,EACnC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,6BAA6B,QAUhC;AACD,UAAM,WAAW,MAAM,KAAK,sBAAsB,MAAM;AACxD,UAAM,MAAM,OAAO,SAAS,YAAY,EAAE,EAAE,YAAY;AACxD,UAAM,QAAQ,IAAI,SAAS,KAAK,KAAK,IAAI,SAAS,MAAM,IAAI,SAAS;AACrE,UAAM,IAAI,OAAO,eAAe;AAEhC,UAAM,OAAO;AAAA,MACX;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,OAAO,CAAC;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAEA,UAAM,KAAKL,OAAM,UAAU,MAAM,EAAE,OAAO,CAAC,QAAQ,QAAQ,MAAM,EAAE,CAAC;AACpE,UAAM,SAAmB,CAAC;AAC1B,QAAI,SAAS;AACb,OAAG,OAAO,GAAG,QAAQ,CAAC,MAAM,OAAO,KAAK,OAAO,KAAK,CAAC,CAAC,CAAC;AACvD,OAAG,OAAO,GAAG,QAAQ,CAAC,MAAO,UAAU,OAAO,CAAC,CAAE;AAEjD,OAAG,MAAM,MAAM,SAAS,KAAK;AAC7B,OAAG,MAAM,IAAI;AAEb,UAAM,WAAmB,MAAM,IAAI,QAAQ,CAAC,SAAS,WAAW;AAC9D,SAAG,GAAG,SAAS,MAAM;AACrB,SAAG,GAAG,SAAS,CAAC,SAAS,QAAQ,QAAQ,CAAC,CAAC;AAAA,IAC7C,CAAC;AAED,QAAI,aAAa,GAAG;AAClB,YAAM,IAAI;AAAA,QACR,+DAA+D,QAAQ,MAAM,MAAM;AAAA,MACrF;AAAA,IACF;AAEA,UAAM,OAAO,OAAO,OAAO,MAAM;AACjC,QAAI,KAAK,SAAS,IAAI;AACpB,YAAM,IAAI;AAAA,QACR,yCAAyC,KAAK,MAAM;AAAA,MACtD;AAAA,IACF;AAEA,WAAO,EAAE,MAAM,SAAS;AAAA,EAC1B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,MAAM,+BAA+B,QA6BlC;AACD,UAAM,SAAS,OAAO,UAAU,KAAK;AAGrC,UAAM,SAAS,uBAAuB,OAAO,QAAQ;AACrD,UAAM,aAAa,QAAQ,cAAc;AAEzC,UAAM,UAAU,KAAK,KAAM,aAAa,MAAQ,GAAG;AAEnD,YAAQ;AAAA,MACN,sDAAsD,OAAO,OAAO,cAAc,OAAO,QAAQ,gBAAgB,UAAU,gBAAgB,OAAO,cAAc,OAAO,YAAY,MAAM;AAAA,IAC3L;AAEA,UAAM,cAEC;AAAA,MACL,SAAS,OAAO;AAAA,MAChB,UAAU,OAAO;AAAA,MACjB;AAAA,MACA,GAAI,OAAO,SAAS,OAAO,EAAE,OAAO,OAAO,MAAM,IAAI,CAAC;AAAA,MACtD,GAAI,OAAO,YAAY,OAAO,EAAE,UAAU,OAAO,SAAS,IAAI,CAAC;AAAA,IACjE;AAGA,UAAM,EAAE,QAAQ,cAAc,SAAS,iBAAiB,IACtD,MAAM,KAAK;AAAA,MAAgC,MACzC,KAAK,2BAA2B,WAAW;AAAA,IAC7C;AAEF,UAAM,EAAE,QAAQ,MAAM,WAAW,IAAI;AAErC,UAAM,QAAQ,IAAI,YAAY;AAC9B,UAAM,SAAS,IAAI,YAAY;AAI/B,QAAI,UAA8B;AAElC,QAAI,KAAsC;AAC1C,QAAI,QAAQ;AACZ,QAAI,aAAa;AAEjB,UAAM,cAAc,CAAC,cAA+B;AAClD,UAAI,GAAI;AAGR,YAAM,iBACJ,cAAc,UAAU,OAAO,wBAAwB;AAEzD,cAAQ;AAAA,QACN,mEAAmE,SAAS,eAAe,cAAc;AAAA,MAC3G;AAGA,kBAAY,cAAc;AAC1B,gBAAU,IAAI,YAAY,EAAE,UAAU,CAAC;AAIvC,YAAM,OAAO;AAAA,QACX;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA;AAAA,QAEA,GAAI,iBACA,CAAC,QAAQ,WAAW,WAAW,aAAa,QAAQ,IAAI,IACxD,CAAC,MAAM,MAAM;AAAA,QACjB;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAEA,WAAKA,OAAM,UAAU,MAAM,EAAE,OAAO,CAAC,QAAQ,QAAQ,MAAM,EAAE,CAAC;AAC9D,UAAI,CAAC,GAAG,SAAS,CAAC,GAAG,UAAU,CAAC,GAAG,QAAQ;AACzC,cAAM,IAAI,MAAM,oCAAoC;AAAA,MACtD;AACA,YAAM,KAAK,GAAG,KAAK;AACnB,SAAG,OAAO,KAAK,MAAM;AAGrB,SAAG,MAAM,GAAG,SAAS,MAAM;AAAA,MAAC,CAAC;AAC7B,SAAG,OAAO,GAAG,SAAS,MAAM;AAAA,MAAC,CAAC;AAC9B,YAAM,GAAG,SAAS,MAAM;AAAA,MAAC,CAAC;AAC1B,aAAO,GAAG,SAAS,MAAM;AAAA,MAAC,CAAC;AAE3B,UAAI,SAAS;AACb,SAAG,OAAO,GAAG,QAAQ,CAAC,MAAO,UAAU,OAAO,CAAC,CAAE;AACjD,SAAG,GAAG,SAAS,CAAC,SAAS;AACvB,YAAI,MAAO;AACX,gBAAQ;AAER,aAAK,QAAQ,OAAO,KAAK,OAAO,KAAK,GAAG;AACtC,kBAAQ;AAAA,YACN,4DAA4D,IAAI,KAAK,MAAM;AAAA,UAC7E;AACA,iBAAO;AAAA,YACL,IAAI,MAAM,2BAA2B,QAAQ,CAAC,KAAK,MAAM,EAAE;AAAA,UAC7D;AAAA,QACF,OAAO;AACL,kBAAQ;AAAA,YACN,mEAAmE,UAAU;AAAA,UAC/E;AACA,iBAAO,IAAI;AAAA,QACb;AAAA,MACF,CAAC;AAAA,IACH;AAEA,UAAM,UAAU,YAA2B;AACzC,UAAI,MAAO;AACX,cAAQ;AACR,cAAQ;AAAA,QACN,4DAA4D,UAAU;AAAA,MACxE;AACA,UAAI;AACF,cAAM,WAAW;AAAA,MACnB,QAAQ;AAAA,MAER;AACA,UAAI;AACF,cAAM,OAAO,KAAK;AAAA,MACpB,QAAQ;AAAA,MAER;AACA,UAAI;AACF,cAAM,IAAI;AAAA,MACZ,QAAQ;AAAA,MAER;AACA,UAAI;AACF,YAAI,KAAK,SAAS;AAAA,MACpB,QAAQ;AAAA,MAER;AACA,UAAI;AACF,eAAO,IAAI;AAAA,MACb,QAAQ;AAAA,MAER;AAEA,uBAAiB;AAAA,IACnB;AAEA,UAAM,QAAQ;AAAA,MACZ,MAAM;AACJ,gBAAQ;AAAA,UACN,qDAAqD,OAAO;AAAA,QAC9D;AACA,aAAK,QAAQ;AAAA,MACf;AAAA,MACA,KAAK,IAAI,GAAG,OAAO,IAAI;AAAA,IACzB;AAEA,WAAO,GAAG,SAAS,MAAM;AACvB,mBAAa,KAAK;AAClB,WAAK,QAAQ;AAAA,IACf,CAAC;AAED,WAAO,GAAG,SAAS,CAAC,MAAM;AACxB,cAAQ;AAAA,QACN,kDAAkD,EAAE,OAAO;AAAA,MAC7D;AACA,aAAO,QAAQ,CAAC;AAChB,WAAK,QAAQ;AAAA,IACf,CAAC;AAED,WAAO;AAAA,MACL;AAAA,MACA,CAAC,EAAE,MAAM,WAAW,YAAY,aAAa,MAAM;AACjD,YAAI,MAAO;AACX,oBAAY,SAAS;AACrB;AAEA,YAAI,SAAS;AACX,gBAAM,SAAS,QAAQ,IAAI,MAAM,cAAc,UAAU;AACzD,gBAAM,MAAM,MAAM;AAAA,QACpB;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,MACL,KAAK;AAAA,MACL,MAAM;AAAA,IACR;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA8BA,MAAM,iCAAiC,QAUpC;AACD,UAAM,YAAY,OAAO,aAAa;AAGtC,UAAM,SAAS,uBAAuB,OAAO,QAAQ;AACrD,UAAM,MACJ,QAAQ,aAAa,OAAO,YAAY,IAAI,OAAO,YAAY;AAGjE,UAAM,UAAU,KAAK,iBAAiB,OAAO,OAAO;AACpD,UAAM,MAAM,MAAM,KAAK,uBAAuB,OAAO;AAGrD,UAAM,EAAE,QAAQ,UAAU,IAAI,MAAM,KAAK,yBAAyB;AAAA,MAChE;AAAA,MACA;AAAA,MACA,UAAU,OAAO;AAAA,MACjB;AAAA,IACF,CAAC;AAED,QAAI,OAAO,WAAW,GAAG;AACvB,YAAM,IAAI,MAAM,+BAA+B;AAAA,IACjD;AAEA,UAAM,QAAQ,IAAI,YAAY;AAC9B,UAAM,SAAS,IAAI,YAAY;AAE/B,QAAI,KAAsC;AAC1C,QAAI,QAAQ;AAEZ,UAAM,UAAU,YAA2B;AACzC,UAAI,MAAO;AACX,cAAQ;AACR,UAAI;AACF,cAAM,IAAI;AAAA,MACZ,QAAQ;AAAA,MAER;AACA,UAAI;AACF,YAAI,KAAK,SAAS;AAAA,MACpB,QAAQ;AAAA,MAER;AACA,UAAI;AACF,eAAO,IAAI;AAAA,MACb,QAAQ;AAAA,MAER;AAAA,IACF;AAGA,UAAM,QAAQ,cAAc,SAAS,SAAS;AAC9C,UAAM,OAAO;AAAA,MACX;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,OAAO,GAAG;AAAA,MACV;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAEA,SAAKA,OAAM,UAAU,MAAM,EAAE,OAAO,CAAC,QAAQ,QAAQ,MAAM,EAAE,CAAC;AAC9D,QAAI,CAAC,GAAG,SAAS,CAAC,GAAG,UAAU,CAAC,GAAG,QAAQ;AACzC,YAAM,IAAI,MAAM,oCAAoC;AAAA,IACtD;AAEA,UAAM,KAAK,GAAG,KAAK;AACnB,OAAG,OAAO,KAAK,MAAM;AAErB,QAAI,SAAS;AACb,OAAG,OAAO,GAAG,QAAQ,CAAC,MAAO,UAAU,OAAO,CAAC,CAAE;AACjD,OAAG,GAAG,SAAS,CAAC,SAAS;AACvB,UAAI,MAAO;AACX,cAAQ;AACR,WAAK,QAAQ,OAAO,KAAK,OAAO,KAAK,GAAG;AACtC,eAAO;AAAA,UACL,IAAI,MAAM,2BAA2B,QAAQ,CAAC,KAAK,MAAM,EAAE;AAAA,QAC7D;AAAA,MACF,OAAO;AACL,eAAO,IAAI;AAAA,MACb;AAAA,IACF,CAAC;AAGD,UAAM,WAAW,OAAO,KAAK,CAAC,GAAM,GAAM,GAAM,GAAM,GAAM,GAAI,CAAC;AAIjE,QAAI,cAAc,QAAQ;AACxB,YAAM,MAAM,QAAQ;AAAA,IACtB;AACA,UAAM,MAAM,MAAM;AAClB,UAAM,IAAI;AAEV,WAAO;AAAA,MACL,KAAK;AAAA,MACL,MAAM;AAAA,IACR;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgCA,MAAM,yBAAyB,QAWF;AAC3B,UAAM,UAAU;AAChB,UAAM,aACJ,OAAO,eAAe,eAAe,eAAe;AACtD,UAAM,YAAY,OAAO,aAAa;AAEtC,WAAO,MAAM,KAAK,cAAc;AAAA,MAC9B;AAAA,MACA,OAAO,OAAO;AAAA,MACd,KAAK,OAAO;AAAA,MACZ;AAAA,MACA;AAAA,IACF,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA8BA,MAAM,4BAA4B,QAc/B;AACD,UAAM,UAAU;AAChB,UAAM,aAAa,OAAO,cAAc;AACxC,UAAM,YAAY,OAAO,aAAa;AAEtC,UAAM,KAAK,OAAO,MAAM;AAMxB,WAAO,MAAM,KAAK,qCAAqC;AAAA,MACrD;AAAA,MACA,UAAU,OAAO;AAAA,MACjB;AAAA,MACA;AAAA,IACF,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA6BA,MAAM,4BAA4B,QAU/B;AACD,UAAM,UAAU;AAChB,UAAM,YAAY,OAAO,aAAa;AAEtC,WAAO,MAAM,KAAK,iCAAiC;AAAA,MACjD;AAAA,MACA,UAAU,OAAO;AAAA,MACjB;AAAA,IACF,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAyBA,MAAM,uBAAuB,QAOS;AACpC,UAAM,UAAU;AAChB,UAAM,WAAW,OAAO,YAAY;AACpC,UAAM,YAAY,OAAO,aAAa;AAEtC,WAAO,MAAM,KAAK,sBAAsB;AAAA,MACtC;AAAA,MACA,MAAM,OAAO;AAAA,MACb;AAAA,MACA;AAAA,IACF,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA0BA,MAAM,0BAA0B,QAM7B;AAED,SAAK,OAAO,GAAG,SAAS,OAAO,OAAO;AAGtC,UAAM,KAAK,gBAAgB;AAE3B,WAAO;AAAA,MACL,aAAa,YAAY;AACvB,cAAM,KAAK,kBAAkB;AAC7B,aAAK,OAAO,IAAI,SAAS,OAAO,OAAO;AAAA,MACzC;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAyBA,MAAM,0BAA0B,QAQ7B;AACD,UAAM,UAAU;AAChB,UAAM,UAAU,QAAQ,WAAW;AAGnC,UAAM,KAAK,iBAAiB,SAAS,OAAO;AAE5C,WAAO;AAAA,MACL,MAAM,YAAY;AAChB,cAAM,KAAK,gBAAgB,SAAS,OAAO;AAAA,MAC7C;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgBA,MAAM,wBAAyC;AAC7C,UAAM,UAAU;AAChB,WAAO,MAAM,KAAK,YAAY,OAAO;AAAA,EACvC;AACF;;;A4Bx8XA,OAAOM,YAAW;AAClB,SAAS,qBAAAC,0BAAyB;AAkClC,eAAsB,qBAAqB,MAAc,SAAwD;AAC/G,MAAI,CAAC,QAAQ,mBAAoB,QAAO,CAAC;AAEzC,QAAM,SAAS,QAAQ;AACvB,QAAM,YAAY,QAAQ,yBAAyB;AACnD,QAAM,aAAiC,CAAC;AAExC,QAAM,aAAa,MAAM,KAAK;AAC9B,MAAI,CAAC,WAAY,QAAO,CAAC;AAEzB,UAAQ,MAAM,gDAAgD,UAAU,aAAa,8BAA8B,QAAQ,8BAA8B,KAAK;AAE9J,SAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,UAAM,SAASC,OAAM,aAAa,MAAM;AACxC,QAAI;AAEJ,WAAO,GAAG,WAAW,CAAC,KAAK,UAAU;AACnC,UAAI;AACF,YAAI,MAAM,YAAY,WAAY;AAClC,cAAM,SAAS,kBAAkB,GAAG;AACpC,YAAI,OAAO,SAAS,YAAa;AAEjC,cAAM,KAAK,WAAW,OAAO,GAAG;AAChC,cAAM,OAAO,KAAK,SAAY,aAAa,OAAO,GAAG;AACrD,YAAI,CAAC,MAAM,CAAC,KAAM;AAElB,cAAM,WAAW,uBAAuB,KAAK,OAAO,GAAG;AACvD,cAAM,aAAa,2BAA2B,KAAK,OAAO,GAAG;AAC7D,cAAM,YAAY,yBAAyB,KAAK,OAAO,GAAG;AAC1D,cAAM,gBAAgB,iCAAiC,KAAK,OAAO,GAAG;AAEtE,cAAM,OAAO,WAAW,CAAC,KAAK,gBAAgB,CAAC,IAAI,KAAK;AACxD,cAAM,QAAQ,aAAa,CAAC,GAAG,KAAK;AACpC,cAAM,OAAO,YAAY,CAAC,GAAG,KAAK;AAElC,cAAM,SAA2B;AAAA,UAC/B,MAAM,MAAM;AAAA,UACZ,iBAAiB;AAAA,UACjB,GAAI,MAAM,EAAE,IAAI,IAAI,CAAC;AAAA,UACrB,GAAI,QAAQ,EAAE,MAAM,IAAI,CAAC;AAAA,UACzB,GAAI,OAAO,EAAE,KAAK,IAAI,CAAC;AAAA,QACzB;AAEA,mBAAW,KAAK,MAAM;AAGtB,YAAI;AACF,iBAAO,MAAM;AAAA,QACf,QAAQ;AAAA,QAER;AAAA,MACF,QAAQ;AAAA,MAER;AAAA,IACF,CAAC;AAED,WAAO,GAAG,SAAS,CAAC,QAAQ;AAC1B,cAAQ,OAAO,wCAAwC,IAAI,OAAO,EAAE;AAAA,IACtE,CAAC;AAED,WAAO,KAAK,MAAM;AAChB,YAAM,YAAY,OAAO,QAAQ,EAAE;AACnC,YAAM,iBAAiB,CAAC,gCAAgC,8BAA8B;AAEtF,iBAAW,QAAQ,gBAAgB;AACjC,YAAI;AACF,gBAAM,MAAM,KAAK,MAAM,KAAK,OAAO,IAAI,GAAI,KAAK;AAChD,gBAAM,MAAM,UAAU,EAAE,YAAY,UAAU,CAAC;AAC/C,gBAAM,SAAS,sBAAsB,KAAK,GAAG;AAC7C,iBAAO,KAAK,QAAQ,MAAM,YAAY,CAAC,QAAQ;AAC7C,gBAAI,KAAK;AACP,sBAAQ,QAAQ,4CAA4C,UAAU,IAAI,IAAI,KAAK,IAAI,OAAO,EAAE;AAAA,YAClG;AAAA,UACF,CAAC;AAAA,QACH,QAAQ;AAAA,QAER;AAAA,MACF;AAEA,gBAAU,WAAW,MAAM;AACzB,YAAI;AACF,iBAAO,MAAM;AAAA,QACf,QAAQ;AAAA,QAER;AAAA,MACF,GAAG,SAAS;AAAA,IACd,CAAC;AAED,WAAO,GAAG,SAAS,MAAM;AACvB,UAAI,QAAS,cAAa,OAAO;AACjC,UAAI,WAAW,WAAW,GAAG;AAC3B,gBAAQ,MAAM,qDAAqD,UAAU,GAAG;AAAA,MAClF,OAAO;AACL,gBAAQ,MAAM,0CAA0C,WAAW,MAAM,aAAa;AAAA,MACxF;AACA,cAAQ,UAAU;AAAA,IACpB,CAAC;AAAA,EACH,CAAC;AACH;AA4BA,SAAS,mBAA6B;AACpC,QAAM,WAAqB,CAAC;AAC5B,QAAM,aAAaC,mBAAkB;AAErC,aAAW,aAAa,OAAO,KAAK,UAAU,GAAG;AAC/C,UAAM,QAAQ,WAAW,SAAS;AAClC,QAAI,CAAC,MAAO;AAEZ,eAAW,QAAQ,OAAO;AAExB,UAAI,KAAK,YAAY,KAAK,WAAW,UAAU,CAAC,KAAK,QAAS;AAG9D,YAAM,UAAU,KAAK,QAAQ,MAAM,GAAG,EAAE,IAAI,MAAM;AAClD,YAAM,YAAY,KAAK,QAAQ,MAAM,GAAG,EAAE,IAAI,MAAM;AAGpD,UAAI,OAAO;AACX,eAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AAC1B,cAAM,YAAY,UAAU,CAAC;AAC7B,YAAI,cAAc,UAAa,CAAC,OAAO,SAAS,SAAS,EAAG;AAC5D,YAAI,cAAc,KAAK;AACrB,kBAAQ;AAAA,QACV,WAAW,cAAc,GAAG;AAC1B;AAAA,QACF,OAAO;AAEL,cAAI,OAAO;AACX,cAAI,IAAY;AAChB,iBAAO,IAAI,GAAG;AACZ,gBAAI,IAAI,EAAG;AACX,gBAAI,KAAK;AAAA,UACX;AACA,kBAAQ;AACR;AAAA,QACF;AAAA,MACF;AAEA,YAAM,cAAc,GAAG,KAAK,QAAQ,MAAM,GAAG,EAAE,MAAM,GAAG,CAAC,EAAE,KAAK,GAAG,CAAC,MAAM,IAAI;AAC9E,UAAI,CAAC,SAAS,SAAS,WAAW,GAAG;AACnC,iBAAS,KAAK,WAAW;AAAA,MAC3B;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAKA,SAAS,UAAU,MAAoE;AACrF,QAAM,QAAQ,KAAK,MAAM,GAAG;AAC5B,QAAM,UAAU,MAAM,CAAC;AACvB,QAAM,YAAY,MAAM,CAAC;AACzB,MAAI,CAAC,QAAS,QAAO;AACrB,QAAM,SAAS,OAAO,SAAS,aAAa,MAAM,EAAE;AACpD,MAAI,CAAC,OAAO,SAAS,MAAM,KAAK,SAAS,KAAK,SAAS,GAAI,QAAO;AAElE,QAAM,UAAU,QAAQ,MAAM,GAAG,EAAE,IAAI,MAAM;AAC7C,MAAI,QAAQ,WAAW,KAAK,QAAQ,KAAK,CAAC,MAAM,CAAC,OAAO,SAAS,CAAC,KAAK,IAAI,KAAK,IAAI,GAAG,EAAG,QAAO;AAEjG,QAAM,cAAc;AACpB,QAAM,WAAW,KAAK;AAGtB,MAAI,cAAc;AAClB,WAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AAC1B,UAAM,OAAO,QAAQ,CAAC;AACtB,QAAI,SAAS,UAAa,CAAC,OAAO,SAAS,IAAI,EAAG,QAAO;AACzD,kBAAe,eAAe,IAAM,OAAO;AAAA,EAC7C;AAGA,QAAM,QAAS,KAAK,eAAe,KAAM;AACzC,iBAAe;AAGf,QAAM,YAAY,KAAK;AACvB,QAAM,QAAQ,UAAU,KAAK,cAAc,IAAI;AAC/C,QAAM,MAAM,UAAU,KAAK,cAAc,YAAY,IAAI,cAAc,YAAY;AAEnF,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,OAAO,MAAM,QAAQ;AAAA,EACvB;AACF;AAKA,SAAS,iBAAiB,IAAoB;AAC5C,SAAO,GAAI,OAAO,KAAM,GAAI,IAAK,OAAO,KAAM,GAAI,IAAK,OAAO,IAAK,GAAI,IAAI,KAAK,GAAI;AACtF;AAKA,eAAe,gBACb,IACA,MACA,SAOkC;AAClC,QAAM,EAAE,UAAU,UAAU,WAAW,QAAQ,SAAS,IAAI;AAE5D,MAAI;AAEF,UAAM,MAAM,IAAI,cAAc;AAAA,MAC5B,MAAM;AAAA,MACN;AAAA,MACA,UAAU,YAAY;AAAA,MACtB,UAAU,YAAY;AAAA,MACtB,UAAU,YAAY;AAAA,MACtB;AAAA,IACF,CAAC;AAGD,QAAI;AACF,YAAM,OAAO,MAAM,IAAI,QAAQ;AAC/B,UAAI,MAAM,MAAM;AACd,gBAAQ,MAAM,uCAAuC,EAAE,IAAI,IAAI,KAAK,WAAW,UAAU,MAAM,OAAO,KAAK,IAAI,EAAE;AACjH,cAAM,SAA2B;AAAA,UAC/B,MAAM;AAAA,UACN,iBAAiB;AAAA,UACjB,eAAe,YAAY;AAAA,UAC3B,gBAAgB,CAAC;AAAA,QACnB;AACA,YAAI,SAAS,QAAW;AACtB,cAAI,UAAU;AACZ,mBAAO,YAAY;AAAA,UACrB,OAAO;AACL,mBAAO,WAAW;AAAA,UACpB;AAAA,QACF;AACA,YAAI,KAAK,KAAM,QAAO,QAAQ,KAAK,KAAK,KAAK;AAC7C,YAAI,KAAK,KAAM,QAAO,OAAO,KAAK,KAAK,KAAK;AAC5C,YAAI,KAAK,gBAAiB,QAAO,kBAAkB,KAAK,gBAAgB,KAAK;AAC7E,eAAO;AAAA,MACT;AAAA,IACF,QAAQ;AAEN,UAAI,YAAY,UAAU;AACxB,YAAI;AACF,gBAAM,IAAI,MAAM;AAChB,gBAAM,OAAO,MAAM,IAAI,QAAQ;AAC/B,cAAI,MAAM,MAAM;AACd,oBAAQ,MAAM,qDAAqD,EAAE,IAAI,IAAI,KAAK,WAAW,UAAU,MAAM,OAAO,KAAK,IAAI,EAAE;AAC/H,kBAAM,SAA2B;AAAA,cAC/B,MAAM;AAAA,cACN,iBAAiB;AAAA,cACjB,eAAe,YAAY;AAAA,cAC3B,gBAAgB,CAAC;AAAA,YACnB;AACA,gBAAI,SAAS,QAAW;AACtB,kBAAI,UAAU;AACZ,uBAAO,YAAY;AAAA,cACrB,OAAO;AACL,uBAAO,WAAW;AAAA,cACpB;AAAA,YACF;AACA,gBAAI,KAAK,KAAM,QAAO,QAAQ,KAAK,KAAK,KAAK;AAC7C,gBAAI,KAAK,KAAM,QAAO,OAAO,KAAK,KAAK,KAAK;AAC5C,gBAAI,KAAK,gBAAiB,QAAO,kBAAkB,KAAK,gBAAgB,KAAK;AAC7E,mBAAO;AAAA,UACT;AAAA,QACF,QAAQ;AAAA,QAER;AAAA,MACF;AAAA,IACF;AAAA,EAIF,SAAS,KAAK;AAEZ,UAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC3D,QAAI,CAAC,IAAI,SAAS,cAAc,KAAK,CAAC,IAAI,SAAS,WAAW,GAAG;AAC/D,cAAQ,OAAO,6BAA6B,EAAE,IAAI,IAAI,KAAK,GAAG,EAAE;AAAA,IAClE;AAAA,EACF;AAEA,SAAO;AACT;AAKA,eAAsB,oBAAoB,SAAwD;AAChG,MAAI,CAAC,QAAQ,mBAAoB,QAAO,CAAC;AAEzC,QAAM,SAAS,QAAQ;AACvB,QAAM,cAAc,QAAQ,eAAe,iBAAiB,EAAE,CAAC;AAC/D,QAAM,YAAY,QAAQ,aAAa,CAAC,IAAI,GAAG;AAC/C,QAAM,YAAY,QAAQ,sBAAsB;AAChD,QAAM,gBAAgB,QAAQ,uBAAuB;AAErD,MAAI,CAAC,aAAa;AAChB,YAAQ,OAAO,yDAAyD;AACxE,WAAO,CAAC;AAAA,EACV;AAEA,UAAQ,MAAM,6CAA6C,WAAW,KAAK;AAE3E,QAAM,UAAU,UAAU,WAAW;AACrC,MAAI,CAAC,SAAS;AACZ,YAAQ,OAAO,6BAA6B,WAAW,EAAE;AACzD,WAAO,CAAC;AAAA,EACV;AAEA,QAAM,aAAiC,CAAC;AACxC,QAAM,cAAsE,CAAC;AAG7E,WAAS,QAAQ,QAAQ,OAAO,SAAS,QAAQ,OAAO,SAAS,QAAQ,QAAQ,KAAK,SAAS;AAC7F,UAAM,KAAK,iBAAiB,KAAK;AACjC,eAAW,QAAQ,WAAW;AAC5B,kBAAY,KAAK,EAAE,IAAI,MAAM,UAAU,SAAS,IAAI,CAAC;AAAA,IACvD;AAAA,EACF;AAEA,UAAQ,MAAM,wBAAwB,YAAY,MAAM,0BAA0B;AAGlF,WAAS,IAAI,GAAG,IAAI,YAAY,QAAQ,KAAK,eAAe;AAC1D,UAAM,QAAQ,YAAY,MAAM,GAAG,IAAI,aAAa;AACpD,UAAM,eAAe,MAAM,QAAQ;AAAA,MACjC,MAAM,IAAI,CAAC,EAAE,IAAI,MAAM,SAAS,MAAM;AACpC,cAAM,eAMF;AAAA,UACF;AAAA,UACA;AAAA,QACF;AACA,YAAI,QAAQ,aAAa,OAAW,cAAa,WAAW,QAAQ;AACpE,YAAI,QAAQ,aAAa,OAAW,cAAa,WAAW,QAAQ;AACpE,YAAI,WAAW,OAAW,cAAa,SAAS;AAChD,eAAO,gBAAgB,IAAI,MAAM,YAAY;AAAA,MAC/C,CAAC;AAAA,IACH;AAEA,eAAW,UAAU,cAAc;AACjC,UAAI,OAAO,WAAW,eAAe,OAAO,OAAO;AACjD,mBAAW,KAAK,OAAO,KAAK;AAAA,MAC9B;AAAA,IACF;AAAA,EACF;AAEA,UAAQ,MAAM,yCAAyC,WAAW,MAAM,aAAa;AACrF,SAAO;AACT;AAKA,eAAsB,wBAAwB,SAAwD;AACpG,MAAI,CAAC,QAAQ,mBAAoB,QAAO,CAAC;AAEzC,QAAM,SAAS,QAAQ;AACvB,QAAM,YAAY,QAAQ,yBAAyB;AACnD,QAAM,aAAiC,CAAC;AAExC,UAAQ,MAAM,yDAAyD,8BAA8B,QAAQ,8BAA8B,KAAK;AAEhJ,SAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,UAAM,SAASD,OAAM,aAAa,MAAM;AACxC,UAAM,UAAU,oBAAI,IAA8B;AAClD,QAAI;AAEJ,WAAO,GAAG,WAAW,CAAC,KAAK,UAAU;AACnC,UAAI;AACF,cAAM,SAAS,kBAAkB,GAAG;AACpC,YAAI,OAAO,SAAS,aAAa;AAC/B,cAAI;AACF,kBAAM,OAAO,MAAM;AAGnB,kBAAM,KAAK,WAAW,OAAO,GAAG;AAChC,gBAAI,MAAM,CAAC,QAAQ,IAAI,IAAI,GAAG;AAC5B,sBAAQ,MAAM,+CAA+C,IAAI,SAAS,GAAG,GAAG,SAAS,GAAG,GAAG,GAAG;AAClG,oBAAM,SAA2B;AAAA,gBAC/B;AAAA,gBACA,iBAAiB;AAAA,cACnB;AAGA,oBAAM,WAAW,uBAAuB,KAAK,OAAO,GAAG;AACvD,oBAAM,aAAa,2BAA2B,KAAK,OAAO,GAAG;AAC7D,oBAAM,YAAY,yBAAyB,KAAK,OAAO,GAAG;AAC1D,oBAAM,gBAAgB,iCAAiC,KAAK,OAAO,GAAG;AAEtE,oBAAM,MAAM,WAAW,CAAC,KAAK,gBAAgB,CAAC;AAC9C,oBAAM,QAAQ,aAAa,CAAC;AAC5B,oBAAM,OAAO,YAAY,CAAC;AAE1B,kBAAI,MAAO,QAAO,QAAQ,MAAM,KAAK;AACrC,kBAAI,IAAK,QAAO,MAAM,IAAI,KAAK;AAC/B,kBAAI,KAAM,QAAO,OAAO,KAAK,KAAK;AAElC,sBAAQ,IAAI,MAAM,MAAM;AACxB;AAAA,YACF;AAGA,kBAAM,OAAO,aAAa,OAAO,GAAG;AACpC,gBAAI,QAAQ,CAAC,QAAQ,IAAI,IAAI,GAAG;AAE9B,oBAAM,WAAW,uBAAuB,KAAK,OAAO,GAAG;AACvD,oBAAM,aAAa,2BAA2B,KAAK,OAAO,GAAG;AAC7D,oBAAM,YAAY,yBAAyB,KAAK,OAAO,GAAG;AAC1D,oBAAM,gBAAgB,iCAAiC,KAAK,OAAO,GAAG;AAEtE,oBAAM,MAAM,WAAW,CAAC,KAAK,gBAAgB,CAAC;AAC9C,oBAAM,QAAQ,aAAa,CAAC;AAC5B,oBAAM,OAAO,YAAY,CAAC;AAE1B,sBAAQ,MAAM,+CAA+C,IAAI,GAAG,MAAM,UAAU,GAAG,MAAM,EAAE,EAAE;AACjG,oBAAM,SAA2B;AAAA,gBAC/B;AAAA,gBACA,iBAAiB;AAAA,cACnB;AACA,kBAAI,MAAO,QAAO,QAAQ,MAAM,KAAK;AACrC,kBAAI,IAAK,QAAO,MAAM,IAAI,KAAK;AAC/B,kBAAI,KAAM,QAAO,OAAO,KAAK,KAAK;AAClC,sBAAQ,IAAI,MAAM,MAAM;AAAA,YAC1B;AAAA,UACF,SAAS,KAAK;AAEZ,oBAAQ;AAAA,cACN,2DAA2D,MAAM,OAAO,IAAI,MAAM,IAAI,KAAK,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,YAC7I;AAAA,UACF;AAAA,QACF;AAAA,MACF,SAAS,KAAK;AAAA,MAEd;AAAA,IACF,CAAC;AAED,WAAO,GAAG,SAAS,CAAC,QAAQ;AAC1B,cAAQ,OAAO,iCAAiC,IAAI,OAAO,EAAE;AAAA,IAC/D,CAAC;AAED,WAAO,KAAK,MAAM;AAChB,aAAO,aAAa,IAAI;AACxB,YAAM,YAAY,OAAO,QAAQ,EAAE;AAKnC,YAAM,iBAAiB,CAAC,gCAAgC,8BAA8B;AAEtF,iBAAW,QAAQ,gBAAgB;AACjC,YAAI;AAEF,gBAAM,MAAM,KAAK,MAAM,KAAK,OAAO,IAAI,GAAI,KAAK;AAChD,gBAAM,MAAM,UAAU,EAAE,YAAY,UAAU,CAAC;AAC/C,gBAAM,SAAS,sBAAsB,KAAK,GAAG;AAG7C,iBAAO,KAAK,QAAQ,MAAM,mBAAmB,CAAC,QAAQ;AACpD,gBAAI,KAAK;AACP,sBAAQ,OAAO,oDAAoD,IAAI,KAAK,IAAI,OAAO,EAAE;AAAA,YAC3F;AAAA,UACF,CAAC;AAAA,QACH,SAAS,KAAK;AACZ,kBAAQ,OAAO,wDAAwD,IAAI,KAAK,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC,EAAE;AAAA,QACpI;AAAA,MACF;AAGA,gBAAU,WAAW,MAAM;AACzB,eAAO,MAAM;AACb,gBAAQ,MAAM,KAAK,QAAQ,OAAO,CAAC,CAAC;AACpC,gBAAQ,MAAM,6CAA6C,QAAQ,IAAI,aAAa;AAAA,MACtF,GAAG,SAAS;AAAA,IACd,CAAC;AAGD,WAAO,GAAG,SAAS,MAAM;AACvB,UAAI,QAAS,cAAa,OAAO;AAAA,IACnC,CAAC;AAAA,EACH,CAAC;AACH;AAsBA,eAAsB,uBAAuB,UAA4B,CAAC,GAAgC;AACxG,QAAM,SAAS,QAAQ;AACvB,UAAQ,MAAM,kDAAkD;AAEhE,QAAM,UAA8B,CAAC;AACrC,QAAM,cAAc,oBAAI,IAA8B;AAGtD,QAAM,cAAc,CAAC,WAA6B;AAChD,UAAM,MAAM,OAAO;AACnB,UAAM,WAAW,YAAY,IAAI,GAAG;AACpC,QAAI,UAAU;AAEZ,UAAI,CAAC,SAAS,SAAS,OAAO,MAAO,UAAS,QAAQ,OAAO;AAC7D,UAAI,CAAC,SAAS,OAAO,OAAO,IAAK,UAAS,MAAM,OAAO;AACvD,UAAI,CAAC,SAAS,QAAQ,OAAO,KAAM,UAAS,OAAO,OAAO;AAC1D,UAAI,CAAC,SAAS,mBAAmB,OAAO,gBAAiB,UAAS,kBAAkB,OAAO;AAC3F,UAAI,OAAO,YAAY,CAAC,SAAS,SAAU,UAAS,WAAW,OAAO;AACtE,UAAI,OAAO,aAAa,CAAC,SAAS,UAAW,UAAS,YAAY,OAAO;AACzE,UAAI,OAAO,kBAAkB,OAAW,UAAS,gBAAgB,OAAO;AACxE,UAAI,OAAO,mBAAmB,OAAW,UAAS,iBAAiB,OAAO;AAAA,IAC5E,OAAO;AACL,kBAAY,IAAI,KAAK,EAAE,GAAG,OAAO,CAAC;AAClC,cAAQ,KAAK,YAAY,IAAI,GAAG,CAAE;AAAA,IACpC;AAAA,EACF;AAGA,QAAM,CAAC,aAAa,UAAU,IAAI,MAAM,QAAQ,IAAI;AAAA,IAClD,oBAAoB,OAAO;AAAA,IAC3B,wBAAwB,OAAO;AAAA,EACjC,CAAC;AAGD,aAAW,UAAU,aAAa;AAChC,gBAAY,MAAM;AAAA,EACpB;AACA,aAAW,UAAU,YAAY;AAC/B,gBAAY,MAAM;AAAA,EACpB;AAEA,UAAQ,MAAM,yCAAyC,QAAQ,MAAM,oBAAoB;AACzF,SAAO;AACT;;;AC3iBO,SAAS,aAAa,KAAkC;AAC7D,QAAM,IAAI,KAAK,KAAK;AACpB,SAAO,IAAI,IAAI;AACjB;AAKO,SAAS,QAAQ,KAAqB;AAC3C,QAAM,IAAI,IAAI,KAAK;AACnB,MAAI,EAAE,UAAU,EAAG,QAAO;AAC1B,SAAO,GAAG,EAAE,MAAM,GAAG,CAAC,CAAC,SAAI,EAAE,MAAM,EAAE,CAAC;AACxC;AAEA,eAAe,gBAAgB,MAA2C;AACxE,MAAI;AAGF,QAAI,CAAC,SAAS,KAAK,IAAI,EAAG,QAAO;AACjC,UAAM,EAAE,OAAO,IAAI,MAAM,OAAO,cAAmB;AACnD,UAAM,MAAM,MAAM,OAAO,IAAI;AAC7B,WAAO,KAAK,WAAW;AAAA,EACzB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,eAAe,mBAAmB,MAAc,QAA8C;AAC5F,MAAI;AACF,UAAM,KAAK,MAAM,gBAAgB,IAAI;AACrC,UAAM,eAAe,MAAM;AAG3B,QAAI;AACF,YAAM,gBAAgB,MAAM,qBAAqB,cAAc;AAAA,QAC7D,oBAAoB;AAAA,QACpB,uBAAuB;AAAA,QACvB,GAAI,SAAS,EAAE,OAAO,IAAI,CAAC;AAAA,MAC7B,CAAC;AACD,YAAM,cAAc,cAAc,KAAK,CAAC,MAAM,EAAE,SAAS,YAAY;AACrE,YAAM,YAAY,aAAa,aAAa,GAAG;AAC/C,UAAI,WAAW;AACb,gBAAQ,MAAM,+CAA+C,QAAQ,SAAS,CAAC,EAAE;AACjF,eAAO;AAAA,MACT;AAAA,IACF,QAAQ;AAAA,IAER;AAGA,UAAM,UAAU;AAAA,MACd,oBAAoB;AAAA,MACpB,uBAAuB;AAAA,MACvB,GAAI,SAAS,EAAE,OAAO,IAAI,CAAC;AAAA,IAC7B;AACA,UAAM,UAAU,MAAM,wBAAwB,OAAO;AACrD,UAAM,QAAQ,QAAQ,KAAK,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,SAAS,IAAI;AAClE,UAAM,MAAM,aAAa,OAAO,GAAG;AACnC,QAAI,KAAK;AACP,cAAQ,MAAM,kDAAkD,QAAQ,GAAG,CAAC,EAAE;AAC9E,aAAO;AAAA,IACT;AAAA,EACF,QAAQ;AAAA,EAER;AACA,SAAO;AACT;AAMO,SAAS,oCAAoC,GAAqB;AACvE,QAAM,UAAW,GAAW,WAAY,GAAW,WAAW,KAAK;AACnE,MAAI,OAAO,YAAY,SAAU,QAAO;AAQxC,SACE,QAAQ,SAAS,cAAc,KAC/B,QAAQ,SAAS,WAAW,KAC5B,QAAQ,SAAS,cAAc,KAC/B,QAAQ,SAAS,aAAa,KAC9B,QAAQ,SAAS,gBAAgB,KACjC,QAAQ,SAAS,wBAAwB,KACzC,QAAQ,SAAS,wBAAwB,KACzC,QAAQ,SAAS,2BAA2B,KAC5C,QAAQ,SAAS,0BAA0B,KAC3C,QAAQ,SAAS,YAAY,KAC7B,QAAQ,SAAS,OAAO;AAE5B;AAKA,eAAe,SAAS,MAAc,YAAoB,KAAwB;AAChF,SAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,UAAM,EAAE,KAAK,IAAI,UAAQ,eAAe;AACxC,UAAM,WAAW,QAAQ;AACzB,UAAM,UACJ,aAAa,UACT,gBAAgB,SAAS,IAAI,IAAI,KACjC,aAAa,WAEX,gBAAgB,SAAS,IAAI,IAAI,KAEjC,gBAAgB,KAAK,IAAI,GAAG,KAAK,MAAM,YAAY,GAAI,CAAC,CAAC,IAAI,IAAI;AAEzE,SAAK,SAAS,CAAC,UAAe;AAC5B,cAAQ,CAAC,KAAK;AAAA,IAChB,CAAC;AAAA,EACH,CAAC;AACH;AAKA,SAAS,kBACP,QACA,WACoB;AACpB,QAAM,OAA8B;AAAA,IAClC,MAAM,OAAO;AAAA,IACb,UAAU,OAAO;AAAA,IACjB,UAAU,OAAO;AAAA,IACjB,GAAI,OAAO,WAAW,SAAY,EAAE,QAAQ,OAAO,OAAO,IAAI,CAAC;AAAA,IAC/D,cAAc,OAAO,gBAAgB,CAAC;AAAA,EACxC;AAEA,MAAI,cAAc,OAAO;AACvB,UAAME,OAAM,IAAI,mBAAmB;AAAA,MACjC,GAAG;AAAA,MACH,WAAW;AAAA,IACb,CAAC;AACD,uBAAmBA,MAAK,WAAW,MAAM;AACzC,WAAOA;AAAA,EACT;AAEA,QAAM,MAAM,aAAa,OAAO,GAAG;AACnC,MAAI,CAAC,KAAK;AACR,UAAM,IAAI,MAAM,6CAA6C;AAAA,EAC/D;AAEA,QAAM,MAAM,IAAI,mBAAmB;AAAA,IACjC,GAAG;AAAA,IACH,WAAW;AAAA,IACX;AAAA,IACA,GAAI,OAAO,qBAAqB,EAAE,oBAAoB,OAAO,mBAAmB,IAAI,CAAC;AAAA,IACrF,gBAAgB;AAAA,EAClB,CAAC;AACD,qBAAmB,KAAK,WAAW,MAAM;AACzC,SAAO;AACT;AAKA,SAAS,mBAAmB,KAAyB,WAA8B,QAAgC;AACjH,MAAI;AACF,QAAI,OAAO,GAAG,SAAS,CAAC,QAAiB;AACvC,UAAI,CAAC,OAAO,OAAQ;AACpB,YAAM,MAAO,KAAa,WAAY,KAAa,WAAW,KAAK,OAAO,GAAG;AAE7E,UACE,OAAO,QAAQ,aACd,IAAI,SAAS,wBAAwB,KACpC,IAAI,SAAS,4BAA4B,KACzC,IAAI,SAAS,aAAa,IAC5B;AAGA;AAAA,MACF;AACA,aAAO,QAAQ,MAAM,2BAA2B,SAAS,KAAK,OAAO,IAAI,KAAK,GAAG,EAAE;AAAA,IACrF,CAAC;AAGD,QAAI,OAAO,GAAG,SAAS,MAAM;AAAA,IAG7B,CAAC;AAAA,EACH,QAAQ;AAAA,EAER;AACF;AAQA,eAAsB,qBAAqB,QAAqD;AAC9F,QAAM,EAAE,MAAM,KAAK,OAAO,IAAI;AAE9B,QAAM,OAAwB,OAAO,QAAQ;AAC7C,QAAM,gBAAgB,OAAO;AAC7B,QAAM,aAAa,KAAK,IAAI,GAAG,KAAK,IAAI,IAAI,OAAO,kBAAkB,YAAY,OAAO,SAAS,aAAa,IAAI,KAAK,MAAM,aAAa,IAAI,CAAC,CAAC;AAEhJ,QAAM,SAAS,CAAC,MAAuB;AACrC,UAAM,IAAK,GAAW,WAAY,GAAW,WAAW,KAAK,OAAO,CAAC;AACrE,WAAO,OAAO,MAAM,WAAW,IAAI,OAAO,CAAC;AAAA,EAC7C;AAEA,QAAMC,WAAU,CAAC,OAAe,IAAI,QAAc,CAAC,YAAY,WAAW,SAAS,EAAE,CAAC;AAEtF,QAAM,iBAAiB,CAAC,MAAwB;AAC9C,UAAM,MAAM,OAAO,CAAC;AAEpB,WACE,oCAAoC,CAAC,KACrC,IAAI,SAAS,2BAA2B,KACxC,IAAI,SAAS,0BAA0B,KACvC,IAAI,SAAS,wBAAwB,KACrC,IAAI,SAAS,YAAY,KACzB,IAAI,SAAS,OAAO;AAAA,EAExB;AAEA,QAAM,iBAAiB,CAAC,MAAwB;AAC9C,UAAM,MAAM,OAAO,CAAC;AAEpB,WACE,IAAI,SAAS,aAAa,KAC1B,IAAI,SAAS,4BAA4B,KACzC,IAAI,SAAS,wBAAwB,KACrC,IAAI,SAAS,WAAW,KACxB,IAAI,YAAY,EAAE,SAAS,SAAS;AAAA,EAExC;AAEA,QAAM,cAAc,OAClB,OACA,KACA,IACA,gBACe;AACf,QAAI;AACJ,aAAS,UAAU,GAAG,WAAW,KAAK,WAAW;AAC/C,UAAI;AACF,YAAI,UAAU,GAAG;AACf,kBAAQ,MAAM,gBAAgB,KAAK,WAAW,OAAO,IAAI,GAAG,KAAK;AAAA,QACnE;AACA,eAAO,MAAM,GAAG,OAAO;AAAA,MACzB,SAAS,GAAG;AACV,kBAAU;AACV,cAAM,MAAM,OAAO,CAAC;AACpB,cAAM,YAAY,UAAU,OAAO,YAAY,CAAC;AAChD,gBAAQ,MAAM,gBAAgB,KAAK,YAAY,OAAO,IAAI,GAAG,YAAY,GAAG,GAAG,YAAY,kBAAkB,EAAE,EAAE;AACjH,YAAI,CAAC,UAAW,OAAM;AAEtB,cAAMA,SAAQ,GAAG;AAAA,MACnB;AAAA,IACF;AACA,UAAM,mBAAmB,QAAQ,UAAU,IAAI,MAAM,OAAO,WAAW,GAAG,KAAK,SAAS,CAAC;AAAA,EAC3F;AAMA,QAAM,eAAe,aAAa,GAAG;AAGrC,UAAQ,MAAM,wBAAwB,IAAI,KAAK;AAC/C,QAAM,cAAc,MAAM,SAAS,IAAI;AACvC,MAAI,CAAC,aAAa;AAChB,YAAQ,MAAM,qBAAqB,IAAI,uEAAuE;AAAA,EAChH,OAAO;AACL,YAAQ,MAAM,qBAAqB,IAAI,eAAe;AAAA,EACxD;AAGA,MAAI,SAAS,OAAO;AAClB,YAAQ,MAAM,gFAAgF;AAC9F,QAAI,gBAAgB;AACpB,QAAI,CAAC,eAAe;AAClB,cAAQ,MAAM,oEAAoE;AAClF,YAAM,aAAa,MAAM,mBAAmB,MAAM,MAAM;AACxD,YAAM,uBAAuB,aAAa,UAAU;AACpD,UAAI,CAAC,sBAAsB;AACzB,cAAM,IAAI;AAAA,UACR,8GAA8G,IAAI;AAAA,QACpH;AAAA,MACF;AACA,sBAAgB;AAAA,IAClB;AAEA,UAAM,eACJ,OAAO,qBACH,CAAC,OAAO,kBAAkB,IAC1B,CAAC,gBAAgB,mBAAmB,UAAU,SAAS,KAAK;AAElE,UAAM,YAAsB,CAAC;AAC7B,eAAW,KAAK,cAAc;AAC5B,UAAI;AACF,gBAAQ,MAAM,6CAA6C,CAAC,KAAK;AACjE,cAAM,SAAS,MAAM;AAAA,UACnB,OAAO,CAAC;AAAA,UACR;AAAA,UACA,OAAO,YAAY;AACjB,kBAAM,MAAM,kBAAkB,EAAE,GAAG,QAAQ,KAAK,eAAe,oBAAoB,EAAE,GAAG,KAAK;AAC7F,gBAAI;AACF,oBAAM,IAAI,MAAM;AAChB,qBAAO;AAAA,YACT,SAAS,GAAG;AACV,kBAAI;AACF,sBAAM,IAAI,MAAM,EAAE,QAAQ,yBAAyB,CAAC,YAAY,OAAO,GAAG,CAAC;AAAA,cAC7E,QAAQ;AAAA,cAER;AACA,oBAAM;AAAA,YACR;AAAA,UACF;AAAA,UACA;AAAA,QACF;AAGA,cAAM,aAAa,MAAM,OAAO,QAAQ;AACxC,cAAM,eAAe,MAAM,OAAO,sBAAsB;AACxD,cAAM,kBAAkB,MAAM,OAAO,eAAe,QAAW,EAAE,WAAW,KAAK,CAAC,EAAE,MAAM,MAAM,MAAS;AACzG,cAAM,aAAa,cAAc,SAAS,cAAc;AACxD,cAAM,QAAQ,WAAW,MAAM,KAAK;AAEpC,cAAM,kBAAkB,QAAQ,MAAM,KAAK,IAAI;AAC/C,cAAM,sBAAsB,kBAAkB,iBAAiB,eAAe,IAAI;AAClF,cAAM,kBAAkB,OAAO,eAAe,WAAW,OAAO,SAAS,YAAY,EAAE,IAAI;AAC3F,cAAM,2BAA2B,oBAAoB,KAAK,oBAAoB,MAAM,OAAO,SAAS,eAAe;AACnH,cAAM,eAAe,uBAAuB;AAE5C,YAAI,cAAc;AAChB,gBAAM,kBAAkB,sBAAsB,gBAAgB;AAC9D,kBAAQ;AAAA,YACN,qBAAqB,CAAC,yDAAyD,eAAe,WAAW,mBAAmB,SAAS,gBAAgB,UAAU;AAAA,UACjK;AACA,iBAAO;AAAA,YACL,MAAM;AAAA,YACN,WAAW;AAAA,YACX,KAAK;AAAA,YACL,oBAAoB;AAAA,YACpB;AAAA,YACA,GAAI,kBAAkB,EAAE,gBAAgB,IAAI,CAAC;AAAA,YAC7C;AAAA,YACA,KAAK;AAAA,UACP;AAAA,QACF;AAEA,gBAAQ,MAAM,qBAAqB,CAAC,mDAAmD;AACvF,eAAO;AAAA,UACL,MAAM;AAAA,UACN,WAAW;AAAA,UACX,KAAK;AAAA,UACL,oBAAoB;AAAA,UACpB;AAAA,UACA,GAAI,kBAAkB,EAAE,gBAAgB,IAAI,CAAC;AAAA,UAC7C,YAAY;AAAA,UACZ,KAAK;AAAA,QACP;AAAA,MACF,SAAS,GAAG;AACV,cAAM,MAAM,OAAO,CAAC;AACpB,kBAAU,KAAK,GAAG,CAAC,KAAK,GAAG,EAAE;AAE7B,gBAAQ,MAAM,qBAAqB,CAAC,aAAa,GAAG,EAAE;AAAA,MACxD;AAAA,IACF;AAEA,UAAM,IAAI,MAAM,iDAAiD,UAAU,KAAK,KAAK,CAAC,EAAE;AAAA,EAC1F;AAGA,MAAI;AACJ,MAAI;AACF,YAAQ,MAAM,yCAAyC,IAAI,KAAK;AAChE,aAAS,MAAM;AAAA,MACb;AAAA,MACA;AAAA,MACA,OAAO,YAAY;AAEjB,cAAMD,OAAM,kBAAkB,QAAQ,KAAK;AAC3C,YAAI;AACF,gBAAMA,KAAI,MAAM;AAChB,iBAAOA;AAAA,QACT,SAAS,GAAG;AACV,cAAI;AACF,kBAAMA,KAAI,MAAM,EAAE,QAAQ,iCAAiC,OAAO,GAAG,CAAC;AAAA,UACxE,QAAQ;AAAA,UAER;AACA,gBAAM;AAAA,QACR;AAAA,MACF;AAAA,MACA;AAAA,IACF;AAGA,UAAM,MAAM;AACZ,QAAI,CAAC,IAAK,OAAM,IAAI,MAAM,2EAA2E;AAKrG,UAAM,mBAAmB,OACvB,OACA,aACuD;AACvD,UAAI;AACJ,iBAAW,KAAK,UAAU;AACxB,YAAI;AACF,gBAAM,QAAQ,MAAM,EAAE,GAAG;AACzB,kBAAQ,MAAM,0BAA0B,KAAK,QAAQ,EAAE,OAAO,GAAG;AACjE,iBAAO,EAAE,OAAO,SAAS,EAAE,QAAQ;AAAA,QACrC,SAAS,GAAG;AACV,gBAAM,MAAM,OAAO,CAAC;AACpB,oBAAU;AACV,kBAAQ,MAAM,0BAA0B,KAAK,YAAY,EAAE,OAAO,MAAM,GAAG,EAAE;AAAA,QAC/E;AAAA,MACF;AACA,UAAI,SAAS;AACX,gBAAQ,MAAM,0BAA0B,KAAK,2BAA2B,OAAO,EAAE;AAAA,MACnF;AACA,aAAO;AAAA,IACT;AAMA,UAAM,YAAY,MAAM;AAAA,MACtB;AAAA,MACA;AAAA,QACE,EAAE,SAAS,sBAAsB,IAAI,MAAM,IAAI,QAAQ,QAAW,EAAE,WAAW,MAAM,cAAc,mBAAmB,CAAC,EAAE;AAAA,QACzH,EAAE,SAAS,sBAAsB,IAAI,MAAM,IAAI,QAAQ,QAAW,EAAE,WAAW,KAAM,cAAc,mBAAmB,CAAC,EAAE;AAAA,QACzH,EAAE,SAAS,4BAA4B,IAAI,MAAM,IAAI,QAAQ,GAAG,EAAE,WAAW,KAAM,cAAc,mBAAmB,CAAC,EAAE;AAAA,QACvH,EAAE,SAAS,4BAA4B,IAAI,MAAM,IAAI,QAAQ,GAAG,EAAE,WAAW,MAAM,cAAc,mBAAmB,CAAC,EAAE;AAAA,MACzH;AAAA,IACF;AAGA,UAAM,eAAe,MAAM;AAAA,MACzB;AAAA,MACA;AAAA,QACE,EAAE,SAAS,uBAAuB,IAAI,MAAM,IAAI,eAAe,EAAE,WAAW,MAAM,cAAc,mBAAmB,CAAC,EAAE;AAAA,QACtH,EAAE,SAAS,uBAAuB,IAAI,MAAM,IAAI,eAAe,EAAE,WAAW,MAAM,cAAc,mBAAmB,CAAC,EAAE;AAAA,MACxH;AAAA,IACF;AAEA,UAAM,aAAa,WAAW;AAC9B,UAAM,UAAU,cAAc;AAE9B,UAAM,gBAAgB,SAAS;AAC/B,UAAM,aAAa,OAAO,kBAAkB,WAAW,OAAO,SAAS,eAAe,EAAE,IAAI;AAC5F,UAAM,sBAAsB,OAAO,SAAS,UAAU,KAAK,cAAc,OAAO,aAAa;AAC7F,UAAM,QAAQ,YAAY,MAAM,KAAK;AAErC,YAAQ;AAAA,MACN,sDAAsD,mBAAmB,GAAG,UAAU,KAAK,yBAAyB,WAAW,SAAS,SAAS,GAAG,aAAa,KAAK,sBAAsB;AAAA,IAC9L;AAGA,UAAM,kBAAkB,QAAQ,MAAM,KAAK,IAAI;AAC/C,UAAM,sBAAsB,kBAAkB,iBAAiB,eAAe,IAAI;AAIlF,UAAM,kBAAkB;AACxB,UAAM,2BAA2B,oBAAoB,KAAK,oBAAoB,MAAM,OAAO,SAAS,eAAe;AAGnH,UAAM,eAAe,uBAAuB;AAE5C,QAAI,cAAc;AAChB,YAAM,kBAAkB,sBAAsB,gBAAgB;AAC9D,cAAQ,MAAM,6CAA6C,eAAe,WAAW,mBAAmB,SAAS,gBAAgB,UAAU,GAAG;AAE9I,aAAO;AAAA,QACL,MAAM;AAAA,QACN,WAAW;AAAA,QACX,KAAK,gBAAgB,OAAO;AAAA,QAC5B,GAAI,aAAa,EAAE,WAAW,IAAI,CAAC;AAAA;AAAA,QAEnC,YAAY;AAAA,QACZ;AAAA,MACF;AAAA,IACF;AAGA,QAAI,sBAAsB,GAAG;AAC3B,cAAQ,MAAM,8BAA8B,mBAAmB,YAAY;AAE3E,aAAO;AAAA,QACL,MAAM;AAAA,QACN,WAAW;AAAA,QACX,KAAK,gBAAgB,OAAO;AAAA,QAC5B,GAAI,aAAa,EAAE,WAAW,IAAI,CAAC;AAAA;AAAA,QAEnC,YAAY;AAAA,QACZ;AAAA,MACF;AAAA,IACF;AAGA,YAAQ,MAAM,uDAAuD;AAErE,WAAO;AAAA,MACL,MAAM;AAAA,MACN,WAAW;AAAA,MACX,KAAK,gBAAgB,OAAO;AAAA,MAC5B,GAAI,aAAa,EAAE,WAAW,IAAI,CAAC;AAAA;AAAA,MAEnC,YAAY;AAAA,MACZ;AAAA,IACF;AAAA,EACF,SAAS,UAAU;AACjB,QAAI,SAAS,OAAO;AAElB,YAAM;AAAA,IACR;AAGA,QAAI,QAAQ;AACV,UAAI;AACF,cAAM,OAAO,MAAM,EAAE,QAAQ,wBAAwB,CAAC;AAAA,MACxD,QAAQ;AAAA,MAER;AAAA,IACF;AAEA,QAAI,CAAC,oCAAoC,QAAQ,GAAG;AAElD,YAAM;AAAA,IACR;AAEA,YAAQ,MAAM,yDAAyD;AACvE,QAAI,gBAAgB;AACpB,QAAI,CAAC,eAAe;AAClB,cAAQ,MAAM,8EAA8E;AAC5F,YAAM,aAAa,MAAM,mBAAmB,MAAM,MAAM;AACxD,UAAI,CAAC,YAAY;AACf,cAAM,IAAI;AAAA,UACR,uGAAuG,IAAI;AAAA,QAC7G;AAAA,MACF;AAEA,YAAM,uBAAuB,aAAa,UAAU;AACpD,UAAI,CAAC,sBAAsB;AACzB,cAAM,IAAI;AAAA,UACR,2GAA2G,IAAI;AAAA,QACjH;AAAA,MACF;AACA,sBAAgB;AAAA,IAClB;AAEA,QAAI;AACF,YAAM,mBAAmB,OACvB,QACA,uBAC8B;AAC9B,cAAM,aAAa,MAAM,OAAO,QAAQ;AACxC,cAAM,eAAe,MAAM,OAAO,sBAAsB;AACxD,cAAM,kBAAkB,MAAM,OAAO,eAAe,QAAW,EAAE,WAAW,KAAK,CAAC,EAAE,MAAM,MAAM,MAAS;AACzG,cAAM,aAAa,cAAc,SAAS,cAAc;AACxD,cAAM,QAAQ,WAAW,MAAM,KAAK;AAIpC,cAAM,kBAAkB,QAAQ,MAAM,KAAK,IAAI;AAC/C,cAAM,sBAAsB,kBAAkB,iBAAiB,eAAe,IAAI;AAIlF,cAAM,kBAAkB,OAAO,eAAe,WAAW,OAAO,SAAS,YAAY,EAAE,IAAI;AAC3F,cAAM,2BAA2B,oBAAoB,KAAK,oBAAoB,MAAM,OAAO,SAAS,eAAe;AAGnH,cAAM,eAAe,uBAAuB;AAE5C,YAAI,cAAc;AAChB,gBAAM,kBAAkB,sBAAsB,gBAAgB;AAC9D,kBAAQ;AAAA,YACN,qBAAqB,kBAAkB,yDAAyD,eAAe,WAAW,mBAAmB,SAAS,gBAAgB,UAAU;AAAA,UAClL;AACA,iBAAO;AAAA,YACL,MAAM;AAAA,YACN,WAAW;AAAA,YACX,KAAK;AAAA,YACL;AAAA,YACA;AAAA,YACA,GAAI,kBAAkB,EAAE,gBAAgB,IAAI,CAAC;AAAA,YAC7C;AAAA,YACA,KAAK;AAAA,UACP;AAAA,QACF;AAGA,gBAAQ,MAAM,qBAAqB,kBAAkB,mDAAmD;AACxG,eAAO;AAAA,UACL,MAAM;AAAA,UACN,WAAW;AAAA,UACX,KAAK;AAAA,UACL;AAAA,UACA;AAAA,UACA,GAAI,kBAAkB,EAAE,gBAAgB,IAAI,CAAC;AAAA,UAC7C,YAAY;AAAA,UACZ,KAAK;AAAA,QACP;AAAA,MACF;AAEA,YAAM,eACJ,CAAC,gBAAgB,mBAAmB,UAAU,SAAS,KAAK;AAE9D,YAAM,YAAsB,CAAC;AAC7B,iBAAW,KAAK,cAAc;AAC5B,YAAI;AACF,kBAAQ,MAAM,6CAA6C,CAAC,KAAK;AACjE,gBAAM,SAAS,MAAM;AAAA,YACnB,OAAO,CAAC;AAAA,YACR;AAAA,YACA,OAAO,YAAY;AACjB,oBAAM,MAAM,kBAAkB,EAAE,GAAG,QAAQ,KAAK,eAAe,oBAAoB,EAAE,GAAG,KAAK;AAC7F,kBAAI;AACF,sBAAM,IAAI,MAAM;AAChB,uBAAO;AAAA,cACT,SAAS,GAAG;AACV,oBAAI;AACF,wBAAM,IAAI,MAAM,EAAE,QAAQ,yBAAyB,CAAC,YAAY,OAAO,GAAG,CAAC;AAAA,gBAC7E,QAAQ;AAAA,gBAER;AACA,sBAAM;AAAA,cACR;AAAA,YACF;AAAA,YACA;AAAA,UACF;AACA,iBAAO,MAAM,iBAAiB,QAAQ,CAAC;AAAA,QACzC,SAAS,GAAG;AACV,gBAAM,MAAO,GAAW,WAAY,GAAW,WAAW,KAAK,OAAO,CAAC;AACvE,oBAAU,KAAK,GAAG,CAAC,KAAK,GAAG,EAAE;AAC7B,cAAI;AAAA,UAEJ,QAAQ;AAAA,UAER;AACA,kBAAQ,MAAM,qBAAqB,CAAC,aAAa,GAAG,EAAE;AAAA,QACxD;AAAA,MACF;AAEA,YAAM,IAAI,MAAM,yCAAyC,UAAU,KAAK,KAAK,CAAC,EAAE;AAAA,IAClF,SAAS,UAAU;AACjB,cAAQ;AAAA,QACN,oDAAoD,QAAQ,gBAAgB,QAAQ;AAAA,MACtF;AACA,YAAM,IAAI;AAAA,QACR,gDAAiD,UAAkB,WAAW,QAAQ,UAAW,UAAkB,WAAW,QAAQ;AAAA,MACxI;AAAA,IACF;AAAA,EACF;AACF;","names":["EventEmitter","EventEmitter","sid","shortUid","now","key","out","status","statusUpper","aiTypeRaw","timeStampRaw","timeStampNum","alarmTimeStamp","statusLower","statusTokens","visitorActive","dayNightActive","statusIndicatesMotion","aiTypeTokenRaw","aiFlag","aiTypeToken","prev","curr","aiTypeChanged","aiTimeStampChanged","body","decrypted","combined","sleep","videoCodecMap","EventEmitter","net","dgram","EventEmitter","i","h","sendInterleaved","resources","splitAnnexBToNalPayloads","convertToAnnexB","spawn","sleep","out","summary","cached","onNvr","streamType","timeoutMs","frameLen","frame","encoding","streamInfo","fr","spawn","randomUUID","result","rtspEnabled","netPort","sleep","dgram","networkInterfaces","dgram","networkInterfaces","api","sleepMs"]}