{"version":3,"file":"index.mjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["import {\n  createRelayManager,\n  createStrategy,\n  entries,\n  fromJson,\n  genId,\n  getRelays,\n  keys,\n  libName,\n  makeSocket,\n  pauseRelayReconnection,\n  resumeRelayReconnection,\n  selfId,\n  sha1,\n  toJson,\n  type JoinRoom,\n  type JoinRoomConfig,\n  type OfferRecord,\n  type SocketClient\n} from '@trystero-p2p/core'\n\nconst relayManager = createRelayManager<SocketClient>(client => client.socket)\nconst topicToInfoHash: Record<string, string> = {}\nconst infoHashToTopic: Record<string, string> = {}\nconst announceIntervals = relayManager.scoped<ReturnType<typeof setInterval>>()\nconst announceFns = relayManager.scoped<() => void | Promise<void>>()\nconst subscriptionTokens = relayManager.scoped<symbol>()\nconst trackerAnnounceMs: Record<string, number> = {}\nconst handledSignals: Record<string, number> = {}\nconst msgHandlers = relayManager.scoped<(data: TrackerMessage) => void>()\nconst topicStates = relayManager.scoped<{\n  announce: () => void | Promise<void>\n  isActive: boolean\n}>()\nconst roomOutstandingOffers: Record<\n  string,\n  Record<string, OfferRecord & {createdAt: number}>\n> = {}\nconst roomOfferGenerationPromises: Record<string, Promise<void> | undefined> =\n  {}\nconst roomSubscriberCounts: Record<string, number> = {}\nconst trackerAction = 'announce'\nconst hashLimit = 20\nconst offerPoolSize = 3\nconst defaultAnnounceMs = 10_000\nconst dormantAnnounceMs = 120_000\nconst maxAnnounceMs = 20_000\nconst offerRetentionMs = 120_000\nconst signalDedupeWindowMs = 4_000\nconst defaultRedundancy = 3\n\nexport type TorrentRoomConfig = JoinRoomConfig\n\ntype TrackerMessage = {\n  offer?: {\n    type: 'offer'\n    sdp: string\n  }\n  answer?: {\n    type: 'answer'\n    sdp: string\n  }\n  offer_id?: string\n  peer_id?: string\n  info_hash?: string\n  interval?: number\n  ['failure reason']?: string\n  ['warning message']?: string\n}\n\nconst getInfoHash = async (topic: string): Promise<string> => {\n  if (topicToInfoHash[topic]) {\n    return topicToInfoHash[topic]\n  }\n\n  const hash = (await sha1(topic)).slice(0, hashLimit)\n  topicToInfoHash[topic] = hash\n  infoHashToTopic[hash] = topic\n\n  return hash\n}\n\nconst send = async (\n  client: SocketClient,\n  topic: string,\n  payload: Record<string, unknown>\n): Promise<void> =>\n  client.send(\n    toJson({\n      action: trackerAction,\n      info_hash: await getInfoHash(topic),\n      peer_id: selfId,\n      ...payload\n    })\n  )\n\nconst warn = (url: string, msg: string, didFail = false): void =>\n  console.warn(\n    `${libName}: torrent tracker ${didFail ? 'failure' : 'warning'} from ${url} - ${msg}`\n  )\n\nconst getRoomOutstandingOffers = (\n  rootTopic: string\n): Record<string, OfferRecord & {createdAt: number}> =>\n  (roomOutstandingOffers[rootTopic] ??= {})\n\nconst deleteRoomOfferBookkeeping = (rootTopic: string): void => {\n  delete roomOutstandingOffers[rootTopic]\n  delete roomOfferGenerationPromises[rootTopic]\n}\n\nconst claimOutstandingOffer = (\n  rootTopic: string,\n  offerId: string\n): OfferRecord | undefined => {\n  const outstandingOffers = roomOutstandingOffers[rootTopic]\n  const offer = outstandingOffers?.[offerId]\n\n  if (!offer) {\n    return\n  }\n\n  delete outstandingOffers[offerId]\n  offer.claim?.()\n\n  if (!keys(outstandingOffers).length && !roomSubscriberCounts[rootTopic]) {\n    deleteRoomOfferBookkeeping(rootTopic)\n  }\n\n  return offer\n}\n\nconst reclaimOutstandingOffer = (rootTopic: string, offerId: string): void => {\n  const outstandingOffers = roomOutstandingOffers[rootTopic]\n  const offer = outstandingOffers?.[offerId]\n\n  if (!offer) {\n    return\n  }\n\n  delete outstandingOffers[offerId]\n  offer.reclaim?.()\n\n  if (!keys(outstandingOffers).length && !roomSubscriberCounts[rootTopic]) {\n    deleteRoomOfferBookkeeping(rootTopic)\n  }\n}\n\nconst reclaimAllOutstandingOffers = (rootTopic: string): void => {\n  keys(getRoomOutstandingOffers(rootTopic)).forEach(offerId =>\n    reclaimOutstandingOffer(rootTopic, offerId)\n  )\n  deleteRoomOfferBookkeeping(rootTopic)\n}\n\nconst pruneOutstandingOffers = (rootTopic: string): void => {\n  const now = Date.now()\n\n  entries(getRoomOutstandingOffers(rootTopic)).forEach(([offerId, offer]) => {\n    if (now - offer.createdAt > offerRetentionMs) {\n      reclaimOutstandingOffer(rootTopic, offerId)\n    }\n  })\n}\n\nconst ensureOutstandingOffers = async (\n  rootTopic: string,\n  getOffers: (n: number) => Promise<OfferRecord[]>\n): Promise<Record<string, OfferRecord & {createdAt: number}>> => {\n  while (roomOfferGenerationPromises[rootTopic]) {\n    await roomOfferGenerationPromises[rootTopic]\n  }\n\n  const nextPromise = (async () => {\n    pruneOutstandingOffers(rootTopic)\n\n    const outstandingOffers = getRoomOutstandingOffers(rootTopic)\n    const outstandingCount = keys(outstandingOffers).length\n    const missingOffers = Math.max(0, offerPoolSize - outstandingCount)\n\n    if (missingOffers > 0) {\n      ;(await getOffers(missingOffers)).forEach(peerAndOffer => {\n        outstandingOffers[genId(hashLimit)] = {\n          ...peerAndOffer,\n          createdAt: Date.now()\n        }\n      })\n    }\n  })().finally(() => {\n    if (roomOfferGenerationPromises[rootTopic] === nextPromise) {\n      delete roomOfferGenerationPromises[rootTopic]\n    }\n  })\n\n  roomOfferGenerationPromises[rootTopic] = nextPromise\n  await nextPromise\n\n  return getRoomOutstandingOffers(rootTopic)\n}\n\nconst joinRoomStrategy: JoinRoom<TorrentRoomConfig> = createStrategy({\n  init: config =>\n    getRelays(config, defaultRelayUrls, defaultRedundancy).map(rawUrl => {\n      const client = relayManager.register(rawUrl, () =>\n        makeSocket(rawUrl, rawData => {\n          const data = fromJson<TrackerMessage>(rawData)\n          const errMsg = data['failure reason']\n          const warnMsg = data['warning message']\n          const {interval} = data\n          const topic = data.info_hash\n            ? infoHashToTopic[data.info_hash]\n            : undefined\n\n          if (errMsg) {\n            warn(client.url, errMsg, true)\n            return\n          }\n\n          if (warnMsg) {\n            warn(client.url, warnMsg)\n          }\n\n          if (\n            interval &&\n            interval * 1000 >\n              (trackerAnnounceMs[client.url] ?? defaultAnnounceMs) &&\n            topic &&\n            announceFns.forKey(rawUrl)[topic]\n          ) {\n            const nextInterval = Math.min(interval * 1000, maxAnnounceMs)\n            const relayIntervals = announceIntervals.forKey(rawUrl)\n            const relayFns = announceFns.forKey(rawUrl)\n\n            if (relayIntervals[topic]) {\n              clearInterval(relayIntervals[topic])\n            }\n            trackerAnnounceMs[client.url] = nextInterval\n            const relayFn = relayFns[topic]\n\n            if (relayFn) {\n              relayIntervals[topic] = setInterval(() => {\n                void relayFn()\n              }, nextInterval)\n            }\n          }\n\n          if ((data.offer || data.answer) && topic && data.offer_id) {\n            if (data.peer_id === selfId) {\n              return\n            }\n\n            const signalType = data.offer ? 'offer' : 'answer'\n            const signalKey = `${topic}:${signalType}:${data.offer_id}:${data.peer_id ?? ''}`\n            const nowMs = Date.now()\n            const lastHandledMs = handledSignals[signalKey]\n\n            if (\n              typeof lastHandledMs === 'number' &&\n              nowMs - lastHandledMs < signalDedupeWindowMs\n            ) {\n              return\n            }\n\n            handledSignals[signalKey] = nowMs\n\n            entries(handledSignals).forEach(([key, handledAtMs]) => {\n              if (nowMs - handledAtMs > signalDedupeWindowMs * 6) {\n                delete handledSignals[key]\n              }\n            })\n\n            msgHandlers.forKey(rawUrl)[topic]?.(data)\n          }\n        })\n      )\n\n      return client.ready\n    }),\n\n  subscribe: (client, rootTopic, _, onMessage, getOffers, context) => {\n    const handlers = msgHandlers.forRelay(client)\n    const relayFns = announceFns.forRelay(client)\n    const relayIntervals = announceIntervals.forRelay(client)\n    const activeTokens = subscriptionTokens.forRelay(client)\n    const states = topicStates.forRelay(client)\n    const subscriptionToken = Symbol(rootTopic)\n\n    activeTokens[rootTopic] = subscriptionToken\n    roomSubscriberCounts[rootTopic] = (roomSubscriberCounts[rootTopic] ?? 0) + 1\n\n    const topicHandler = (data: TrackerMessage): void => {\n      if (data.offer && data.peer_id && data.offer_id) {\n        void onMessage(\n          rootTopic,\n          {\n            offer: data.offer.sdp,\n            peerId: data.peer_id,\n            hasOutgoingOffer:\n              keys(getRoomOutstandingOffers(rootTopic)).length > 0\n          },\n          (_, signal) =>\n            void send(client, rootTopic, {\n              answer: {\n                type: 'answer',\n                sdp: fromJson<{answer: string}>(signal).answer\n              },\n              offer_id: data.offer_id,\n              to_peer_id: data.peer_id\n            })\n        )\n      } else if (data.answer && data.offer_id && data.peer_id) {\n        const offer = claimOutstandingOffer(rootTopic, data.offer_id)\n\n        if (offer) {\n          void onMessage(\n            rootTopic,\n            {\n              answer: data.answer.sdp,\n              peerId: data.peer_id,\n              peer: offer.peer\n            },\n            () => {}\n          )\n        }\n      }\n    }\n\n    handlers[rootTopic] = topicHandler\n\n    const topicState: {\n      announce: () => void | Promise<void>\n      isActive: boolean\n    } = {\n      announce: async () => {\n        if (activeTokens[rootTopic] !== subscriptionToken) {\n          return\n        }\n\n        if (!topicState.isActive) {\n          void send(client, rootTopic, {\n            left: 0,\n            numwant: offerPoolSize,\n            offers: []\n          })\n          return\n        }\n\n        const outstandingOffers = await ensureOutstandingOffers(\n          rootTopic,\n          getOffers\n        )\n        const offers = entries(outstandingOffers).map(([id, {offer}]) => ({\n          offer_id: id,\n          offer: {type: 'offer', sdp: offer}\n        }))\n\n        void send(client, rootTopic, {\n          numwant: offerPoolSize,\n          offers\n        })\n      },\n      isActive: !context?.isPassive\n    }\n\n    trackerAnnounceMs[client.url] = defaultAnnounceMs\n    const {announce} = topicState\n    relayFns[rootTopic] = announce\n    states[rootTopic] = topicState\n    const initialInterval = topicState.isActive\n      ? trackerAnnounceMs[client.url]\n      : dormantAnnounceMs\n\n    relayIntervals[rootTopic] = setInterval(announce, initialInterval)\n    void announce()\n\n    return () => {\n      roomSubscriberCounts[rootTopic] = Math.max(\n        0,\n        (roomSubscriberCounts[rootTopic] ?? 1) - 1\n      )\n\n      if (!roomSubscriberCounts[rootTopic]) {\n        delete roomSubscriberCounts[rootTopic]\n      }\n\n      if (activeTokens[rootTopic] !== subscriptionToken) {\n        if (!roomSubscriberCounts[rootTopic]) {\n          reclaimAllOutstandingOffers(rootTopic)\n          delete states[rootTopic]\n        }\n\n        return\n      }\n\n      const interval = relayIntervals[rootTopic]\n      if (interval) {\n        clearInterval(interval)\n        delete relayIntervals[rootTopic]\n      }\n\n      if (handlers[rootTopic] === topicHandler) {\n        delete handlers[rootTopic]\n      }\n\n      if (relayFns[rootTopic] === announce) {\n        delete relayFns[rootTopic]\n      }\n\n      delete activeTokens[rootTopic]\n\n      if (states[rootTopic] === topicState) {\n        delete states[rootTopic]\n      }\n\n      if (!roomSubscriberCounts[rootTopic]) {\n        reclaimAllOutstandingOffers(rootTopic)\n      }\n    }\n  },\n\n  announce: (client, rootTopic) => {\n    const state = topicStates.forRelay(client)[rootTopic]\n    const relayIntervals = announceIntervals.forRelay(client)\n    const relayFns = announceFns.forRelay(client)\n    const fn = relayFns[rootTopic]\n\n    if (state) {\n      state.isActive = true\n    }\n\n    if (fn && relayIntervals[rootTopic]) {\n      clearInterval(relayIntervals[rootTopic])\n      relayIntervals[rootTopic] = setInterval(() => {\n        void fn()\n      }, trackerAnnounceMs[client.url])\n      void fn()\n    }\n\n    return trackerAnnounceMs[client.url]\n  },\n\n  deactivate: (client, rootTopic) => {\n    const state = topicStates.forRelay(client)[rootTopic]\n    const relayIntervals = announceIntervals.forRelay(client)\n    const relayFns = announceFns.forRelay(client)\n    const fn = relayFns[rootTopic]\n\n    if (state) {\n      state.isActive = false\n    }\n\n    reclaimAllOutstandingOffers(rootTopic)\n\n    if (fn && relayIntervals[rootTopic]) {\n      clearInterval(relayIntervals[rootTopic])\n      relayIntervals[rootTopic] = setInterval(() => {\n        void fn()\n      }, dormantAnnounceMs)\n      void fn()\n    }\n  }\n})\n\nexport const joinRoom: JoinRoom<TorrentRoomConfig> = (\n  config,\n  roomId,\n  callbacks\n) =>\n  joinRoomStrategy(\n    {\n      ...config,\n      trickleIce: config.trickleIce ?? false\n    },\n    roomId,\n    callbacks\n  )\n\nexport const getRelaySockets = relayManager.getSockets\n\nexport {pauseRelayReconnection, resumeRelayReconnection, selfId}\n\nexport const defaultRelayUrls = [\n  'tracker.webtorrent.dev',\n  'tracker.openwebtorrent.com',\n  'tracker.btorrent.xyz',\n  'tracker.files.fm:7073/announce'\n].map(url => 'wss://' + url)\n\nexport type * from '@trystero-p2p/core'\n"],"mappings":";;AAqBA,MAAM,eAAe,oBAAiC,WAAU,OAAO,MAAM;AAC7E,MAAM,kBAA0C,CAAC;AACjD,MAAM,kBAA0C,CAAC;AACjD,MAAM,oBAAoB,aAAa,OAAuC;AAC9E,MAAM,cAAc,aAAa,OAAmC;AACpE,MAAM,qBAAqB,aAAa,OAAe;AACvD,MAAM,oBAA4C,CAAC;AACnD,MAAM,iBAAyC,CAAC;AAChD,MAAM,cAAc,aAAa,OAAuC;AACxE,MAAM,cAAc,aAAa,OAG9B;AACH,MAAM,wBAGF,CAAC;AACL,MAAM,8BACJ,CAAC;AACH,MAAM,uBAA+C,CAAC;AACtD,MAAM,gBAAgB;AACtB,MAAM,YAAY;AAClB,MAAM,gBAAgB;AACtB,MAAM,oBAAoB;AAC1B,MAAM,oBAAoB;AAC1B,MAAM,gBAAgB;AACtB,MAAM,mBAAmB;AACzB,MAAM,uBAAuB;AAC7B,MAAM,oBAAoB;AAqB1B,MAAM,cAAc,OAAO,UAAmC;CAC5D,IAAI,gBAAgB,QAClB,OAAO,gBAAgB;CAGzB,MAAM,QAAQ,MAAM,KAAK,KAAK,EAAA,CAAG,MAAM,GAAG,SAAS;CACnD,gBAAgB,SAAS;CACzB,gBAAgB,QAAQ;CAExB,OAAO;AACT;AAEA,MAAM,OAAO,OACX,QACA,OACA,YAEA,OAAO,KACL,OAAO;CACL,QAAQ;CACR,WAAW,MAAM,YAAY,KAAK;CAClC,SAAS;CACT,GAAG;AACL,CAAC,CACH;AAEF,MAAM,QAAQ,KAAa,KAAa,UAAU,UAChD,QAAQ,KACN,GAAG,QAAQ,oBAAoB,UAAU,YAAY,UAAU,QAAQ,IAAI,KAAK,KAClF;AAEF,MAAM,4BACJ,cAEC,sBAAsB,eAAe,CAAC;AAEzC,MAAM,8BAA8B,cAA4B;CAC9D,OAAO,sBAAsB;CAC7B,OAAO,4BAA4B;AACrC;AAEA,MAAM,yBACJ,WACA,YAC4B;CAC5B,MAAM,oBAAoB,sBAAsB;CAChD,MAAM,QAAQ,oBAAoB;CAElC,IAAI,CAAC,OACH;CAGF,OAAO,kBAAkB;CACzB,MAAM,QAAQ;CAEd,IAAI,CAAC,KAAK,iBAAiB,CAAC,CAAC,UAAU,CAAC,qBAAqB,YAC3D,2BAA2B,SAAS;CAGtC,OAAO;AACT;AAEA,MAAM,2BAA2B,WAAmB,YAA0B;CAC5E,MAAM,oBAAoB,sBAAsB;CAChD,MAAM,QAAQ,oBAAoB;CAElC,IAAI,CAAC,OACH;CAGF,OAAO,kBAAkB;CACzB,MAAM,UAAU;CAEhB,IAAI,CAAC,KAAK,iBAAiB,CAAC,CAAC,UAAU,CAAC,qBAAqB,YAC3D,2BAA2B,SAAS;AAExC;AAEA,MAAM,+BAA+B,cAA4B;CAC/D,KAAK,yBAAyB,SAAS,CAAC,CAAC,CAAC,SAAQ,YAChD,wBAAwB,WAAW,OAAO,CAC5C;CACA,2BAA2B,SAAS;AACtC;AAEA,MAAM,0BAA0B,cAA4B;CAC1D,MAAM,MAAM,KAAK,IAAI;CAErB,QAAQ,yBAAyB,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,SAAS,WAAW;EACzE,IAAI,MAAM,MAAM,YAAY,kBAC1B,wBAAwB,WAAW,OAAO;CAE9C,CAAC;AACH;AAEA,MAAM,0BAA0B,OAC9B,WACA,cAC+D;CAC/D,OAAO,4BAA4B,YACjC,MAAM,4BAA4B;CAGpC,MAAM,eAAe,YAAY;EAC/B,uBAAuB,SAAS;EAEhC,MAAM,oBAAoB,yBAAyB,SAAS;EAC5D,MAAM,mBAAmB,KAAK,iBAAiB,CAAC,CAAC;EACjD,MAAM,gBAAgB,KAAK,IAAI,GAAG,gBAAgB,gBAAgB;EAElE,IAAI,gBAAgB,GACjB,CAAC,MAAM,UAAU,aAAa,EAAA,CAAG,SAAQ,iBAAgB;GACxD,kBAAkB,MAAM,SAAS,KAAK;IACpC,GAAG;IACH,WAAW,KAAK,IAAI;GACtB;EACF,CAAC;CAEL,EAAA,CAAG,CAAC,CAAC,cAAc;EACjB,IAAI,4BAA4B,eAAe,aAC7C,OAAO,4BAA4B;CAEvC,CAAC;CAED,4BAA4B,aAAa;CACzC,MAAM;CAEN,OAAO,yBAAyB,SAAS;AAC3C;AAEA,MAAM,mBAAgD,eAAe;CACnE,OAAM,WACJ,UAAU,QAAQ,kBAAkB,iBAAiB,CAAC,CAAC,KAAI,WAAU;EACnE,MAAM,SAAS,aAAa,SAAS,cACnC,WAAW,SAAQ,YAAW;GAC5B,MAAM,OAAO,SAAyB,OAAO;GAC7C,MAAM,SAAS,KAAK;GACpB,MAAM,UAAU,KAAK;GACrB,MAAM,EAAC,aAAY;GACnB,MAAM,QAAQ,KAAK,YACf,gBAAgB,KAAK,aACrB,KAAA;GAEJ,IAAI,QAAQ;IACV,KAAK,OAAO,KAAK,QAAQ,IAAI;IAC7B;GACF;GAEA,IAAI,SACF,KAAK,OAAO,KAAK,OAAO;GAG1B,IACE,YACA,WAAW,OACR,kBAAkB,OAAO,QAAQ,sBACpC,SACA,YAAY,OAAO,MAAM,CAAC,CAAC,QAC3B;IACA,MAAM,eAAe,KAAK,IAAI,WAAW,KAAM,aAAa;IAC5D,MAAM,iBAAiB,kBAAkB,OAAO,MAAM;IACtD,MAAM,WAAW,YAAY,OAAO,MAAM;IAE1C,IAAI,eAAe,QACjB,cAAc,eAAe,MAAM;IAErC,kBAAkB,OAAO,OAAO;IAChC,MAAM,UAAU,SAAS;IAEzB,IAAI,SACF,eAAe,SAAS,kBAAkB;KACxC,QAAa;IACf,GAAG,YAAY;GAEnB;GAEA,KAAK,KAAK,SAAS,KAAK,WAAW,SAAS,KAAK,UAAU;IACzD,IAAI,KAAK,YAAY,QACnB;IAIF,MAAM,YAAY,GAAG,MAAM,GADR,KAAK,QAAQ,UAAU,SACD,GAAG,KAAK,SAAS,GAAG,KAAK,WAAW;IAC7E,MAAM,QAAQ,KAAK,IAAI;IACvB,MAAM,gBAAgB,eAAe;IAErC,IACE,OAAO,kBAAkB,YACzB,QAAQ,gBAAgB,sBAExB;IAGF,eAAe,aAAa;IAE5B,QAAQ,cAAc,CAAC,CAAC,SAAS,CAAC,KAAK,iBAAiB;KACtD,IAAI,QAAQ,cAAc,uBAAuB,GAC/C,OAAO,eAAe;IAE1B,CAAC;IAED,YAAY,OAAO,MAAM,CAAC,CAAC,MAAM,GAAG,IAAI;GAC1C;EACF,CAAC,CACH;EAEA,OAAO,OAAO;CAChB,CAAC;CAEH,YAAY,QAAQ,WAAW,GAAG,WAAW,WAAW,YAAY;EAClE,MAAM,WAAW,YAAY,SAAS,MAAM;EAC5C,MAAM,WAAW,YAAY,SAAS,MAAM;EAC5C,MAAM,iBAAiB,kBAAkB,SAAS,MAAM;EACxD,MAAM,eAAe,mBAAmB,SAAS,MAAM;EACvD,MAAM,SAAS,YAAY,SAAS,MAAM;EAC1C,MAAM,oBAAoB,OAAO,SAAS;EAE1C,aAAa,aAAa;EAC1B,qBAAqB,cAAc,qBAAqB,cAAc,KAAK;EAE3E,MAAM,gBAAgB,SAA+B;GACnD,IAAI,KAAK,SAAS,KAAK,WAAW,KAAK,UACrC,UACE,WACA;IACE,OAAO,KAAK,MAAM;IAClB,QAAQ,KAAK;IACb,kBACE,KAAK,yBAAyB,SAAS,CAAC,CAAC,CAAC,SAAS;GACvD,IACC,GAAG,WACF,KAAK,KAAK,QAAQ,WAAW;IAC3B,QAAQ;KACN,MAAM;KACN,KAAK,SAA2B,MAAM,CAAC,CAAC;IAC1C;IACA,UAAU,KAAK;IACf,YAAY,KAAK;GACnB,CAAC,CACL;QACK,IAAI,KAAK,UAAU,KAAK,YAAY,KAAK,SAAS;IACvD,MAAM,QAAQ,sBAAsB,WAAW,KAAK,QAAQ;IAE5D,IAAI,OACF,UACE,WACA;KACE,QAAQ,KAAK,OAAO;KACpB,QAAQ,KAAK;KACb,MAAM,MAAM;IACd,SACM,CAAC,CACT;GAEJ;EACF;EAEA,SAAS,aAAa;EAEtB,MAAM,aAGF;GACF,UAAU,YAAY;IACpB,IAAI,aAAa,eAAe,mBAC9B;IAGF,IAAI,CAAC,WAAW,UAAU;KACxB,KAAU,QAAQ,WAAW;MAC3B,MAAM;MACN,SAAS;MACT,QAAQ,CAAC;KACX,CAAC;KACD;IACF;IAWA,KAAU,QAAQ,WAAW;KAC3B,SAAS;KACT,QAPa,QAAQ,MAJS,wBAC9B,WACA,SACF,CACwC,CAAC,CAAC,KAAK,CAAC,IAAI,EAAC,cAAa;MAChE,UAAU;MACV,OAAO;OAAC,MAAM;OAAS,KAAK;MAAK;KACnC,EAIO;IACP,CAAC;GACH;GACA,UAAU,CAAC,SAAS;EACtB;EAEA,kBAAkB,OAAO,OAAO;EAChC,MAAM,EAAC,aAAY;EACnB,SAAS,aAAa;EACtB,OAAO,aAAa;EACpB,MAAM,kBAAkB,WAAW,WAC/B,kBAAkB,OAAO,OACzB;EAEJ,eAAe,aAAa,YAAY,UAAU,eAAe;EACjE,SAAc;EAEd,aAAa;GACX,qBAAqB,aAAa,KAAK,IACrC,IACC,qBAAqB,cAAc,KAAK,CAC3C;GAEA,IAAI,CAAC,qBAAqB,YACxB,OAAO,qBAAqB;GAG9B,IAAI,aAAa,eAAe,mBAAmB;IACjD,IAAI,CAAC,qBAAqB,YAAY;KACpC,4BAA4B,SAAS;KACrC,OAAO,OAAO;IAChB;IAEA;GACF;GAEA,MAAM,WAAW,eAAe;GAChC,IAAI,UAAU;IACZ,cAAc,QAAQ;IACtB,OAAO,eAAe;GACxB;GAEA,IAAI,SAAS,eAAe,cAC1B,OAAO,SAAS;GAGlB,IAAI,SAAS,eAAe,UAC1B,OAAO,SAAS;GAGlB,OAAO,aAAa;GAEpB,IAAI,OAAO,eAAe,YACxB,OAAO,OAAO;GAGhB,IAAI,CAAC,qBAAqB,YACxB,4BAA4B,SAAS;EAEzC;CACF;CAEA,WAAW,QAAQ,cAAc;EAC/B,MAAM,QAAQ,YAAY,SAAS,MAAM,CAAC,CAAC;EAC3C,MAAM,iBAAiB,kBAAkB,SAAS,MAAM;EAExD,MAAM,KADW,YAAY,SAAS,MACpB,CAAC,CAAC;EAEpB,IAAI,OACF,MAAM,WAAW;EAGnB,IAAI,MAAM,eAAe,YAAY;GACnC,cAAc,eAAe,UAAU;GACvC,eAAe,aAAa,kBAAkB;IAC5C,GAAQ;GACV,GAAG,kBAAkB,OAAO,IAAI;GAChC,GAAQ;EACV;EAEA,OAAO,kBAAkB,OAAO;CAClC;CAEA,aAAa,QAAQ,cAAc;EACjC,MAAM,QAAQ,YAAY,SAAS,MAAM,CAAC,CAAC;EAC3C,MAAM,iBAAiB,kBAAkB,SAAS,MAAM;EAExD,MAAM,KADW,YAAY,SAAS,MACpB,CAAC,CAAC;EAEpB,IAAI,OACF,MAAM,WAAW;EAGnB,4BAA4B,SAAS;EAErC,IAAI,MAAM,eAAe,YAAY;GACnC,cAAc,eAAe,UAAU;GACvC,eAAe,aAAa,kBAAkB;IAC5C,GAAQ;GACV,GAAG,iBAAiB;GACpB,GAAQ;EACV;CACF;AACF,CAAC;AAED,MAAa,YACX,QACA,QACA,cAEA,iBACE;CACE,GAAG;CACH,YAAY,OAAO,cAAc;AACnC,GACA,QACA,SACF;AAEF,MAAa,kBAAkB,aAAa;AAI5C,MAAa,mBAAmB;CAC9B;CACA;CACA;CACA;AACF,CAAC,CAAC,KAAI,QAAO,WAAW,GAAG"}