{"version":3,"sources":["../src/util/event_manager.ts","../src/parse.ts","../src/util/env.ts","../src/util/log.ts","../src/beast_handler.ts"],"sourcesContent":["\r\nexport default class EventManager<M extends {[key: string] : any}> {\r\n    private listeners = new Map<String, Function[]>()\r\n\r\n    addListener<E extends keyof M>(eventName: E, callback: (data: M[E])=> void){\r\n        if(!this.listeners.has(eventName as string)){\r\n            this.listeners.set(eventName as string, [])\r\n        }\r\n\r\n        this.listeners.get(eventName as string)!.push(callback)\r\n    }\r\n\r\n    removeListener<E extends keyof M>(eventName: E, callback: (data: M[E])=> void){\r\n        if(this.listeners.has(eventName as string)){\r\n            const list = this.listeners.get(eventName as string)\r\n\r\n            const index = list!.findIndex(c => c === callback)\r\n\r\n            if(index !== -1){\r\n                list!.splice(index, 1)\r\n            }\r\n\r\n        }\r\n    }\r\n\r\n    protected dispatch<E extends keyof M>(eventName: E, data: M[E]){\r\n        if(this.listeners.has(eventName as string)){\r\n            const list = this.listeners.get(eventName as string)\r\n\r\n            for(const l of list!){\r\n                l(data)\r\n            }\r\n\r\n        }\r\n    }\r\n}\r\n","\r\n// docs see https://wiki.jetvision.de/wiki/Mode-S_Beast:Data_Output_Formats\r\n\r\nimport * as modeSAdsbParser from 'mode-s-adsb-parser'\r\nimport {logError, logWarning} from './util/log'\r\nimport {Byte} from './util/typescript'\r\n\r\ntype BeastBinaryFrame<T extends BeastBinaryFrameFormat> = {\r\n    format: T,\r\n    bytes: Byte[]\r\n}\r\n\r\nexport type BeastParsedFrameType = BeastParsedFrameType_Mode_AC | BeastParsedFrameType_Mode_S | BeastParsedFrameType_ADSB | BeastParsedFrameType_Debug\r\ntype BeastParsedFrameType_Mode_AC = 'MODE_AC'\r\ntype BeastParsedFrameType_Mode_S = 'MODE_S'\r\ntype BeastParsedFrameType_ADSB = 'ADSB'\r\ntype BeastParsedFrameType_Debug = 'DEBUG'\r\n\r\nexport type BeastParsedFrame<T extends BeastParsedFrameType> = {\r\n    type: T,\r\n    mlatTimestamp: number\r\n    signalLevel: Byte\r\n    dataBytes: Byte[]\r\n}\r\n\r\nexport function parseBeastSocketData(data: Buffer<ArrayBufferLike>, ignoreInvalid?: boolean){\r\n\r\n    const frames = parseBeastLine(data, ignoreInvalid)\r\n\r\n    const parsedFrames: BeastParsedFrame<BeastParsedFrameType>[]= []\r\n    for(const frame of frames){\r\n        try {\r\n            parsedFrames.push(parseBeastFrame(frame))\r\n        } catch (err){\r\n            if(ignoreInvalid){\r\n                //ignore\r\n                logWarning('ignoring invalid frame', frame.format, frame.bytes)\r\n            } else {\r\n                throw err\r\n            }\r\n        }\r\n    }\r\n    return parsedFrames\r\n}\r\n\r\n\r\nconst ESCAPE_BYTE = 0x1a\r\n\r\nconst BEAST_BINARY_FRAME_FORMAT_MODE_AC: BeastBinaryFrameFormat_Mode_AC = 0x31\r\nconst BEAST_BINARY_FRAME_FORMAT_MODE_S_SHORT: BeastBinaryFrameFormat_Mode_S = 0x32\r\nconst BEAST_BINARY_FRAME_FORMAT_MODE_S_LONG: BeastBinaryFrameFormat_ADSB = 0x33\r\nconst BEAST_BINARY_FRAME_FORMAT_DEBUG: BeastBinaryFrameFormat_Debug = 0x34\r\n\r\n// just the content, no escape bytes, no frame format bytes\r\nconst BEAST_BINARY_FRAME_CONTENT_LENGTHS: Record<BeastBinaryFrameFormat, number>=  {\r\n    0x31: 6 + 1 + 2,\r\n    0x32: 6 + 1 + 7,\r\n    0x33: 6 + 1 + 14,\r\n    0x34: 6 + 1 + 1 //TODO is this correct? documentation is unclear https://wiki.jetvision.de/wiki/Mode-S_Beast:Data_Output_Formats\r\n}\r\n\r\ntype BeastBinaryFrameFormat = BeastBinaryFrameFormat_Mode_AC | BeastBinaryFrameFormat_Mode_S | BeastBinaryFrameFormat_ADSB | BeastBinaryFrameFormat_Debug\r\ntype BeastBinaryFrameFormat_Mode_AC = 0x31\r\ntype BeastBinaryFrameFormat_Mode_S = 0x32\r\ntype BeastBinaryFrameFormat_ADSB = 0x33\r\ntype BeastBinaryFrameFormat_Debug = 0x34\r\n\r\nconst BEAST_BINARY_FRAME_TYPES: Record<BeastBinaryFrameFormat, BeastParsedFrameType> = {\r\n    '49': 'MODE_AC',\r\n    '50': 'MODE_S',\r\n    '51': 'ADSB',\r\n    '52': 'DEBUG'\r\n}\r\n\r\n\r\nclass FormatError extends Error {\r\n    constructor(message: string){\r\n        super(message)\r\n    }\r\n}\r\n\r\nclass FormatErrorWithByteIndex extends Error {\r\n    constructor(message: string, byteIndex: number){\r\n        super(message + ' at byte ' + byteIndex)\r\n    }\r\n}\r\n\r\n\r\n/**\r\n *\r\n * @returns messages contained in that line (already unescaped)\r\n */\r\nexport function parseBeastLine(bytes: Buffer<ArrayBufferLike>, ignoreInvalid?: boolean){\r\n\r\n    /* for documentation see\r\n\r\n        https://wiki.jetvision.de/wiki/Mode-S_Beast:Data_Output_Formats\r\n        https://github.com/denzen84/dump1090/blob/master/sdr_beast.c @ static int copyBinMessageSafe()\r\n    */\r\n\r\n\r\n    const frames: BeastBinaryFrame<BeastBinaryFrameFormat>[] = [] // TODO is this efficient? shouldnt we use some kind of Buffer?\r\n\r\n    if(bytes[0] !== ESCAPE_BYTE){\r\n        if(ignoreInvalid){\r\n            return []\r\n        } else {\r\n            throw new FormatErrorWithByteIndex('first byte is not an escape byte', 0)\r\n        }\r\n    }\r\n\r\n    let currentFrameBytes: Byte[] = []\r\n    let currentFrameFormat: BeastBinaryFrameFormat\r\n\r\n    for(let byteIndex = 0; byteIndex < bytes.length; byteIndex++){\r\n        const byte = bytes[byteIndex] as Byte\r\n\r\n        if(currentFrameBytes.length === 0 && typeof currentFrameFormat !== 'number' && byte !== ESCAPE_BYTE){\r\n            if(ignoreInvalid){\r\n                break\r\n            } else {\r\n                throw new FormatErrorWithByteIndex('first byte of frame is not an escape byte', byteIndex)\r\n            }\r\n        }\r\n\r\n        if(byte === ESCAPE_BYTE){\r\n            const nextByteIndex = byteIndex+1\r\n            if(bytes.length <= nextByteIndex){\r\n                if(ignoreInvalid){\r\n                    break\r\n                } else {\r\n                    throw new FormatErrorWithByteIndex('unexpected escape byte at the very end', byteIndex)\r\n                }\r\n            }\r\n\r\n            const nextByte = bytes[nextByteIndex] as Byte\r\n\r\n            if(nextByte === ESCAPE_BYTE){\r\n                currentFrameBytes.push(nextByte)\r\n            } else if(nextByte === BEAST_BINARY_FRAME_FORMAT_MODE_AC || nextByte === BEAST_BINARY_FRAME_FORMAT_MODE_S_SHORT || nextByte === BEAST_BINARY_FRAME_FORMAT_MODE_S_LONG || nextByte === BEAST_BINARY_FRAME_FORMAT_DEBUG){\r\n                // new message starts\r\n\r\n                if(currentFrameBytes.length > 0){\r\n                    makeFrameAndCleanup()\r\n                }\r\n\r\n                currentFrameFormat = nextByte\r\n            } else {\r\n                if(ignoreInvalid){\r\n                    currentFrameBytes.push(nextByte)\r\n                } else {\r\n                    throw new FormatErrorWithByteIndex('unexpected escape byte (the following byte ' + (byteIndex+1) + ' does not need escaping)', byteIndex)\r\n                }\r\n            }\r\n\r\n            byteIndex += 1 //skip processing of next byte\r\n        } else {\r\n            currentFrameBytes.push(byte)\r\n        }\r\n    }\r\n\r\n    if(currentFrameBytes.length > 0){\r\n        makeFrameAndCleanup()\r\n    }\r\n\r\n    function makeFrameAndCleanup(){\r\n        if(typeof currentFrameFormat !== 'number'){\r\n            if(ignoreInvalid){\r\n                cleanup()\r\n                return\r\n            } else {\r\n                throw new FormatError('can not make message, currentFrameFormat is undefined')\r\n            }\r\n        }\r\n\r\n        const expectedFrameContentLength = BEAST_BINARY_FRAME_CONTENT_LENGTHS[currentFrameFormat]\r\n        if(currentFrameBytes.length !== expectedFrameContentLength){\r\n            if(ignoreInvalid){\r\n                cleanup()\r\n                return\r\n            } else {\r\n                logError(currentFrameBytes, Buffer.from(currentFrameBytes).toString('ascii'))\r\n                throw new FormatError('invalid content byte length for frame type \"' + String.fromCharCode(currentFrameFormat) + '\": ' + currentFrameBytes.length + ' (expected ' + expectedFrameContentLength + ')')\r\n            }\r\n        }\r\n\r\n        let allBytesAreNull = true\r\n        for(const byte of currentFrameBytes){\r\n            if(byte !== 0){\r\n                allBytesAreNull = false\r\n                break\r\n            }\r\n        }\r\n\r\n        //skip empty frames\r\n        if(!allBytesAreNull){\r\n            frames.push({\r\n                format: currentFrameFormat,\r\n                bytes: currentFrameBytes\r\n            })\r\n        }\r\n\r\n        cleanup()\r\n\r\n        function cleanup(){\r\n\r\n            currentFrameBytes = []\r\n            currentFrameFormat = undefined\r\n        }\r\n    }\r\n\r\n    return frames\r\n}\r\n\r\n\r\nexport function parseBeastFrame(frame: BeastBinaryFrame<BeastBinaryFrameFormat>) : BeastParsedFrame<BeastParsedFrameType> {\r\n\r\n    const dataBytes = frame.bytes.slice(7)\r\n    const mlatTimestamp = modeSAdsbParser.util.bits_and_bytes.bitStringToUnsignedIntegerSafeRangeFromBits(modeSAdsbParser.util.bits_and_bytes.bytesToFullBitString(frame.bytes.slice(0, 6)), 6*8, 'mlatTimestamp')\r\n    const signalLevel = frame.bytes[6]\r\n\r\n    if(frame.format < 49 || frame.format > 52){\r\n        throw new Error('unexpected frame.format: ' + frame.format)\r\n    }\r\n\r\n    return {\r\n        type: BEAST_BINARY_FRAME_TYPES[frame.format],\r\n        mlatTimestamp,\r\n        signalLevel,\r\n        dataBytes\r\n    }\r\n}\r\n\r\n","export function isDevEnvironment(){\r\n    return process.env.NODE_ENV !== 'production'\r\n}\r\n","import { isDevEnvironment } from \"./env\"\r\n\r\nexport function logsAreEnabled(){\r\n    return isDevEnvironment()\r\n}\r\n\r\nexport function logError(...args: any){\r\n    if(logsAreEnabled()){\r\n        console.error.apply(null, args)\r\n    }\r\n}\r\n\r\nexport function logWarning(...args: any){\r\n    if(logsAreEnabled()){\r\n        console.warn.apply(null, args)\r\n    }\r\n}\r\n","import EventManager from \"./util/event_manager\";\r\nimport {BeastParsedFrame, BeastParsedFrameType, parseBeastSocketData} from './parse'\r\nimport { decodeBasics, parseADSB, parseModeS, calculateUnambigiousLocation, CprData } from \"mode-s-adsb-parser\";\r\n\r\nexport type AircraftData = {\r\n    icaoAddress: AircraftIcaoAddress\r\n}\r\n\r\nexport interface AircraftDataWithLocation extends AircraftData {\r\n    location: {\r\n        latitude: number\r\n        longitude: number\r\n    }\r\n}\r\n\r\nexport type BeastHandlerEvents = {\r\n    'aircraft_new': AircraftData // first time we see a message from this aircraft (aircraft not in the cache)\r\n    'aircraft_update': AircraftData // received message from aircraft\r\n    'aircraft_update_with_location': AircraftDataWithLocation // received message from aircraft and we can locate it\r\n    'warning': {message: string, context: any}\r\n    'removed_from_cache': AircraftIcaoAddress // no message received from this aircraft for some time\r\n}\r\n\r\ntype AircraftIcaoAddress = string\r\n\r\ntype EncodedCprData = {\r\n    receivedTime: number\r\n    cpr: CprData\r\n}\r\n\r\ntype AircraftCacheData = {\r\n    lastDataReceivedTime: number\r\n    lastUnambigiousLocation?: {\r\n        latitude: number\r\n        longitude: number\r\n    }\r\n    lastEncodedLocationOdd?: EncodedCprData\r\n    lastEncodedLocationEven?: EncodedCprData\r\n}\r\n\r\nconst MAX_AIRCRAFT_CACHE_TIME = 1000 * 60 * 30 // 30 minutes\r\n\r\nexport class BeastHandler extends EventManager<BeastHandlerEvents> {\r\n\r\n    private readonly aircraftCache = new Map<AircraftIcaoAddress, AircraftCacheData>()\r\n    private cleanUpCacheInterval: NodeJS.Timeout\r\n\r\n    /**\r\n     * don't forget to call stop() once you are done\r\n     */\r\n    constructor(){\r\n        super()\r\n\r\n        this.cleanUpCacheInterval = setInterval(()=>{\r\n            this.cleanUpCache()\r\n        }, 1000 * 30)\r\n    }\r\n\r\n    stop(){\r\n        clearInterval(this.cleanUpCacheInterval)\r\n        this.aircraftCache.clear()\r\n    }\r\n\r\n    //TODO rewrite to be non-blocking\r\n    private cleanUpCache(){\r\n\r\n        const now = Date.now()\r\n\r\n        for(const aircraftEntry of this.aircraftCache){\r\n            const aircraft = aircraftEntry[1]\r\n\r\n            if(now - aircraft.lastDataReceivedTime > MAX_AIRCRAFT_CACHE_TIME){\r\n                const icaoAddress = aircraftEntry[0]\r\n                this.aircraftCache.delete(icaoAddress)\r\n\r\n                this.dispatch('removed_from_cache', icaoAddress)\r\n            }\r\n        }\r\n    }\r\n\r\n\r\n    handleBeastData(data: Buffer<ArrayBufferLike>){\r\n        const frames = parseBeastSocketData(data, true)\r\n\r\n        for(const frame of frames){\r\n            this.handleBeastFrame(frame)\r\n        }\r\n    }\r\n\r\n    private handleBeastFrame(frame: BeastParsedFrame<BeastParsedFrameType>){\r\n        if(frame.type === 'ADSB'){\r\n            try {\r\n                const parsedModeS = parseModeS(frame.dataBytes)\r\n                const parsedADSB = parseADSB(parsedModeS)\r\n\r\n                const myIcaoAddressHex = icaoNumberToHex(parsedModeS.icaoAddress)\r\n\r\n                const existingAircraft = this.aircraftCache.get(myIcaoAddressHex)\r\n                const aircraft: AircraftCacheData = existingAircraft || {\r\n                    lastDataReceivedTime: Date.now()\r\n                }\r\n\r\n                this.aircraftCache.set(myIcaoAddressHex, aircraft)\r\n\r\n                const decodedBasics = decodeBasics(parsedADSB)\r\n\r\n                if(decodedBasics.locationInformation.locationAvailable){\r\n                    if(decodedBasics.locationInformation.location.format === 'even'){\r\n                        aircraft.lastEncodedLocationEven = {\r\n                            receivedTime: Date.now(),//TODO shouldnt we use mlatTimestamp for this?\r\n                            cpr: decodedBasics.locationInformation.location\r\n                        }\r\n                    } else {\r\n                        aircraft.lastEncodedLocationOdd = {\r\n                            receivedTime: Date.now(),//TODO shouldnt we use mlatTimestamp for this?\r\n                            cpr: decodedBasics.locationInformation.location\r\n                        }\r\n                    }\r\n                }\r\n\r\n                if(aircraft.lastEncodedLocationEven && aircraft.lastEncodedLocationOdd){\r\n                    if(Math.abs(aircraft.lastEncodedLocationEven.receivedTime - aircraft.lastEncodedLocationOdd.receivedTime) < 1000 * 60 * 2){// 2 minutes\r\n\r\n                        const olderMessage = aircraft.lastEncodedLocationEven.receivedTime < aircraft.lastEncodedLocationOdd.receivedTime ? aircraft.lastEncodedLocationEven.cpr : aircraft.lastEncodedLocationOdd.cpr\r\n\r\n                        const moreRecentMessage = aircraft.lastEncodedLocationEven.receivedTime < aircraft.lastEncodedLocationOdd.receivedTime ? aircraft.lastEncodedLocationOdd.cpr : aircraft.lastEncodedLocationEven.cpr\r\n\r\n                        const unambigiousLocation = calculateUnambigiousLocation([olderMessage, moreRecentMessage])\r\n\r\n                        aircraft.lastUnambigiousLocation = unambigiousLocation\r\n\r\n                        this.dispatch('aircraft_update_with_location', {\r\n                            icaoAddress: myIcaoAddressHex,\r\n                            location: unambigiousLocation\r\n                        })\r\n                    }\r\n                }\r\n\r\n\r\n\r\n                if(!existingAircraft){\r\n                    this.dispatch('aircraft_new', {\r\n                        icaoAddress: myIcaoAddressHex\r\n                    })\r\n                } else {\r\n                    if(!aircraft.lastUnambigiousLocation){\r\n                        this.dispatch('aircraft_update', {\r\n                            icaoAddress: myIcaoAddressHex\r\n                        })\r\n                    }\r\n                }\r\n\r\n            } catch (err){\r\n                this.dispatch('warning', {\r\n                    message: 'Failed to decode adsb frame',\r\n                    context: err\r\n                })\r\n            }\r\n        } else if(frame.type === 'MODE_S') {\r\n            try {\r\n                const parsedModeS = parseModeS(frame.dataBytes)\r\n\r\n                const myIcaoAddressHex = icaoNumberToHex(parsedModeS.icaoAddress)\r\n\r\n                const existingAircraft = this.aircraftCache.get(myIcaoAddressHex)\r\n                const aircraft: AircraftCacheData = existingAircraft || {\r\n                    lastDataReceivedTime: Date.now()\r\n                }\r\n\r\n                this.aircraftCache.set(myIcaoAddressHex, aircraft)\r\n\r\n                if(existingAircraft){\r\n                    this.dispatch('aircraft_update', {\r\n                        icaoAddress: myIcaoAddressHex\r\n                    })\r\n                } else {\r\n                    this.dispatch('aircraft_new', {\r\n                        icaoAddress: myIcaoAddressHex\r\n                    })\r\n                }\r\n\r\n            } catch (err){\r\n                this.dispatch('warning', {\r\n                    message: 'Failed to decode mode s frame',\r\n                    context: err\r\n                })\r\n            }\r\n        } else {\r\n            this.dispatch('warning', {\r\n                message: 'Unsupported frame type',\r\n                context: frame.type\r\n            })\r\n        }\r\n    }\r\n\r\n}\r\n\r\nfunction icaoNumberToHex(n: number){\r\n    return n.toString(16)\r\n}\r\n"],"mappings":";AACA,IAAqB,eAArB,MAAmE;AAAA,EAAnE;AACI,SAAQ,YAAY,oBAAI,IAAwB;AAAA;AAAA,EAEhD,YAA+B,WAAc,UAA8B;AACvE,QAAG,CAAC,KAAK,UAAU,IAAI,SAAmB,GAAE;AACxC,WAAK,UAAU,IAAI,WAAqB,CAAC,CAAC;AAAA,IAC9C;AAEA,SAAK,UAAU,IAAI,SAAmB,EAAG,KAAK,QAAQ;AAAA,EAC1D;AAAA,EAEA,eAAkC,WAAc,UAA8B;AAC1E,QAAG,KAAK,UAAU,IAAI,SAAmB,GAAE;AACvC,YAAM,OAAO,KAAK,UAAU,IAAI,SAAmB;AAEnD,YAAM,QAAQ,KAAM,UAAU,OAAK,MAAM,QAAQ;AAEjD,UAAG,UAAU,IAAG;AACZ,aAAM,OAAO,OAAO,CAAC;AAAA,MACzB;AAAA,IAEJ;AAAA,EACJ;AAAA,EAEU,SAA4B,WAAc,MAAW;AAC3D,QAAG,KAAK,UAAU,IAAI,SAAmB,GAAE;AACvC,YAAM,OAAO,KAAK,UAAU,IAAI,SAAmB;AAEnD,iBAAU,KAAK,MAAM;AACjB,UAAE,IAAI;AAAA,MACV;AAAA,IAEJ;AAAA,EACJ;AACJ;;;AChCA,YAAY,qBAAqB;;;ACH1B,SAAS,mBAAkB;AAC9B,SAAO,QAAQ,IAAI,aAAa;AACpC;;;ACAO,SAAS,iBAAgB;AAC5B,SAAO,iBAAiB;AAC5B;AAEO,SAAS,YAAY,MAAU;AAClC,MAAG,eAAe,GAAE;AAChB,YAAQ,MAAM,MAAM,MAAM,IAAI;AAAA,EAClC;AACJ;AAEO,SAAS,cAAc,MAAU;AACpC,MAAG,eAAe,GAAE;AAChB,YAAQ,KAAK,MAAM,MAAM,IAAI;AAAA,EACjC;AACJ;;;AFSO,SAAS,qBAAqB,MAA+B,eAAwB;AAExF,QAAM,SAAS,eAAe,MAAM,aAAa;AAEjD,QAAM,eAAwD,CAAC;AAC/D,aAAU,SAAS,QAAO;AACtB,QAAI;AACA,mBAAa,KAAK,gBAAgB,KAAK,CAAC;AAAA,IAC5C,SAAS,KAAI;AACT,UAAG,eAAc;AAEb,mBAAW,0BAA0B,MAAM,QAAQ,MAAM,KAAK;AAAA,MAClE,OAAO;AACH,cAAM;AAAA,MACV;AAAA,IACJ;AAAA,EACJ;AACA,SAAO;AACX;AAGA,IAAM,cAAc;AAEpB,IAAM,oCAAoE;AAC1E,IAAM,yCAAwE;AAC9E,IAAM,wCAAqE;AAC3E,IAAM,kCAAgE;AAGtE,IAAM,qCAA6E;AAAA,EAC/E,IAAM,IAAI,IAAI;AAAA,EACd,IAAM,IAAI,IAAI;AAAA,EACd,IAAM,IAAI,IAAI;AAAA,EACd,IAAM,IAAI,IAAI;AAAA;AAClB;AAQA,IAAM,2BAAiF;AAAA,EACnF,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AACV;AAGA,IAAM,cAAN,cAA0B,MAAM;AAAA,EAC5B,YAAY,SAAgB;AACxB,UAAM,OAAO;AAAA,EACjB;AACJ;AAEA,IAAM,2BAAN,cAAuC,MAAM;AAAA,EACzC,YAAY,SAAiB,WAAkB;AAC3C,UAAM,UAAU,cAAc,SAAS;AAAA,EAC3C;AACJ;AAOO,SAAS,eAAe,OAAgC,eAAwB;AASnF,QAAM,SAAqD,CAAC;AAE5D,MAAG,MAAM,CAAC,MAAM,aAAY;AACxB,QAAG,eAAc;AACb,aAAO,CAAC;AAAA,IACZ,OAAO;AACH,YAAM,IAAI,yBAAyB,oCAAoC,CAAC;AAAA,IAC5E;AAAA,EACJ;AAEA,MAAI,oBAA4B,CAAC;AACjC,MAAI;AAEJ,WAAQ,YAAY,GAAG,YAAY,MAAM,QAAQ,aAAY;AACzD,UAAM,OAAO,MAAM,SAAS;AAE5B,QAAG,kBAAkB,WAAW,KAAK,OAAO,uBAAuB,YAAY,SAAS,aAAY;AAChG,UAAG,eAAc;AACb;AAAA,MACJ,OAAO;AACH,cAAM,IAAI,yBAAyB,6CAA6C,SAAS;AAAA,MAC7F;AAAA,IACJ;AAEA,QAAG,SAAS,aAAY;AACpB,YAAM,gBAAgB,YAAU;AAChC,UAAG,MAAM,UAAU,eAAc;AAC7B,YAAG,eAAc;AACb;AAAA,QACJ,OAAO;AACH,gBAAM,IAAI,yBAAyB,0CAA0C,SAAS;AAAA,QAC1F;AAAA,MACJ;AAEA,YAAM,WAAW,MAAM,aAAa;AAEpC,UAAG,aAAa,aAAY;AACxB,0BAAkB,KAAK,QAAQ;AAAA,MACnC,WAAU,aAAa,qCAAqC,aAAa,0CAA0C,aAAa,yCAAyC,aAAa,iCAAgC;AAGlN,YAAG,kBAAkB,SAAS,GAAE;AAC5B,8BAAoB;AAAA,QACxB;AAEA,6BAAqB;AAAA,MACzB,OAAO;AACH,YAAG,eAAc;AACb,4BAAkB,KAAK,QAAQ;AAAA,QACnC,OAAO;AACH,gBAAM,IAAI,yBAAyB,iDAAiD,YAAU,KAAK,4BAA4B,SAAS;AAAA,QAC5I;AAAA,MACJ;AAEA,mBAAa;AAAA,IACjB,OAAO;AACH,wBAAkB,KAAK,IAAI;AAAA,IAC/B;AAAA,EACJ;AAEA,MAAG,kBAAkB,SAAS,GAAE;AAC5B,wBAAoB;AAAA,EACxB;AAEA,WAAS,sBAAqB;AAC1B,QAAG,OAAO,uBAAuB,UAAS;AACtC,UAAG,eAAc;AACb,gBAAQ;AACR;AAAA,MACJ,OAAO;AACH,cAAM,IAAI,YAAY,uDAAuD;AAAA,MACjF;AAAA,IACJ;AAEA,UAAM,6BAA6B,mCAAmC,kBAAkB;AACxF,QAAG,kBAAkB,WAAW,4BAA2B;AACvD,UAAG,eAAc;AACb,gBAAQ;AACR;AAAA,MACJ,OAAO;AACH,iBAAS,mBAAmB,OAAO,KAAK,iBAAiB,EAAE,SAAS,OAAO,CAAC;AAC5E,cAAM,IAAI,YAAY,iDAAiD,OAAO,aAAa,kBAAkB,IAAI,QAAQ,kBAAkB,SAAS,gBAAgB,6BAA6B,GAAG;AAAA,MACxM;AAAA,IACJ;AAEA,QAAI,kBAAkB;AACtB,eAAU,QAAQ,mBAAkB;AAChC,UAAG,SAAS,GAAE;AACV,0BAAkB;AAClB;AAAA,MACJ;AAAA,IACJ;AAGA,QAAG,CAAC,iBAAgB;AAChB,aAAO,KAAK;AAAA,QACR,QAAQ;AAAA,QACR,OAAO;AAAA,MACX,CAAC;AAAA,IACL;AAEA,YAAQ;AAER,aAAS,UAAS;AAEd,0BAAoB,CAAC;AACrB,2BAAqB;AAAA,IACzB;AAAA,EACJ;AAEA,SAAO;AACX;AAGO,SAAS,gBAAgB,OAA0F;AAEtH,QAAM,YAAY,MAAM,MAAM,MAAM,CAAC;AACrC,QAAM,gBAAgC,qBAAK,eAAe,4CAA4D,qBAAK,eAAe,qBAAqB,MAAM,MAAM,MAAM,GAAG,CAAC,CAAC,GAAG,IAAE,GAAG,eAAe;AAC7M,QAAM,cAAc,MAAM,MAAM,CAAC;AAEjC,MAAG,MAAM,SAAS,MAAM,MAAM,SAAS,IAAG;AACtC,UAAM,IAAI,MAAM,8BAA8B,MAAM,MAAM;AAAA,EAC9D;AAEA,SAAO;AAAA,IACH,MAAM,yBAAyB,MAAM,MAAM;AAAA,IAC3C;AAAA,IACA;AAAA,IACA;AAAA,EACJ;AACJ;;;AGrOA,SAAS,cAAc,WAAW,YAAY,oCAA6C;AAsC3F,IAAM,0BAA0B,MAAO,KAAK;AAErC,IAAM,eAAN,cAA2B,aAAiC;AAAA;AAAA;AAAA;AAAA,EAQ/D,cAAa;AACT,UAAM;AAPV,SAAiB,gBAAgB,oBAAI,IAA4C;AAS7E,SAAK,uBAAuB,YAAY,MAAI;AACxC,WAAK,aAAa;AAAA,IACtB,GAAG,MAAO,EAAE;AAAA,EAChB;AAAA,EAEA,OAAM;AACF,kBAAc,KAAK,oBAAoB;AACvC,SAAK,cAAc,MAAM;AAAA,EAC7B;AAAA;AAAA,EAGQ,eAAc;AAElB,UAAM,MAAM,KAAK,IAAI;AAErB,eAAU,iBAAiB,KAAK,eAAc;AAC1C,YAAM,WAAW,cAAc,CAAC;AAEhC,UAAG,MAAM,SAAS,uBAAuB,yBAAwB;AAC7D,cAAM,cAAc,cAAc,CAAC;AACnC,aAAK,cAAc,OAAO,WAAW;AAErC,aAAK,SAAS,sBAAsB,WAAW;AAAA,MACnD;AAAA,IACJ;AAAA,EACJ;AAAA,EAGA,gBAAgB,MAA8B;AAC1C,UAAM,SAAS,qBAAqB,MAAM,IAAI;AAE9C,eAAU,SAAS,QAAO;AACtB,WAAK,iBAAiB,KAAK;AAAA,IAC/B;AAAA,EACJ;AAAA,EAEQ,iBAAiB,OAA8C;AACnE,QAAG,MAAM,SAAS,QAAO;AACrB,UAAI;AACA,cAAM,cAAc,WAAW,MAAM,SAAS;AAC9C,cAAM,aAAa,UAAU,WAAW;AAExC,cAAM,mBAAmB,gBAAgB,YAAY,WAAW;AAEhE,cAAM,mBAAmB,KAAK,cAAc,IAAI,gBAAgB;AAChE,cAAM,WAA8B,oBAAoB;AAAA,UACpD,sBAAsB,KAAK,IAAI;AAAA,QACnC;AAEA,aAAK,cAAc,IAAI,kBAAkB,QAAQ;AAEjD,cAAM,gBAAgB,aAAa,UAAU;AAE7C,YAAG,cAAc,oBAAoB,mBAAkB;AACnD,cAAG,cAAc,oBAAoB,SAAS,WAAW,QAAO;AAC5D,qBAAS,0BAA0B;AAAA,cAC/B,cAAc,KAAK,IAAI;AAAA;AAAA,cACvB,KAAK,cAAc,oBAAoB;AAAA,YAC3C;AAAA,UACJ,OAAO;AACH,qBAAS,yBAAyB;AAAA,cAC9B,cAAc,KAAK,IAAI;AAAA;AAAA,cACvB,KAAK,cAAc,oBAAoB;AAAA,YAC3C;AAAA,UACJ;AAAA,QACJ;AAEA,YAAG,SAAS,2BAA2B,SAAS,wBAAuB;AACnE,cAAG,KAAK,IAAI,SAAS,wBAAwB,eAAe,SAAS,uBAAuB,YAAY,IAAI,MAAO,KAAK,GAAE;AAEtH,kBAAM,eAAe,SAAS,wBAAwB,eAAe,SAAS,uBAAuB,eAAe,SAAS,wBAAwB,MAAM,SAAS,uBAAuB;AAE3L,kBAAM,oBAAoB,SAAS,wBAAwB,eAAe,SAAS,uBAAuB,eAAe,SAAS,uBAAuB,MAAM,SAAS,wBAAwB;AAEhM,kBAAM,sBAAsB,6BAA6B,CAAC,cAAc,iBAAiB,CAAC;AAE1F,qBAAS,0BAA0B;AAEnC,iBAAK,SAAS,iCAAiC;AAAA,cAC3C,aAAa;AAAA,cACb,UAAU;AAAA,YACd,CAAC;AAAA,UACL;AAAA,QACJ;AAIA,YAAG,CAAC,kBAAiB;AACjB,eAAK,SAAS,gBAAgB;AAAA,YAC1B,aAAa;AAAA,UACjB,CAAC;AAAA,QACL,OAAO;AACH,cAAG,CAAC,SAAS,yBAAwB;AACjC,iBAAK,SAAS,mBAAmB;AAAA,cAC7B,aAAa;AAAA,YACjB,CAAC;AAAA,UACL;AAAA,QACJ;AAAA,MAEJ,SAAS,KAAI;AACT,aAAK,SAAS,WAAW;AAAA,UACrB,SAAS;AAAA,UACT,SAAS;AAAA,QACb,CAAC;AAAA,MACL;AAAA,IACJ,WAAU,MAAM,SAAS,UAAU;AAC/B,UAAI;AACA,cAAM,cAAc,WAAW,MAAM,SAAS;AAE9C,cAAM,mBAAmB,gBAAgB,YAAY,WAAW;AAEhE,cAAM,mBAAmB,KAAK,cAAc,IAAI,gBAAgB;AAChE,cAAM,WAA8B,oBAAoB;AAAA,UACpD,sBAAsB,KAAK,IAAI;AAAA,QACnC;AAEA,aAAK,cAAc,IAAI,kBAAkB,QAAQ;AAEjD,YAAG,kBAAiB;AAChB,eAAK,SAAS,mBAAmB;AAAA,YAC7B,aAAa;AAAA,UACjB,CAAC;AAAA,QACL,OAAO;AACH,eAAK,SAAS,gBAAgB;AAAA,YAC1B,aAAa;AAAA,UACjB,CAAC;AAAA,QACL;AAAA,MAEJ,SAAS,KAAI;AACT,aAAK,SAAS,WAAW;AAAA,UACrB,SAAS;AAAA,UACT,SAAS;AAAA,QACb,CAAC;AAAA,MACL;AAAA,IACJ,OAAO;AACH,WAAK,SAAS,WAAW;AAAA,QACrB,SAAS;AAAA,QACT,SAAS,MAAM;AAAA,MACnB,CAAC;AAAA,IACL;AAAA,EACJ;AAEJ;AAEA,SAAS,gBAAgB,GAAU;AAC/B,SAAO,EAAE,SAAS,EAAE;AACxB;","names":[]}