{"version":3,"file":"src-D32LnYRN.cjs","names":["config: ProxyConfig","backendUrl: string","protocol: string","req: IncomingMessage","res: ServerResponse","WsWebSocket","client: Client","options: { requireOpenWebSocket?: boolean }","handler: (client: Client, data: T) => void | Promise<void>","data: T","data: { code?: number; reason?: string }","actualCode: number","actualReason: Buffer","base64Data: string","sessionId: string","encodedData: string","reason: 'sse-closed' | 'sse-aborted' | 'websocket-closed' | 'timeout'","originalRequestUrl: string","ws: WsWebSocket","ws","rawData: string"],"sources":["../src/index.ts"],"sourcesContent":["import WsWebSocket from 'ws'\nimport { IncomingMessage, ServerResponse, Server, createServer } from 'http'\nimport {\n  encodePingMessage,\n  encodeWebSocketConnectedMessage,\n  encodeDataMessage,\n  encodeBinaryDataMessage,\n  encodeWebSocketErrorMessage,\n  encodeWebSocketClosedMessage,\n  encodeSessionSecretMessage,\n} from './sse-protocol.js'\nimport { decodeMessageRequest, decodeBinaryData, isTextMessageRequest, isBinaryMessageRequest } from './messages-protocol.js'\n\nexport interface ProxyConfig {\n  port: number\n  allowedHosts: string[]\n  allowAnyLocalhostPort: boolean\n  dangerouslyAllowAnyHost?: boolean\n  keepaliveInterval?: number\n  connectionTimeout?: number\n}\n\ninterface Client {\n  sseResponse: ServerResponse\n  websocket: WsWebSocket\n  sessionId: string\n  sessionSecret: string\n  lastActivity: number\n}\n\nexport class SSEWebSocketProxy {\n  private clients = new Map<string, Client>()\n  private server: Server | null = null\n  private cleanupInterval: NodeJS.Timeout | null = null\n\n  constructor(private config: ProxyConfig) {\n    this.config = {\n      keepaliveInterval: 30000, // 30 seconds\n      connectionTimeout: 60000, // 60 seconds\n      ...config,\n    }\n  }\n\n  private isVerbose(): boolean {\n    return !!process.env.SSE_WS_PROXY_VERBOSE\n  }\n\n  private verboseLog(...args: any[]): void {\n    if (this.isVerbose()) {\n      console.log('[SSE-WS-PROXY-VERBOSE]', ...args)\n    }\n  }\n\n  private isBackendAllowed(backendUrl: string): boolean {\n    try {\n      const url = new URL(backendUrl)\n\n      // Allow any host if explicitly enabled\n      if (this.config.dangerouslyAllowAnyHost) {\n        console.warn(\n          `⚠️  SECURITY WARNING: dangerouslyAllowAnyHost is enabled - allowing connection to ${url.hostname}:${url.port || (url.protocol === 'wss:' ? 443 : 80)}`,\n        )\n        return true\n      }\n\n      // Check localhost/127.0.0.1 (any port allowed if enabled)\n      if (this.config.allowAnyLocalhostPort) {\n        const isLocalhost = url.hostname === 'localhost' || url.hostname === '127.0.0.1' || url.hostname === '::1'\n        if (isLocalhost) {\n          return true\n        }\n      }\n\n      // Check against allowed hosts (exact match including port)\n      const hostWithPort = url.port ? `${url.hostname}:${url.port}` : url.hostname\n      const hostWithoutPort = url.hostname\n\n      for (const allowedHost of this.config.allowedHosts) {\n        try {\n          const allowedUrl = new URL(allowedHost)\n          const allowedHostWithPort = allowedUrl.port ? `${allowedUrl.hostname}:${allowedUrl.port}` : allowedUrl.hostname\n          const allowedHostWithoutPort = allowedUrl.hostname\n\n          // Match with or without port, and ensure protocol compatibility\n          // WebSocket protocols (ws/wss) should match HTTP protocols (http/https)\n          const normalizeProtocol = (protocol: string): string => {\n            const p = protocol.replace(':', '')\n            if (p === 'ws') return 'http'\n            if (p === 'wss') return 'https'\n            return p\n          }\n\n          if (\n            (hostWithPort === allowedHostWithPort || hostWithoutPort === allowedHostWithoutPort) &&\n            normalizeProtocol(url.protocol) === normalizeProtocol(allowedUrl.protocol)\n          ) {\n            return true\n          }\n        } catch {\n          // If allowedHost is not a valid URL, skip it\n          continue\n        }\n      }\n\n      return false\n    } catch {\n      // Invalid URL\n      return false\n    }\n  }\n\n  async start(): Promise<void> {\n    return new Promise((resolve, reject) => {\n      this.server = createServer((req, res) => {\n        this.handleRequest(req, res)\n      })\n\n      this.server.listen(this.config.port, () => {\n        console.log(`SSE-WebSocket Proxy listening on port ${this.config.port}`)\n        console.log(`Allowed hosts: ${this.config.allowedHosts.length > 0 ? this.config.allowedHosts.join(', ') : 'none'}`)\n        console.log(`Localhost allowed: ${this.config.allowAnyLocalhostPort ? 'yes (any port)' : 'no'}`)\n        if (this.config.dangerouslyAllowAnyHost) {\n          console.warn(`⚠️  SECURITY WARNING: Any host connections allowed (--dangerously-allow-any-host)`)\n        }\n\n        // Cleanup inactive connections\n        this.cleanupInterval = setInterval(() => {\n          this.cleanupInactiveConnections()\n        }, this.config.connectionTimeout!)\n\n        resolve()\n      })\n\n      this.server.on('error', reject)\n    })\n  }\n\n  private handleRequest(req: IncomingMessage, res: ServerResponse): void {\n    const url = new URL(req.url!, `http://localhost:${this.config.port}`)\n\n    // Enable CORS for all requests\n    this.setCORSHeaders(res)\n\n    if (req.method === 'OPTIONS') {\n      res.writeHead(200)\n      res.end()\n      return\n    }\n\n    if (url.pathname.startsWith('/sse')) {\n      this.handleSSEConnection(req, res)\n    } else if (url.pathname === '/messages' && req.method === 'POST') {\n      this.handleMessageSend(req, res)\n    } else if (url.pathname === '/close' && req.method === 'POST') {\n      this.handleCloseRequest(req, res)\n    } else if (url.pathname === '/health') {\n      this.handleHealthCheck(req, res)\n    } else {\n      res.writeHead(404, { 'Content-Type': 'text/plain' })\n      res.end('Not Found')\n    }\n  }\n\n  private setCORSHeaders(res: ServerResponse): void {\n    res.setHeader('Access-Control-Allow-Origin', '*')\n    res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS')\n    res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization, X-Session-Id, X-Session-Secret')\n    res.setHeader('Access-Control-Allow-Credentials', 'true')\n  }\n\n  private handleSSEConnection(req: IncomingMessage, res: ServerResponse): void {\n    const url = new URL(req.url!, `http://localhost:${this.config.port}`)\n    const sessionId = url.searchParams.get('sessionId') || this.generateSessionId()\n    const backendUrl = url.searchParams.get('backend')\n\n    // Enhanced connection logging\n    console.log(`SSE connection established: session=${sessionId}, client_url=${req.url}, client_ip=${req.socket.remoteAddress}`)\n\n    // Validate backend URL is provided\n    if (!backendUrl) {\n      console.log(`Rejecting connection: no backend URL specified (session ${sessionId})`)\n      res.writeHead(400, { 'Content-Type': 'text/plain' })\n      res.end('Missing backend parameter. Usage: /sse?backend=ws://localhost:8080&sessionId=...')\n      return\n    }\n\n    // Validate backend URL is allowed\n    if (!this.isBackendAllowed(backendUrl)) {\n      console.log(`Rejecting connection: backend URL not allowed: ${backendUrl} (session ${sessionId})`)\n      res.writeHead(403, { 'Content-Type': 'text/plain' })\n      res.end(`Backend URL not allowed: ${backendUrl}`)\n      return\n    }\n\n    // Set up SSE headers\n    res.writeHead(200, {\n      'Content-Type': 'text/event-stream',\n      'Cache-Control': 'no-cache',\n      Connection: 'keep-alive',\n      ...this.getCORSHeaders(),\n    })\n\n    // Create WebSocket connection to backend, preserving the original path structure\n    const wsUrl = this.buildWebSocketUrl(backendUrl, req.url!)\n    console.log(`Connecting to WebSocket backend: ${wsUrl} (for session ${sessionId})`)\n    const websocket = new WsWebSocket(wsUrl)\n    websocket.binaryType = 'arraybuffer'\n\n    // Generate session secret for message authentication\n    const sessionSecret = this.generateSessionSecret()\n\n    const client: Client = {\n      sseResponse: res,\n      websocket,\n      sessionId,\n      sessionSecret,\n      lastActivity: Date.now(),\n    }\n\n    this.clients.set(sessionId, client)\n\n    this.setupWebSocketHandlers(client)\n    this.setupSSECleanup(req, res, sessionId)\n\n    // Send session secret as first message\n    this.sendSSEMessage(res, encodeSessionSecretMessage(sessionSecret, Date.now()))\n\n    // Send keepalive pings\n    const keepaliveInterval = setInterval(() => {\n      if (!res.destroyed) {\n        this.sendSSEMessage(res, encodePingMessage(Date.now()))\n        client.lastActivity = Date.now()\n      } else {\n        clearInterval(keepaliveInterval)\n      }\n    }, this.config.keepaliveInterval!)\n  }\n\n  private async handleRequestWithSession<T>(\n    req: IncomingMessage,\n    res: ServerResponse,\n    options: { requireOpenWebSocket?: boolean } = {},\n    handler: (client: Client, data: T) => void | Promise<void>,\n  ): Promise<void> {\n    const sessionId = req.headers['x-session-id'] as string\n    const sessionSecret = req.headers['x-session-secret'] as string\n\n    if (!sessionId) {\n      res.writeHead(400, { 'Content-Type': 'application/json' })\n      res.end(JSON.stringify({ error: 'Missing X-Session-Id header' }))\n      return\n    }\n\n    if (!sessionSecret) {\n      res.writeHead(400, { 'Content-Type': 'application/json' })\n      res.end(JSON.stringify({ error: 'Missing X-Session-Secret header' }))\n      return\n    }\n\n    const client = this.clients.get(sessionId)\n    if (!client) {\n      res.writeHead(404, { 'Content-Type': 'application/json' })\n      res.end(JSON.stringify({ error: 'Session not found' }))\n      return\n    }\n\n    if (client.sessionSecret !== sessionSecret) {\n      res.writeHead(403, { 'Content-Type': 'application/json' })\n      res.end(JSON.stringify({ error: 'Invalid session secret' }))\n      return\n    }\n\n    if (options.requireOpenWebSocket && client.websocket.readyState !== WebSocket.OPEN) {\n      res.writeHead(503, { 'Content-Type': 'application/json' })\n      res.end(JSON.stringify({ error: 'WebSocket not connected' }))\n      return\n    }\n\n    // Read the request body\n    let body = ''\n    req.on('data', (chunk) => {\n      body += chunk.toString()\n    })\n\n    req.on('end', async () => {\n      let data: T\n      try {\n        data = JSON.parse(body)\n      } catch (error) {\n        console.error('Error parsing JSON request:', error)\n        res.writeHead(400, { 'Content-Type': 'application/json' })\n        res.end(JSON.stringify({ error: 'Invalid JSON' }))\n        return\n      }\n\n      // Call the handler - any errors here should crash, not be caught\n      await handler(client, data)\n    })\n  }\n\n  private handleMessageSend(req: IncomingMessage, res: ServerResponse): void {\n    const sessionId = req.headers['x-session-id'] as string\n    const sessionSecret = req.headers['x-session-secret'] as string\n\n    if (!sessionId) {\n      res.writeHead(400, { 'Content-Type': 'application/json' })\n      res.end(JSON.stringify({ error: 'Missing X-Session-Id header' }))\n      return\n    }\n\n    if (!sessionSecret) {\n      res.writeHead(400, { 'Content-Type': 'application/json' })\n      res.end(JSON.stringify({ error: 'Missing X-Session-Secret header' }))\n      return\n    }\n\n    const client = this.clients.get(sessionId)\n    if (!client) {\n      res.writeHead(404, { 'Content-Type': 'application/json' })\n      res.end(JSON.stringify({ error: 'Session not found' }))\n      return\n    }\n\n    if (client.sessionSecret !== sessionSecret) {\n      res.writeHead(403, { 'Content-Type': 'application/json' })\n      res.end(JSON.stringify({ error: 'Invalid session secret' }))\n      return\n    }\n\n    if (client.websocket.readyState !== WebSocket.OPEN) {\n      res.writeHead(503, { 'Content-Type': 'application/json' })\n      res.end(JSON.stringify({ error: 'WebSocket not connected' }))\n      return\n    }\n\n    // Read the request body as JSON\n    let body = ''\n    req.on('data', (chunk) => {\n      body += chunk.toString()\n    })\n\n    req.on('end', () => {\n      try {\n        // Parse and validate the message request\n        const messageRequest = decodeMessageRequest(body)\n        this.verboseLog(`Received message from client:`, messageRequest)\n\n        if (isTextMessageRequest(messageRequest)) {\n          // Send text message directly to WebSocket backend\n          this.verboseLog(`Sending text message to WebSocket backend:`, messageRequest.data)\n          client.websocket.send(messageRequest.data)\n        } else if (isBinaryMessageRequest(messageRequest)) {\n          // Decode base64 data and send as binary to WebSocket backend\n          const binaryData = decodeBinaryData(messageRequest.data)\n          this.verboseLog(`Sending binary message to WebSocket backend: ${binaryData.length} bytes`)\n          client.websocket.send(binaryData)\n        }\n\n        client.lastActivity = Date.now()\n\n        res.writeHead(200, { 'Content-Type': 'application/json' })\n        res.end(JSON.stringify({ success: true }))\n      } catch (error) {\n        console.error(`Failed to process message for session:`, error)\n        res.writeHead(400, { 'Content-Type': 'application/json' })\n        res.end(\n          JSON.stringify({\n            error: `Invalid message format: ${error instanceof Error ? error.message : 'Unknown error'}`,\n          }),\n        )\n      }\n    })\n  }\n\n  private handleCloseRequest(req: IncomingMessage, res: ServerResponse): void {\n    this.handleRequestWithSession(\n      req,\n      res,\n      {}, // Don't require open WebSocket - we might want to close during CONNECTING\n      (client, data: { code?: number; reason?: string }) => {\n        const { code = 1000, reason = '' } = data\n\n        // Track if response has been sent\n        let responseSent = false\n\n        // Set up close event listener to capture actual close info\n        const closeHandler = (actualCode: number, actualReason: Buffer) => {\n          if (responseSent) return\n          responseSent = true\n\n          const wasClean = actualCode >= 1000 && actualCode <= 1003\n          res.writeHead(200, { 'Content-Type': 'application/json' })\n          res.end(\n            JSON.stringify({\n              code: actualCode,\n              reason: actualReason.toString(),\n              wasClean,\n            }),\n          )\n        }\n\n        // Set up timeout in case WebSocket doesn't close cleanly\n        const timeout = setTimeout(() => {\n          if (responseSent) return\n          responseSent = true\n\n          client.websocket.removeListener('close', closeHandler)\n          res.writeHead(200, { 'Content-Type': 'application/json' })\n          res.end(\n            JSON.stringify({\n              code: 1006,\n              reason: 'Close timeout',\n              wasClean: false,\n            }),\n          )\n        }, 5000) // 5 second timeout\n\n        client.websocket.once('close', (actualCode, actualReason) => {\n          clearTimeout(timeout)\n          closeHandler(actualCode, actualReason)\n        })\n\n        // Close the backend WebSocket with the requested code/reason\n        console.log(`Closing WebSocket with code ${code}, reason: ${reason}`)\n\n        // Handle different WebSocket states\n        if (client.websocket.readyState === WebSocket.CONNECTING) {\n          // Still connecting - terminate to avoid error\n          client.websocket.terminate()\n        } else if (client.websocket.readyState === WebSocket.OPEN) {\n          // Open - normal close\n          client.websocket.close(code, reason)\n        }\n        // If already closing or closed, do nothing\n      },\n    )\n  }\n\n  private handleHealthCheck(req: IncomingMessage, res: ServerResponse): void {\n    const url = new URL(req.url!, `http://localhost:${this.config.port}`)\n    const providedSecret = url.searchParams.get('secret')\n    const requiredSecret = process.env.SSE_WS_PROXY_HEALTH_SECRET\n\n    // If secret is configured and provided secret matches, return detailed info\n    if (requiredSecret && providedSecret === requiredSecret) {\n      const activeConnections = Array.from(this.clients.entries()).map(([sessionId, client]) => ({\n        sessionId,\n        websocketState: this.getWebSocketState(client.websocket),\n        lastActivity: new Date(client.lastActivity).toISOString(),\n      }))\n\n      res.writeHead(200, { 'Content-Type': 'application/json' })\n      res.end(\n        JSON.stringify({\n          status: 'healthy',\n          activeConnections: activeConnections.length,\n          connections: activeConnections,\n          uptime: process.uptime(),\n        }),\n      )\n    } else {\n      // Return minimal health info only\n      res.writeHead(200, { 'Content-Type': 'application/json' })\n      res.end(\n        JSON.stringify({\n          status: 'healthy',\n        }),\n      )\n    }\n  }\n\n  private setupWebSocketHandlers(client: Client): void {\n    const { websocket, sseResponse, sessionId } = client\n\n    websocket.on('open', () => {\n      console.log(`WebSocket connected to backend (session ${sessionId})`)\n      this.verboseLog(`WebSocket connected event fired for session ${sessionId}`)\n      this.sendSSEMessage(sseResponse, encodeWebSocketConnectedMessage(sessionId, Date.now()))\n    })\n\n    websocket.on('message', (data, isBinary) => {\n      client.lastActivity = Date.now()\n\n      if (isBinary) {\n        // Binary message - encode as base64 and send as binary SSE message\n        let base64Data: string\n        if (data instanceof ArrayBuffer) {\n          base64Data = Buffer.from(data).toString('base64')\n        } else if (Buffer.isBuffer(data)) {\n          base64Data = data.toString('base64')\n        } else {\n          // Handle other binary types\n          base64Data = Buffer.from(data as any).toString('base64')\n        }\n        this.verboseLog(\n          `Received binary message from WebSocket backend (session ${sessionId}): ${'length' in data ? data.length : (data as any).byteLength || 0} bytes`,\n        )\n        this.sendSSEMessage(sseResponse, encodeBinaryDataMessage(base64Data, Date.now()))\n      } else {\n        // Text message\n        this.verboseLog(`Received text message from WebSocket backend (session ${sessionId}):`, data.toString())\n        this.sendSSEMessage(sseResponse, encodeDataMessage(data.toString(), Date.now()))\n      }\n    })\n\n    websocket.on('error', (error) => {\n      console.error(`WebSocket error for session ${sessionId}:`, error)\n      this.sendSSEMessage(sseResponse, encodeWebSocketErrorMessage(error.message, Date.now()))\n    })\n\n    websocket.on('close', (code, reason) => {\n      console.log(`WebSocket closed (session ${sessionId}): code=${code}, reason=${reason.toString()}`)\n      this.verboseLog(`WebSocket close event fired for session ${sessionId}: code=${code}, reason=${reason.toString()}`)\n\n      // Determine if close was clean (normal closure codes)\n      const wasClean = code >= 1000 && code <= 1003\n\n      this.sendSSEMessage(sseResponse, encodeWebSocketClosedMessage(code, reason.toString(), wasClean, Date.now()))\n\n      // Clean up the client connection\n      this.cleanupClient(sessionId, 'websocket-closed')\n    })\n  }\n\n  private setupSSECleanup(req: IncomingMessage, res: ServerResponse, sessionId: string): void {\n    res.on('close', () => {\n      this.cleanupClient(sessionId, 'sse-closed')\n    })\n\n    req.on('aborted', () => {\n      this.cleanupClient(sessionId, 'sse-aborted')\n    })\n  }\n\n  private sendSSEMessage(res: ServerResponse, encodedData: string): void {\n    if (res.destroyed) return\n\n    const message = `data: ${encodedData}\\n\\n`\n    this.verboseLog(`Sending SSE message to client:`, encodedData)\n    res.write(message)\n  }\n\n  private cleanupClient(\n    sessionId: string,\n    reason: 'sse-closed' | 'sse-aborted' | 'websocket-closed' | 'timeout' = 'websocket-closed',\n  ): void {\n    const client = this.clients.get(sessionId)\n    if (client) {\n      if (client.websocket && client.websocket.readyState === WebSocket.OPEN) {\n        // If SSE connection was terminated (tab close, network issue), send 1001 \"going away\"\n        if (reason === 'sse-closed' || reason === 'sse-aborted') {\n          client.websocket.close(1001, 'Client going away')\n        } else {\n          // Normal cleanup - let WebSocket close naturally\n          client.websocket.close()\n        }\n      }\n      if (!client.sseResponse.destroyed) {\n        client.sseResponse.end()\n      }\n      this.clients.delete(sessionId)\n    }\n  }\n\n  private cleanupInactiveConnections(): void {\n    const now = Date.now()\n    for (const [sessionId, client] of this.clients.entries()) {\n      if (now - client.lastActivity > this.config.connectionTimeout!) {\n        console.log(`Cleaning up inactive session: ${sessionId}`)\n        this.cleanupClient(sessionId, 'timeout')\n      }\n    }\n  }\n\n  private buildWebSocketUrl(backendUrl: string, originalRequestUrl: string): string {\n    // The backend URL is now passed directly from the client, so we just need to ensure\n    // localhost uses IPv4 to avoid connection issues\n    const url = new URL(backendUrl)\n\n    // Force IPv4 for localhost to avoid IPv6 connection issues\n    if (url.hostname === 'localhost') {\n      url.hostname = '127.0.0.1'\n    }\n\n    return url.toString()\n  }\n\n  private generateSessionId(): string {\n    // Generate a more random session ID using crypto\n    const timestamp = Date.now()\n    const random = Math.random().toString(36).substring(2, 15)\n    const random2 = Math.random().toString(36).substring(2, 15)\n    return `session-${timestamp}-${random}-${random2}`\n  }\n\n  private generateSessionSecret(): string {\n    // Generate cryptographically secure session secret\n    const crypto = require('crypto')\n    return crypto.randomBytes(32).toString('hex')\n  }\n\n  private getWebSocketState(ws: WsWebSocket): string {\n    switch (ws.readyState) {\n      case WebSocket.CONNECTING:\n        return 'connecting'\n      case WebSocket.OPEN:\n        return 'open'\n      case WebSocket.CLOSING:\n        return 'closing'\n      case WebSocket.CLOSED:\n        return 'closed'\n      default:\n        return 'unknown'\n    }\n  }\n\n  private getCORSHeaders(): Record<string, string> {\n    return {\n      'Access-Control-Allow-Origin': '*',\n      'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',\n      'Access-Control-Allow-Headers': 'Content-Type, Authorization, X-Session-Id, X-Session-Secret',\n      'Access-Control-Allow-Credentials': 'true',\n    }\n  }\n\n  async stop(): Promise<void> {\n    console.log('Shutting down proxy server...')\n\n    // Clear the cleanup interval\n    if (this.cleanupInterval) {\n      clearInterval(this.cleanupInterval)\n      this.cleanupInterval = null\n    }\n\n    // Close all client connections\n    for (const sessionId of this.clients.keys()) {\n      this.cleanupClient(sessionId, 'websocket-closed')\n    }\n\n    // Close the HTTP server\n    if (this.server) {\n      return new Promise((resolve) => {\n        this.server!.close(() => {\n          console.log('Proxy server shut down complete')\n          this.server = null\n          resolve()\n        })\n      })\n    }\n  }\n\n  // Test utilities for sending raw messages to specific sessions\n  sendRawMessageToSession(sessionId: string, rawData: string): boolean {\n    const client = this.clients.get(sessionId)\n    if (!client) {\n      return false\n    }\n\n    this.sendSSEMessage(client.sseResponse, rawData)\n    return true\n  }\n\n  getActiveSessionIds(): string[] {\n    return Array.from(this.clients.keys())\n  }\n\n  hasSession(sessionId: string): boolean {\n    return this.clients.has(sessionId)\n  }\n}\n"],"mappings":";;;;;;;AA8BA,IAAa,oBAAb,MAA+B;CAC7B,AAAQ,0BAAU,IAAI;CACtB,AAAQ,SAAwB;CAChC,AAAQ,kBAAyC;CAEjD,YAAoBA,QAAqB;EAArB;AAClB,OAAK,SAAS;GACZ,mBAAmB;GACnB,mBAAmB;GACnB,GAAG;EACJ;CACF;CAED,AAAQ,YAAqB;AAC3B,WAAS,QAAQ,IAAI;CACtB;CAED,AAAQ,WAAW,GAAG,MAAmB;AACvC,MAAI,KAAK,WAAW,CAClB,SAAQ,IAAI,0BAA0B,GAAG,KAAK;CAEjD;CAED,AAAQ,iBAAiBC,YAA6B;AACpD,MAAI;GACF,MAAM,MAAM,IAAI,IAAI;AAGpB,OAAI,KAAK,OAAO,yBAAyB;AACvC,YAAQ,MACL,oFAAoF,IAAI,SAAS,GAAG,IAAI,SAAS,IAAI,aAAa,SAAS,MAAM,IAAI,EACvJ;AACD,WAAO;GACR;AAGD,OAAI,KAAK,OAAO,uBAAuB;IACrC,MAAM,cAAc,IAAI,aAAa,eAAe,IAAI,aAAa,eAAe,IAAI,aAAa;AACrG,QAAI,YACF,QAAO;GAEV;GAGD,MAAM,eAAe,IAAI,QAAQ,EAAE,IAAI,SAAS,GAAG,IAAI,KAAK,IAAI,IAAI;GACpE,MAAM,kBAAkB,IAAI;AAE5B,QAAK,MAAM,eAAe,KAAK,OAAO,aACpC,KAAI;IACF,MAAM,aAAa,IAAI,IAAI;IAC3B,MAAM,sBAAsB,WAAW,QAAQ,EAAE,WAAW,SAAS,GAAG,WAAW,KAAK,IAAI,WAAW;IACvG,MAAM,yBAAyB,WAAW;IAI1C,MAAM,oBAAoB,CAACC,aAA6B;KACtD,MAAM,IAAI,SAAS,QAAQ,KAAK,GAAG;AACnC,SAAI,MAAM,KAAM,QAAO;AACvB,SAAI,MAAM,MAAO,QAAO;AACxB,YAAO;IACR;AAED,SACG,iBAAiB,uBAAuB,oBAAoB,2BAC7D,kBAAkB,IAAI,SAAS,KAAK,kBAAkB,WAAW,SAAS,CAE1E,QAAO;GAEV,QAAO;AAEN;GACD;AAGH,UAAO;EACR,QAAO;AAEN,UAAO;EACR;CACF;CAED,MAAM,QAAuB;AAC3B,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,QAAK,SAAS,uBAAa,CAAC,KAAK,QAAQ;AACvC,SAAK,cAAc,KAAK,IAAI;GAC7B,EAAC;AAEF,QAAK,OAAO,OAAO,KAAK,OAAO,MAAM,MAAM;AACzC,YAAQ,KAAK,wCAAwC,KAAK,OAAO,KAAK,EAAE;AACxE,YAAQ,KAAK,iBAAiB,KAAK,OAAO,aAAa,SAAS,IAAI,KAAK,OAAO,aAAa,KAAK,KAAK,GAAG,OAAO,EAAE;AACnH,YAAQ,KAAK,qBAAqB,KAAK,OAAO,wBAAwB,mBAAmB,KAAK,EAAE;AAChG,QAAI,KAAK,OAAO,wBACd,SAAQ,MAAM,mFAAmF;AAInG,SAAK,kBAAkB,YAAY,MAAM;AACvC,UAAK,4BAA4B;IAClC,GAAE,KAAK,OAAO,kBAAmB;AAElC,aAAS;GACV,EAAC;AAEF,QAAK,OAAO,GAAG,SAAS,OAAO;EAChC;CACF;CAED,AAAQ,cAAcC,KAAsBC,KAA2B;EACrE,MAAM,MAAM,IAAI,IAAI,IAAI,MAAO,mBAAmB,KAAK,OAAO,KAAK;AAGnE,OAAK,eAAe,IAAI;AAExB,MAAI,IAAI,WAAW,WAAW;AAC5B,OAAI,UAAU,IAAI;AAClB,OAAI,KAAK;AACT;EACD;AAED,MAAI,IAAI,SAAS,WAAW,OAAO,CACjC,MAAK,oBAAoB,KAAK,IAAI;WACzB,IAAI,aAAa,eAAe,IAAI,WAAW,OACxD,MAAK,kBAAkB,KAAK,IAAI;WACvB,IAAI,aAAa,YAAY,IAAI,WAAW,OACrD,MAAK,mBAAmB,KAAK,IAAI;WACxB,IAAI,aAAa,UAC1B,MAAK,kBAAkB,KAAK,IAAI;OAC3B;AACL,OAAI,UAAU,KAAK,EAAE,gBAAgB,aAAc,EAAC;AACpD,OAAI,IAAI,YAAY;EACrB;CACF;CAED,AAAQ,eAAeA,KAA2B;AAChD,MAAI,UAAU,+BAA+B,IAAI;AACjD,MAAI,UAAU,gCAAgC,qBAAqB;AACnE,MAAI,UAAU,gCAAgC,8DAA8D;AAC5G,MAAI,UAAU,oCAAoC,OAAO;CAC1D;CAED,AAAQ,oBAAoBD,KAAsBC,KAA2B;EAC3E,MAAM,MAAM,IAAI,IAAI,IAAI,MAAO,mBAAmB,KAAK,OAAO,KAAK;EACnE,MAAM,YAAY,IAAI,aAAa,IAAI,YAAY,IAAI,KAAK,mBAAmB;EAC/E,MAAM,aAAa,IAAI,aAAa,IAAI,UAAU;AAGlD,UAAQ,KAAK,sCAAsC,UAAU,eAAe,IAAI,IAAI,cAAc,IAAI,OAAO,cAAc,EAAE;AAG7H,OAAK,YAAY;AACf,WAAQ,KAAK,0DAA0D,UAAU,GAAG;AACpF,OAAI,UAAU,KAAK,EAAE,gBAAgB,aAAc,EAAC;AACpD,OAAI,IAAI,mFAAmF;AAC3F;EACD;AAGD,OAAK,KAAK,iBAAiB,WAAW,EAAE;AACtC,WAAQ,KAAK,iDAAiD,WAAW,YAAY,UAAU,GAAG;AAClG,OAAI,UAAU,KAAK,EAAE,gBAAgB,aAAc,EAAC;AACpD,OAAI,KAAK,2BAA2B,WAAW,EAAE;AACjD;EACD;AAGD,MAAI,UAAU,KAAK;GACjB,gBAAgB;GAChB,iBAAiB;GACjB,YAAY;GACZ,GAAG,KAAK,gBAAgB;EACzB,EAAC;EAGF,MAAM,QAAQ,KAAK,kBAAkB,YAAY,IAAI,IAAK;AAC1D,UAAQ,KAAK,mCAAmC,MAAM,gBAAgB,UAAU,GAAG;EACnF,MAAM,YAAY,IAAIC,WAAY;AAClC,YAAU,aAAa;EAGvB,MAAM,gBAAgB,KAAK,uBAAuB;EAElD,MAAMC,SAAiB;GACrB,aAAa;GACb;GACA;GACA;GACA,cAAc,KAAK,KAAK;EACzB;AAED,OAAK,QAAQ,IAAI,WAAW,OAAO;AAEnC,OAAK,uBAAuB,OAAO;AACnC,OAAK,gBAAgB,KAAK,KAAK,UAAU;AAGzC,OAAK,eAAe,KAAK,gDAA2B,eAAe,KAAK,KAAK,CAAC,CAAC;EAG/E,MAAM,oBAAoB,YAAY,MAAM;AAC1C,QAAK,IAAI,WAAW;AAClB,SAAK,eAAe,KAAK,uCAAkB,KAAK,KAAK,CAAC,CAAC;AACvD,WAAO,eAAe,KAAK,KAAK;GACjC,MACC,eAAc,kBAAkB;EAEnC,GAAE,KAAK,OAAO,kBAAmB;CACnC;CAED,MAAc,yBACZH,KACAC,KACAG,UAA8C,CAAE,GAChDC,SACe;EACf,MAAM,YAAY,IAAI,QAAQ;EAC9B,MAAM,gBAAgB,IAAI,QAAQ;AAElC,OAAK,WAAW;AACd,OAAI,UAAU,KAAK,EAAE,gBAAgB,mBAAoB,EAAC;AAC1D,OAAI,IAAI,KAAK,UAAU,EAAE,OAAO,8BAA+B,EAAC,CAAC;AACjE;EACD;AAED,OAAK,eAAe;AAClB,OAAI,UAAU,KAAK,EAAE,gBAAgB,mBAAoB,EAAC;AAC1D,OAAI,IAAI,KAAK,UAAU,EAAE,OAAO,kCAAmC,EAAC,CAAC;AACrE;EACD;EAED,MAAM,SAAS,KAAK,QAAQ,IAAI,UAAU;AAC1C,OAAK,QAAQ;AACX,OAAI,UAAU,KAAK,EAAE,gBAAgB,mBAAoB,EAAC;AAC1D,OAAI,IAAI,KAAK,UAAU,EAAE,OAAO,oBAAqB,EAAC,CAAC;AACvD;EACD;AAED,MAAI,OAAO,kBAAkB,eAAe;AAC1C,OAAI,UAAU,KAAK,EAAE,gBAAgB,mBAAoB,EAAC;AAC1D,OAAI,IAAI,KAAK,UAAU,EAAE,OAAO,yBAA0B,EAAC,CAAC;AAC5D;EACD;AAED,MAAI,QAAQ,wBAAwB,OAAO,UAAU,eAAe,UAAU,MAAM;AAClF,OAAI,UAAU,KAAK,EAAE,gBAAgB,mBAAoB,EAAC;AAC1D,OAAI,IAAI,KAAK,UAAU,EAAE,OAAO,0BAA2B,EAAC,CAAC;AAC7D;EACD;EAGD,IAAI,OAAO;AACX,MAAI,GAAG,QAAQ,CAAC,UAAU;AACxB,WAAQ,MAAM,UAAU;EACzB,EAAC;AAEF,MAAI,GAAG,OAAO,YAAY;GACxB,IAAIC;AACJ,OAAI;AACF,WAAO,KAAK,MAAM,KAAK;GACxB,SAAQ,OAAO;AACd,YAAQ,MAAM,+BAA+B,MAAM;AACnD,QAAI,UAAU,KAAK,EAAE,gBAAgB,mBAAoB,EAAC;AAC1D,QAAI,IAAI,KAAK,UAAU,EAAE,OAAO,eAAgB,EAAC,CAAC;AAClD;GACD;AAGD,SAAM,QAAQ,QAAQ,KAAK;EAC5B,EAAC;CACH;CAED,AAAQ,kBAAkBN,KAAsBC,KAA2B;EACzE,MAAM,YAAY,IAAI,QAAQ;EAC9B,MAAM,gBAAgB,IAAI,QAAQ;AAElC,OAAK,WAAW;AACd,OAAI,UAAU,KAAK,EAAE,gBAAgB,mBAAoB,EAAC;AAC1D,OAAI,IAAI,KAAK,UAAU,EAAE,OAAO,8BAA+B,EAAC,CAAC;AACjE;EACD;AAED,OAAK,eAAe;AAClB,OAAI,UAAU,KAAK,EAAE,gBAAgB,mBAAoB,EAAC;AAC1D,OAAI,IAAI,KAAK,UAAU,EAAE,OAAO,kCAAmC,EAAC,CAAC;AACrE;EACD;EAED,MAAM,SAAS,KAAK,QAAQ,IAAI,UAAU;AAC1C,OAAK,QAAQ;AACX,OAAI,UAAU,KAAK,EAAE,gBAAgB,mBAAoB,EAAC;AAC1D,OAAI,IAAI,KAAK,UAAU,EAAE,OAAO,oBAAqB,EAAC,CAAC;AACvD;EACD;AAED,MAAI,OAAO,kBAAkB,eAAe;AAC1C,OAAI,UAAU,KAAK,EAAE,gBAAgB,mBAAoB,EAAC;AAC1D,OAAI,IAAI,KAAK,UAAU,EAAE,OAAO,yBAA0B,EAAC,CAAC;AAC5D;EACD;AAED,MAAI,OAAO,UAAU,eAAe,UAAU,MAAM;AAClD,OAAI,UAAU,KAAK,EAAE,gBAAgB,mBAAoB,EAAC;AAC1D,OAAI,IAAI,KAAK,UAAU,EAAE,OAAO,0BAA2B,EAAC,CAAC;AAC7D;EACD;EAGD,IAAI,OAAO;AACX,MAAI,GAAG,QAAQ,CAAC,UAAU;AACxB,WAAQ,MAAM,UAAU;EACzB,EAAC;AAEF,MAAI,GAAG,OAAO,MAAM;AAClB,OAAI;IAEF,MAAM,iBAAiB,+CAAqB,KAAK;AACjD,SAAK,YAAY,gCAAgC,eAAe;AAEhE,QAAI,+CAAqB,eAAe,EAAE;AAExC,UAAK,YAAY,6CAA6C,eAAe,KAAK;AAClF,YAAO,UAAU,KAAK,eAAe,KAAK;IAC3C,WAAU,iDAAuB,eAAe,EAAE;KAEjD,MAAM,aAAa,2CAAiB,eAAe,KAAK;AACxD,UAAK,YAAY,+CAA+C,WAAW,OAAO,QAAQ;AAC1F,YAAO,UAAU,KAAK,WAAW;IAClC;AAED,WAAO,eAAe,KAAK,KAAK;AAEhC,QAAI,UAAU,KAAK,EAAE,gBAAgB,mBAAoB,EAAC;AAC1D,QAAI,IAAI,KAAK,UAAU,EAAE,SAAS,KAAM,EAAC,CAAC;GAC3C,SAAQ,OAAO;AACd,YAAQ,OAAO,yCAAyC,MAAM;AAC9D,QAAI,UAAU,KAAK,EAAE,gBAAgB,mBAAoB,EAAC;AAC1D,QAAI,IACF,KAAK,UAAU,EACb,QAAQ,0BAA0B,iBAAiB,QAAQ,MAAM,UAAU,gBAAgB,EAC5F,EAAC,CACH;GACF;EACF,EAAC;CACH;CAED,AAAQ,mBAAmBD,KAAsBC,KAA2B;AAC1E,OAAK,yBACH,KACA,KACA,CAAE,GACF,CAAC,QAAQM,SAA6C;GACpD,MAAM,EAAE,OAAO,KAAM,SAAS,IAAI,GAAG;GAGrC,IAAI,eAAe;GAGnB,MAAM,eAAe,CAACC,YAAoBC,iBAAyB;AACjE,QAAI,aAAc;AAClB,mBAAe;IAEf,MAAM,WAAW,cAAc,OAAQ,cAAc;AACrD,QAAI,UAAU,KAAK,EAAE,gBAAgB,mBAAoB,EAAC;AAC1D,QAAI,IACF,KAAK,UAAU;KACb,MAAM;KACN,QAAQ,aAAa,UAAU;KAC/B;IACD,EAAC,CACH;GACF;GAGD,MAAM,UAAU,WAAW,MAAM;AAC/B,QAAI,aAAc;AAClB,mBAAe;AAEf,WAAO,UAAU,eAAe,SAAS,aAAa;AACtD,QAAI,UAAU,KAAK,EAAE,gBAAgB,mBAAoB,EAAC;AAC1D,QAAI,IACF,KAAK,UAAU;KACb,MAAM;KACN,QAAQ;KACR,UAAU;IACX,EAAC,CACH;GACF,GAAE,IAAK;AAER,UAAO,UAAU,KAAK,SAAS,CAAC,YAAY,iBAAiB;AAC3D,iBAAa,QAAQ;AACrB,iBAAa,YAAY,aAAa;GACvC,EAAC;AAGF,WAAQ,KAAK,8BAA8B,KAAK,YAAY,OAAO,EAAE;AAGrE,OAAI,OAAO,UAAU,eAAe,UAAU,WAE5C,QAAO,UAAU,WAAW;YACnB,OAAO,UAAU,eAAe,UAAU,KAEnD,QAAO,UAAU,MAAM,MAAM,OAAO;EAGvC,EACF;CACF;CAED,AAAQ,kBAAkBT,KAAsBC,KAA2B;EACzE,MAAM,MAAM,IAAI,IAAI,IAAI,MAAO,mBAAmB,KAAK,OAAO,KAAK;EACnE,MAAM,iBAAiB,IAAI,aAAa,IAAI,SAAS;EACrD,MAAM,iBAAiB,QAAQ,IAAI;AAGnC,MAAI,kBAAkB,mBAAmB,gBAAgB;GACvD,MAAM,oBAAoB,MAAM,KAAK,KAAK,QAAQ,SAAS,CAAC,CAAC,IAAI,CAAC,CAAC,WAAW,OAAO,MAAM;IACzF;IACA,gBAAgB,KAAK,kBAAkB,OAAO,UAAU;IACxD,cAAc,IAAI,KAAK,OAAO,cAAc,aAAa;GAC1D,GAAE;AAEH,OAAI,UAAU,KAAK,EAAE,gBAAgB,mBAAoB,EAAC;AAC1D,OAAI,IACF,KAAK,UAAU;IACb,QAAQ;IACR,mBAAmB,kBAAkB;IACrC,aAAa;IACb,QAAQ,QAAQ,QAAQ;GACzB,EAAC,CACH;EACF,OAAM;AAEL,OAAI,UAAU,KAAK,EAAE,gBAAgB,mBAAoB,EAAC;AAC1D,OAAI,IACF,KAAK,UAAU,EACb,QAAQ,UACT,EAAC,CACH;EACF;CACF;CAED,AAAQ,uBAAuBE,QAAsB;EACnD,MAAM,EAAE,WAAW,aAAa,WAAW,GAAG;AAE9C,YAAU,GAAG,QAAQ,MAAM;AACzB,WAAQ,KAAK,0CAA0C,UAAU,GAAG;AACpE,QAAK,YAAY,8CAA8C,UAAU,EAAE;AAC3E,QAAK,eAAe,aAAa,qDAAgC,WAAW,KAAK,KAAK,CAAC,CAAC;EACzF,EAAC;AAEF,YAAU,GAAG,WAAW,CAAC,MAAM,aAAa;AAC1C,UAAO,eAAe,KAAK,KAAK;AAEhC,OAAI,UAAU;IAEZ,IAAIO;AACJ,QAAI,gBAAgB,YAClB,cAAa,OAAO,KAAK,KAAK,CAAC,SAAS,SAAS;aACxC,OAAO,SAAS,KAAK,CAC9B,cAAa,KAAK,SAAS,SAAS;QAGpC,cAAa,OAAO,KAAK,KAAY,CAAC,SAAS,SAAS;AAE1D,SAAK,YACF,0DAA0D,UAAU,KAAK,YAAY,OAAO,KAAK,SAAU,KAAa,cAAc,EAAE,QAC1I;AACD,SAAK,eAAe,aAAa,6CAAwB,YAAY,KAAK,KAAK,CAAC,CAAC;GAClF,OAAM;AAEL,SAAK,YAAY,wDAAwD,UAAU,KAAK,KAAK,UAAU,CAAC;AACxG,SAAK,eAAe,aAAa,uCAAkB,KAAK,UAAU,EAAE,KAAK,KAAK,CAAC,CAAC;GACjF;EACF,EAAC;AAEF,YAAU,GAAG,SAAS,CAAC,UAAU;AAC/B,WAAQ,OAAO,8BAA8B,UAAU,IAAI,MAAM;AACjE,QAAK,eAAe,aAAa,iDAA4B,MAAM,SAAS,KAAK,KAAK,CAAC,CAAC;EACzF,EAAC;AAEF,YAAU,GAAG,SAAS,CAAC,MAAM,WAAW;AACtC,WAAQ,KAAK,4BAA4B,UAAU,UAAU,KAAK,WAAW,OAAO,UAAU,CAAC,EAAE;AACjG,QAAK,YAAY,0CAA0C,UAAU,SAAS,KAAK,WAAW,OAAO,UAAU,CAAC,EAAE;GAGlH,MAAM,WAAW,QAAQ,OAAQ,QAAQ;AAEzC,QAAK,eAAe,aAAa,kDAA6B,MAAM,OAAO,UAAU,EAAE,UAAU,KAAK,KAAK,CAAC,CAAC;AAG7G,QAAK,cAAc,WAAW,mBAAmB;EAClD,EAAC;CACH;CAED,AAAQ,gBAAgBV,KAAsBC,KAAqBU,WAAyB;AAC1F,MAAI,GAAG,SAAS,MAAM;AACpB,QAAK,cAAc,WAAW,aAAa;EAC5C,EAAC;AAEF,MAAI,GAAG,WAAW,MAAM;AACtB,QAAK,cAAc,WAAW,cAAc;EAC7C,EAAC;CACH;CAED,AAAQ,eAAeV,KAAqBW,aAA2B;AACrE,MAAI,IAAI,UAAW;EAEnB,MAAM,WAAW,QAAQ,YAAY;AACrC,OAAK,YAAY,iCAAiC,YAAY;AAC9D,MAAI,MAAM,QAAQ;CACnB;CAED,AAAQ,cACND,WACAE,SAAwE,oBAClE;EACN,MAAM,SAAS,KAAK,QAAQ,IAAI,UAAU;AAC1C,MAAI,QAAQ;AACV,OAAI,OAAO,aAAa,OAAO,UAAU,eAAe,UAAU,KAEhE,KAAI,WAAW,gBAAgB,WAAW,cACxC,QAAO,UAAU,MAAM,MAAM,oBAAoB;OAGjD,QAAO,UAAU,OAAO;AAG5B,QAAK,OAAO,YAAY,UACtB,QAAO,YAAY,KAAK;AAE1B,QAAK,QAAQ,OAAO,UAAU;EAC/B;CACF;CAED,AAAQ,6BAAmC;EACzC,MAAM,MAAM,KAAK,KAAK;AACtB,OAAK,MAAM,CAAC,WAAW,OAAO,IAAI,KAAK,QAAQ,SAAS,CACtD,KAAI,MAAM,OAAO,eAAe,KAAK,OAAO,mBAAoB;AAC9D,WAAQ,KAAK,gCAAgC,UAAU,EAAE;AACzD,QAAK,cAAc,WAAW,UAAU;EACzC;CAEJ;CAED,AAAQ,kBAAkBf,YAAoBgB,oBAAoC;EAGhF,MAAM,MAAM,IAAI,IAAI;AAGpB,MAAI,IAAI,aAAa,YACnB,KAAI,WAAW;AAGjB,SAAO,IAAI,UAAU;CACtB;CAED,AAAQ,oBAA4B;EAElC,MAAM,YAAY,KAAK,KAAK;EAC5B,MAAM,SAAS,KAAK,QAAQ,CAAC,SAAS,GAAG,CAAC,UAAU,GAAG,GAAG;EAC1D,MAAM,UAAU,KAAK,QAAQ,CAAC,SAAS,GAAG,CAAC,UAAU,GAAG,GAAG;AAC3D,UAAQ,UAAU,UAAU,GAAG,OAAO,GAAG,QAAQ;CAClD;CAED,AAAQ,wBAAgC;EAEtC,MAAM,SAAS,QAAQ,SAAS;AAChC,SAAO,OAAO,YAAY,GAAG,CAAC,SAAS,MAAM;CAC9C;CAED,AAAQ,kBAAkBC,MAAyB;AACjD,UAAQC,KAAG,YAAX;GACE,KAAK,UAAU,WACb,QAAO;GACT,KAAK,UAAU,KACb,QAAO;GACT,KAAK,UAAU,QACb,QAAO;GACT,KAAK,UAAU,OACb,QAAO;GACT,QACE,QAAO;EACV;CACF;CAED,AAAQ,iBAAyC;AAC/C,SAAO;GACL,+BAA+B;GAC/B,gCAAgC;GAChC,gCAAgC;GAChC,oCAAoC;EACrC;CACF;CAED,MAAM,OAAsB;AAC1B,UAAQ,IAAI,gCAAgC;AAG5C,MAAI,KAAK,iBAAiB;AACxB,iBAAc,KAAK,gBAAgB;AACnC,QAAK,kBAAkB;EACxB;AAGD,OAAK,MAAM,aAAa,KAAK,QAAQ,MAAM,CACzC,MAAK,cAAc,WAAW,mBAAmB;AAInD,MAAI,KAAK,OACP,QAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,QAAK,OAAQ,MAAM,MAAM;AACvB,YAAQ,IAAI,kCAAkC;AAC9C,SAAK,SAAS;AACd,aAAS;GACV,EAAC;EACH;CAEJ;CAGD,wBAAwBL,WAAmBM,SAA0B;EACnE,MAAM,SAAS,KAAK,QAAQ,IAAI,UAAU;AAC1C,OAAK,OACH,QAAO;AAGT,OAAK,eAAe,OAAO,aAAa,QAAQ;AAChD,SAAO;CACR;CAED,sBAAgC;AAC9B,SAAO,MAAM,KAAK,KAAK,QAAQ,MAAM,CAAC;CACvC;CAED,WAAWN,WAA4B;AACrC,SAAO,KAAK,QAAQ,IAAI,UAAU;CACnC;AACF"}