{"version":3,"file":"delegation-monitor.mjs","names":[],"sources":["../../../src/services/delegation-monitor.ts"],"sourcesContent":["/**\n * Delegation Monitor\n *\n * Tracks remaining budget, expiry, and health of active delegations.\n * Emits warnings when:\n *   - A delegation is within 20% of its spending limit\n *   - A delegation expires within 24 hours\n *   - A delegation has been revoked on-chain\n *\n * The monitor runs periodically via the event bus / heartbeat service,\n * or can be invoked on-demand via /delegate status.\n */\n\nimport { createPublicClient, http, type Address, type Hex } from 'viem';\nimport { base, mainnet, arbitrum, optimism, polygon, sepolia, linea, baseSepolia } from 'viem/chains';\nimport type { Policy, DelegationInfo } from './policy-types.js';\nimport { isDelegationMode } from './policy-types.js';\nimport { getPolicyStore } from './policy-store.js';\nimport { getDelegationStore } from './delegation-store.js';\nimport { getDelegatedPolicies } from './delegation-service.js';\nimport {\n  DELEGATION_CONTRACTS,\n  NATIVE_PERIOD_ENFORCER_ABI,\n  ERC20_PERIOD_ENFORCER_ABI,\n  LIMITED_CALLS_ENFORCER_ABI,\n} from './delegation-types.js';\n\n// ─── Types ──────────────────────────────────────────────────────────────\n\nexport type AlertSeverity = 'info' | 'warning' | 'critical';\n\nexport interface DelegationAlert {\n  severity: AlertSeverity;\n  policyId: string;\n  policyName: string;\n  chainId: number;\n  message: string;\n  /** Suggested action for the user. */\n  action: string;\n}\n\nexport interface DelegationHealth {\n  policyId: string;\n  policyName: string;\n  chainId: number;\n  status: DelegationInfo['status'];\n  /** Spending used in current period (USD). Null if not tracked. */\n  spentUsd: number | null;\n  /** Spending limit for current period (USD). Null if no limit. */\n  limitUsd: number | null;\n  /** Percentage of limit used (0-100). Null if no limit. */\n  usagePercent: number | null;\n  /** Seconds until delegation expires. Null if no time-bound. */\n  expiresInSec: number | null;\n  /** Actions used vs limit. Null if no action limit. */\n  actionsUsed: number | null;\n  actionsLimit: number | null;\n  /** On-chain state reads (populated by checkOnChainState). */\n  onChain?: OnChainUsage;\n}\n\nexport interface OnChainUsage {\n  /** Native token (ETH) spent on-chain in wei. Null if no period enforcer. */\n  nativeSpentWei: bigint | null;\n  /** ERC-20 spent on-chain in smallest unit. Null if no period enforcer. */\n  erc20Spent: bigint | null;\n  /** Call count from on-chain LimitedCallsEnforcer. Null if no enforcer. */\n  callCount: bigint | null;\n  /** Whether on-chain data diverges from local tracking. */\n  driftDetected: boolean;\n  /** Human-readable drift description if any. */\n  driftDetails?: string;\n  /** Timestamp of the on-chain query (epoch ms). */\n  queriedAt: number;\n}\n\n// ─── Constants ──────────────────────────────────────────────────────────\n\n/** Warn when usage exceeds this percentage of limit. */\nconst SPENDING_WARN_THRESHOLD = 0.80;\n/** Critical when usage exceeds this percentage. */\nconst SPENDING_CRITICAL_THRESHOLD = 0.95;\n/** Warn when delegation expires within this many seconds. */\nconst EXPIRY_WARN_SEC = 86_400; // 24 hours\n/** Critical when delegation expires within this many seconds. */\nconst EXPIRY_CRITICAL_SEC = 3_600; // 1 hour\n\n// ─── Monitor ────────────────────────────────────────────────────────────\n\n/**\n * Check all active delegations for the given user and return health + alerts.\n */\nexport function checkDelegations(userId: string): {\n  health: DelegationHealth[];\n  alerts: DelegationAlert[];\n} {\n  if (!isDelegationMode()) {\n    return { health: [], alerts: [] };\n  }\n\n  const policies = getDelegatedPolicies(userId);\n  const store = getPolicyStore();\n  const health: DelegationHealth[] = [];\n  const alerts: DelegationAlert[] = [];\n\n  for (const policy of policies) {\n    const info = policy.delegation;\n    if (!info) continue;\n\n    const h = buildHealth(policy, info, store, userId);\n    health.push(h);\n\n    // Generate alerts based on health\n    const policyAlerts = generateAlerts(h, policy.name);\n    alerts.push(...policyAlerts);\n  }\n\n  return { health, alerts };\n}\n\n/**\n * Get a formatted summary of delegation health for display.\n */\nexport function formatDelegationHealth(userId: string): string {\n  const { health, alerts } = checkDelegations(userId);\n\n  if (health.length === 0) {\n    return 'No active delegations to monitor.';\n  }\n\n  const lines: string[] = [];\n  lines.push('**Delegation Health**');\n  lines.push('');\n\n  for (const h of health) {\n    const statusIcon = getStatusIcon(h.status);\n    lines.push(`${statusIcon} **${h.policyName}** (chain ${h.chainId})`);\n\n    if (h.usagePercent !== null && h.limitUsd !== null) {\n      const bar = renderProgressBar(h.usagePercent);\n      lines.push(`  Spending: $${(h.spentUsd ?? 0).toFixed(0)} / $${h.limitUsd.toFixed(0)} ${bar}`);\n    }\n\n    if (h.actionsUsed !== null && h.actionsLimit !== null) {\n      lines.push(`  Actions: ${h.actionsUsed} / ${h.actionsLimit}`);\n    }\n\n    if (h.expiresInSec !== null) {\n      lines.push(`  Expires: ${formatDuration(h.expiresInSec)}`);\n    }\n\n    lines.push('');\n  }\n\n  if (alerts.length > 0) {\n    lines.push('---');\n    lines.push('');\n\n    for (const a of alerts) {\n      const icon = a.severity === 'critical' ? '!!' : a.severity === 'warning' ? '!' : '-';\n      lines.push(`[${icon}] ${a.message}`);\n      lines.push(`    ${a.action}`);\n    }\n  }\n\n  return lines.join('\\n');\n}\n\n// ─── Internals ──────────────────────────────────────────────────────────\n\nfunction buildHealth(\n  policy: Policy,\n  info: DelegationInfo,\n  store: ReturnType<typeof getPolicyStore>,\n  userId: string,\n): DelegationHealth {\n  let spentUsd: number | null = null;\n  let limitUsd: number | null = null;\n  let usagePercent: number | null = null;\n  let expiresInSec: number | null = null;\n  let actionsUsed: number | null = null;\n  let actionsLimit: number | null = null;\n\n  // Extract spending info from policy rules using store helper methods\n  for (const rule of policy.rules) {\n    if (rule.type === 'spending_limit') {\n      limitUsd = rule.maxAmountUsd;\n      const periodMs = getPeriodMs(rule.period);\n      spentUsd = store.getSpendInWindow(userId, policy.id, periodMs);\n      usagePercent = limitUsd > 0 ? (spentUsd / limitUsd) * 100 : null;\n    }\n\n    if (rule.type === 'rate_limit') {\n      actionsLimit = rule.maxCalls;\n      actionsUsed = store.getCallsInWindow(userId, policy.id, rule.periodMs);\n    }\n  }\n\n  // Check delegation expiry from the expiresAt field\n  if (info.expiresAt) {\n    const expiryMs = new Date(info.expiresAt).getTime();\n    if (!isNaN(expiryMs)) {\n      const remainingMs = expiryMs - Date.now();\n      expiresInSec = Math.max(0, Math.floor(remainingMs / 1000));\n    }\n  }\n\n  return {\n    policyId: policy.id,\n    policyName: policy.name,\n    chainId: info.chainId,\n    status: info.status,\n    spentUsd,\n    limitUsd,\n    usagePercent,\n    expiresInSec,\n    actionsUsed,\n    actionsLimit,\n  };\n}\n\nfunction generateAlerts(h: DelegationHealth, policyName: string): DelegationAlert[] {\n  const alerts: DelegationAlert[] = [];\n\n  // Revoked on-chain\n  if (h.status === 'revoked') {\n    alerts.push({\n      severity: 'critical',\n      policyId: h.policyId,\n      policyName,\n      chainId: h.chainId,\n      message: `Delegation \"${policyName}\" has been revoked on-chain.`,\n      action: 'Create a new delegation with `/delegate create`.',\n    });\n  }\n\n  // Expired\n  if (h.status === 'expired') {\n    alerts.push({\n      severity: 'critical',\n      policyId: h.policyId,\n      policyName,\n      chainId: h.chainId,\n      message: `Delegation \"${policyName}\" has expired.`,\n      action: 'Create a new delegation with `/delegate create`.',\n    });\n  }\n\n  // Spending near limit\n  if (h.usagePercent !== null) {\n    if (h.usagePercent >= SPENDING_CRITICAL_THRESHOLD * 100) {\n      alerts.push({\n        severity: 'critical',\n        policyId: h.policyId,\n        policyName,\n        chainId: h.chainId,\n        message: `Spending at ${h.usagePercent.toFixed(0)}% of limit for \"${policyName}\".`,\n        action: 'Consider increasing the limit or creating a new delegation.',\n      });\n    } else if (h.usagePercent >= SPENDING_WARN_THRESHOLD * 100) {\n      alerts.push({\n        severity: 'warning',\n        policyId: h.policyId,\n        policyName,\n        chainId: h.chainId,\n        message: `Spending at ${h.usagePercent.toFixed(0)}% of limit for \"${policyName}\".`,\n        action: 'Monitor spending or adjust limits.',\n      });\n    }\n  }\n\n  // Actions near limit\n  if (h.actionsUsed !== null && h.actionsLimit !== null && h.actionsLimit > 0) {\n    const pct = (h.actionsUsed / h.actionsLimit) * 100;\n    if (pct >= SPENDING_CRITICAL_THRESHOLD * 100) {\n      alerts.push({\n        severity: 'critical',\n        policyId: h.policyId,\n        policyName,\n        chainId: h.chainId,\n        message: `${h.actionsUsed}/${h.actionsLimit} actions used for \"${policyName}\".`,\n        action: 'Create a new delegation to continue operating.',\n      });\n    } else if (pct >= SPENDING_WARN_THRESHOLD * 100) {\n      alerts.push({\n        severity: 'warning',\n        policyId: h.policyId,\n        policyName,\n        chainId: h.chainId,\n        message: `${h.actionsUsed}/${h.actionsLimit} actions used for \"${policyName}\".`,\n        action: 'Approaching action limit.',\n      });\n    }\n  }\n\n  // Expiry approaching\n  if (h.expiresInSec !== null) {\n    if (h.expiresInSec <= EXPIRY_CRITICAL_SEC) {\n      alerts.push({\n        severity: 'critical',\n        policyId: h.policyId,\n        policyName,\n        chainId: h.chainId,\n        message: `Delegation \"${policyName}\" expires in ${formatDuration(h.expiresInSec)}.`,\n        action: 'Renew immediately with `/delegate create`.',\n      });\n    } else if (h.expiresInSec <= EXPIRY_WARN_SEC) {\n      alerts.push({\n        severity: 'warning',\n        policyId: h.policyId,\n        policyName,\n        chainId: h.chainId,\n        message: `Delegation \"${policyName}\" expires in ${formatDuration(h.expiresInSec)}.`,\n        action: 'Plan renewal with `/delegate create`.',\n      });\n    }\n  }\n\n  return alerts;\n}\n\n// ─── Display Helpers ────────────────────────────────────────────────────\n\nfunction getStatusIcon(status: DelegationInfo['status']): string {\n  switch (status) {\n    case 'active':   return '[OK]';\n    case 'signed':   return '[--]';\n    case 'revoked':  return '[!!]';\n    case 'expired':  return '[!!]';\n    case 'unsigned': return '[..]';\n    default:         return '[??]';\n  }\n}\n\nfunction renderProgressBar(percent: number): string {\n  const width = 10;\n  const filled = Math.min(width, Math.round((percent / 100) * width));\n  const empty = width - filled;\n  return `[${'#'.repeat(filled)}${'-'.repeat(empty)}]`;\n}\n\nfunction formatDuration(seconds: number): string {\n  if (seconds <= 0) return 'expired';\n  if (seconds < 60) return `${seconds}s`;\n  if (seconds < 3600) return `${Math.round(seconds / 60)}m`;\n  if (seconds < 86400) return `${Math.round(seconds / 3600)}h`;\n  return `${Math.round(seconds / 86400)}d`;\n}\n\nfunction getPeriodMs(period: string): number {\n  switch (period) {\n    case 'hourly':  return 60 * 60 * 1000;\n    case 'daily':   return 24 * 60 * 60 * 1000;\n    case 'weekly':  return 7 * 24 * 60 * 60 * 1000;\n    case 'monthly': return 30 * 24 * 60 * 60 * 1000;\n    default:        return 24 * 60 * 60 * 1000;\n  }\n}\n\n// ─── On-Chain State Reading ─────────────────────────────────────────────\n\nconst CHAIN_CONFIGS: Record<number, any> = {\n  1: mainnet, 8453: base, 42161: arbitrum, 10: optimism,\n  137: polygon, 59144: linea, 11155111: sepolia, 84532: baseSepolia,\n};\n\nconst _clientCache = new Map<number, any>();\n\nfunction getMonitorClient(chainId: number): any {\n  let client = _clientCache.get(chainId);\n  if (client) return client;\n  const chain = CHAIN_CONFIGS[chainId];\n  if (!chain) return null;\n  client = createPublicClient({ chain, transport: http() });\n  _clientCache.set(chainId, client);\n  return client;\n}\n\n/**\n * Read on-chain enforcer state for a delegation.\n *\n * Queries the enforcer contracts that track cumulative usage:\n * - NativeTokenPeriodTransferEnforcer.spentMap → ETH spent\n * - ERC20PeriodTransferEnforcer.spentMap → ERC-20 spent\n * - LimitedCallsEnforcer.callCounts → call count\n *\n * Compares on-chain state with local tracking and flags drift.\n * All reads are best-effort: RPC errors are caught silently.\n */\nexport async function readOnChainUsage(\n  policyId: string,\n  delegationHash: Hex,\n  chainId: number,\n  localHealth: DelegationHealth,\n): Promise<OnChainUsage> {\n  const client = getMonitorClient(chainId);\n  const result: OnChainUsage = {\n    nativeSpentWei: null,\n    erc20Spent: null,\n    callCount: null,\n    driftDetected: false,\n    queriedAt: Date.now(),\n  };\n\n  if (!client || !delegationHash || delegationHash === '0x') {\n    return result;\n  }\n\n  const dmAddr = DELEGATION_CONTRACTS.DelegationManager;\n\n  // Read NativeTokenPeriodTransferEnforcer\n  try {\n    const [spent] = await client.readContract({\n      address: DELEGATION_CONTRACTS.NativeTokenPeriodTransferEnforcer,\n      abi: NATIVE_PERIOD_ENFORCER_ABI,\n      functionName: 'spentMap',\n      args: [dmAddr, delegationHash],\n    }) as [bigint, bigint];\n    result.nativeSpentWei = spent;\n  } catch {\n    // Enforcer not used or RPC error — skip\n  }\n\n  // Read ERC20PeriodTransferEnforcer\n  try {\n    const [spent] = await client.readContract({\n      address: DELEGATION_CONTRACTS.ERC20PeriodTransferEnforcer,\n      abi: ERC20_PERIOD_ENFORCER_ABI,\n      functionName: 'spentMap',\n      args: [dmAddr, delegationHash],\n    }) as [bigint, bigint];\n    result.erc20Spent = spent;\n  } catch {\n    // Enforcer not used or RPC error — skip\n  }\n\n  // Read LimitedCallsEnforcer\n  try {\n    const count = await client.readContract({\n      address: DELEGATION_CONTRACTS.LimitedCallsEnforcer,\n      abi: LIMITED_CALLS_ENFORCER_ABI,\n      functionName: 'callCounts',\n      args: [dmAddr, delegationHash],\n    }) as bigint;\n    result.callCount = count;\n  } catch {\n    // Enforcer not used or RPC error — skip\n  }\n\n  // Detect drift between on-chain and local tracking\n  result.driftDetected = false;\n  const driftNotes: string[] = [];\n\n  if (result.callCount !== null && localHealth.actionsUsed !== null) {\n    const onChainCalls = Number(result.callCount);\n    if (onChainCalls !== localHealth.actionsUsed) {\n      driftNotes.push(`calls: local=${localHealth.actionsUsed}, on-chain=${onChainCalls}`);\n    }\n  }\n\n  // For spending, we can only compare if we have ETH price to convert wei→USD.\n  // Flag drift if on-chain shows usage but local shows zero, or vice versa.\n  if (result.nativeSpentWei !== null && result.nativeSpentWei > 0n) {\n    if (localHealth.spentUsd === null || localHealth.spentUsd === 0) {\n      driftNotes.push('on-chain shows ETH spending but local tracking is empty');\n    }\n  }\n\n  if (driftNotes.length > 0) {\n    result.driftDetected = true;\n    result.driftDetails = driftNotes.join('; ');\n  }\n\n  return result;\n}\n\n/**\n * Enhanced health check that includes on-chain state reads.\n * Slower than checkDelegations (makes RPC calls) — use for /delegate status detail.\n */\nexport async function checkDelegationsWithOnChain(userId: string): Promise<{\n  health: DelegationHealth[];\n  alerts: DelegationAlert[];\n}> {\n  const base = checkDelegations(userId);\n  if (base.health.length === 0) return base;\n\n  const delegationStore = getDelegationStore();\n\n  // Enrich each health entry with on-chain data\n  const enriched = await Promise.all(\n    base.health.map(async (h) => {\n      const stored = delegationStore.load(h.policyId);\n      if (!stored) return h;\n\n      const policies = getDelegatedPolicies(userId);\n      const policy = policies.find(p => p.id === h.policyId);\n      const hash = policy?.delegation?.hash;\n      if (!hash || hash === '0x') return h;\n\n      try {\n        const onChain = await readOnChainUsage(\n          h.policyId,\n          hash as Hex,\n          h.chainId,\n          h,\n        );\n        h.onChain = onChain;\n\n        // Add drift alerts\n        if (onChain.driftDetected && onChain.driftDetails) {\n          base.alerts.push({\n            severity: 'warning',\n            policyId: h.policyId,\n            policyName: h.policyName,\n            chainId: h.chainId,\n            message: `Usage drift detected for \"${h.policyName}\": ${onChain.driftDetails}`,\n            action: 'Local tracking may be stale. On-chain enforcers have the authoritative state.',\n          });\n        }\n      } catch {\n        // On-chain read failed — keep local-only health\n      }\n\n      return h;\n    }),\n  );\n\n  return { health: enriched, alerts: base.alerts };\n}\n\n/**\n * Format on-chain usage for display (appended to health output).\n */\nexport function formatOnChainUsage(onChain: OnChainUsage): string {\n  const lines: string[] = [];\n\n  if (onChain.nativeSpentWei !== null) {\n    const ethSpent = Number(onChain.nativeSpentWei) / 1e18;\n    lines.push(`  On-chain ETH spent: ${ethSpent.toFixed(6)} ETH`);\n  }\n\n  if (onChain.erc20Spent !== null && onChain.erc20Spent > 0n) {\n    lines.push(`  On-chain ERC-20 spent: ${onChain.erc20Spent.toString()} (raw units)`);\n  }\n\n  if (onChain.callCount !== null) {\n    lines.push(`  On-chain call count: ${onChain.callCount.toString()}`);\n  }\n\n  if (onChain.driftDetected) {\n    lines.push(`  [!] Drift: ${onChain.driftDetails}`);\n  }\n\n  return lines.join('\\n');\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AA+EA,MAAM,0BAA0B;;AAEhC,MAAM,8BAA8B;;AAEpC,MAAM,kBAAkB;;AAExB,MAAM,sBAAsB;;;;AAO5B,SAAgB,iBAAiB,QAG/B;AACA,KAAI,CAAC,kBAAkB,CACrB,QAAO;EAAE,QAAQ,EAAE;EAAE,QAAQ,EAAE;EAAE;CAGnC,MAAM,WAAW,qBAAqB,OAAO;CAC7C,MAAM,QAAQ,gBAAgB;CAC9B,MAAM,SAA6B,EAAE;CACrC,MAAM,SAA4B,EAAE;AAEpC,MAAK,MAAM,UAAU,UAAU;EAC7B,MAAM,OAAO,OAAO;AACpB,MAAI,CAAC,KAAM;EAEX,MAAM,IAAI,YAAY,QAAQ,MAAM,OAAO,OAAO;AAClD,SAAO,KAAK,EAAE;EAGd,MAAM,eAAe,eAAe,GAAG,OAAO,KAAK;AACnD,SAAO,KAAK,GAAG,aAAa;;AAG9B,QAAO;EAAE;EAAQ;EAAQ;;;;;AAM3B,SAAgB,uBAAuB,QAAwB;CAC7D,MAAM,EAAE,QAAQ,WAAW,iBAAiB,OAAO;AAEnD,KAAI,OAAO,WAAW,EACpB,QAAO;CAGT,MAAM,QAAkB,EAAE;AAC1B,OAAM,KAAK,wBAAwB;AACnC,OAAM,KAAK,GAAG;AAEd,MAAK,MAAM,KAAK,QAAQ;EACtB,MAAM,aAAa,cAAc,EAAE,OAAO;AAC1C,QAAM,KAAK,GAAG,WAAW,KAAK,EAAE,WAAW,YAAY,EAAE,QAAQ,GAAG;AAEpE,MAAI,EAAE,iBAAiB,QAAQ,EAAE,aAAa,MAAM;GAClD,MAAM,MAAM,kBAAkB,EAAE,aAAa;AAC7C,SAAM,KAAK,iBAAiB,EAAE,YAAY,GAAG,QAAQ,EAAE,CAAC,MAAM,EAAE,SAAS,QAAQ,EAAE,CAAC,GAAG,MAAM;;AAG/F,MAAI,EAAE,gBAAgB,QAAQ,EAAE,iBAAiB,KAC/C,OAAM,KAAK,cAAc,EAAE,YAAY,KAAK,EAAE,eAAe;AAG/D,MAAI,EAAE,iBAAiB,KACrB,OAAM,KAAK,cAAc,eAAe,EAAE,aAAa,GAAG;AAG5D,QAAM,KAAK,GAAG;;AAGhB,KAAI,OAAO,SAAS,GAAG;AACrB,QAAM,KAAK,MAAM;AACjB,QAAM,KAAK,GAAG;AAEd,OAAK,MAAM,KAAK,QAAQ;GACtB,MAAM,OAAO,EAAE,aAAa,aAAa,OAAO,EAAE,aAAa,YAAY,MAAM;AACjF,SAAM,KAAK,IAAI,KAAK,IAAI,EAAE,UAAU;AACpC,SAAM,KAAK,OAAO,EAAE,SAAS;;;AAIjC,QAAO,MAAM,KAAK,KAAK;;AAKzB,SAAS,YACP,QACA,MACA,OACA,QACkB;CAClB,IAAI,WAA0B;CAC9B,IAAI,WAA0B;CAC9B,IAAI,eAA8B;CAClC,IAAI,eAA8B;CAClC,IAAI,cAA6B;CACjC,IAAI,eAA8B;AAGlC,MAAK,MAAM,QAAQ,OAAO,OAAO;AAC/B,MAAI,KAAK,SAAS,kBAAkB;AAClC,cAAW,KAAK;GAChB,MAAM,WAAW,YAAY,KAAK,OAAO;AACzC,cAAW,MAAM,iBAAiB,QAAQ,OAAO,IAAI,SAAS;AAC9D,kBAAe,WAAW,IAAK,WAAW,WAAY,MAAM;;AAG9D,MAAI,KAAK,SAAS,cAAc;AAC9B,kBAAe,KAAK;AACpB,iBAAc,MAAM,iBAAiB,QAAQ,OAAO,IAAI,KAAK,SAAS;;;AAK1E,KAAI,KAAK,WAAW;EAClB,MAAM,WAAW,IAAI,KAAK,KAAK,UAAU,CAAC,SAAS;AACnD,MAAI,CAAC,MAAM,SAAS,EAAE;GACpB,MAAM,cAAc,WAAW,KAAK,KAAK;AACzC,kBAAe,KAAK,IAAI,GAAG,KAAK,MAAM,cAAc,IAAK,CAAC;;;AAI9D,QAAO;EACL,UAAU,OAAO;EACjB,YAAY,OAAO;EACnB,SAAS,KAAK;EACd,QAAQ,KAAK;EACb;EACA;EACA;EACA;EACA;EACA;EACD;;AAGH,SAAS,eAAe,GAAqB,YAAuC;CAClF,MAAM,SAA4B,EAAE;AAGpC,KAAI,EAAE,WAAW,UACf,QAAO,KAAK;EACV,UAAU;EACV,UAAU,EAAE;EACZ;EACA,SAAS,EAAE;EACX,SAAS,eAAe,WAAW;EACnC,QAAQ;EACT,CAAC;AAIJ,KAAI,EAAE,WAAW,UACf,QAAO,KAAK;EACV,UAAU;EACV,UAAU,EAAE;EACZ;EACA,SAAS,EAAE;EACX,SAAS,eAAe,WAAW;EACnC,QAAQ;EACT,CAAC;AAIJ,KAAI,EAAE,iBAAiB;MACjB,EAAE,gBAAgB,8BAA8B,IAClD,QAAO,KAAK;GACV,UAAU;GACV,UAAU,EAAE;GACZ;GACA,SAAS,EAAE;GACX,SAAS,eAAe,EAAE,aAAa,QAAQ,EAAE,CAAC,kBAAkB,WAAW;GAC/E,QAAQ;GACT,CAAC;WACO,EAAE,gBAAgB,0BAA0B,IACrD,QAAO,KAAK;GACV,UAAU;GACV,UAAU,EAAE;GACZ;GACA,SAAS,EAAE;GACX,SAAS,eAAe,EAAE,aAAa,QAAQ,EAAE,CAAC,kBAAkB,WAAW;GAC/E,QAAQ;GACT,CAAC;;AAKN,KAAI,EAAE,gBAAgB,QAAQ,EAAE,iBAAiB,QAAQ,EAAE,eAAe,GAAG;EAC3E,MAAM,MAAO,EAAE,cAAc,EAAE,eAAgB;AAC/C,MAAI,OAAO,8BAA8B,IACvC,QAAO,KAAK;GACV,UAAU;GACV,UAAU,EAAE;GACZ;GACA,SAAS,EAAE;GACX,SAAS,GAAG,EAAE,YAAY,GAAG,EAAE,aAAa,qBAAqB,WAAW;GAC5E,QAAQ;GACT,CAAC;WACO,OAAO,0BAA0B,IAC1C,QAAO,KAAK;GACV,UAAU;GACV,UAAU,EAAE;GACZ;GACA,SAAS,EAAE;GACX,SAAS,GAAG,EAAE,YAAY,GAAG,EAAE,aAAa,qBAAqB,WAAW;GAC5E,QAAQ;GACT,CAAC;;AAKN,KAAI,EAAE,iBAAiB;MACjB,EAAE,gBAAgB,oBACpB,QAAO,KAAK;GACV,UAAU;GACV,UAAU,EAAE;GACZ;GACA,SAAS,EAAE;GACX,SAAS,eAAe,WAAW,eAAe,eAAe,EAAE,aAAa,CAAC;GACjF,QAAQ;GACT,CAAC;WACO,EAAE,gBAAgB,gBAC3B,QAAO,KAAK;GACV,UAAU;GACV,UAAU,EAAE;GACZ;GACA,SAAS,EAAE;GACX,SAAS,eAAe,WAAW,eAAe,eAAe,EAAE,aAAa,CAAC;GACjF,QAAQ;GACT,CAAC;;AAIN,QAAO;;AAKT,SAAS,cAAc,QAA0C;AAC/D,SAAQ,QAAR;EACE,KAAK,SAAY,QAAO;EACxB,KAAK,SAAY,QAAO;EACxB,KAAK,UAAY,QAAO;EACxB,KAAK,UAAY,QAAO;EACxB,KAAK,WAAY,QAAO;EACxB,QAAiB,QAAO;;;AAI5B,SAAS,kBAAkB,SAAyB;CAClD,MAAM,QAAQ;CACd,MAAM,SAAS,KAAK,IAAI,OAAO,KAAK,MAAO,UAAU,MAAO,MAAM,CAAC;CACnE,MAAM,QAAQ,QAAQ;AACtB,QAAO,IAAI,IAAI,OAAO,OAAO,GAAG,IAAI,OAAO,MAAM,CAAC;;AAGpD,SAAS,eAAe,SAAyB;AAC/C,KAAI,WAAW,EAAG,QAAO;AACzB,KAAI,UAAU,GAAI,QAAO,GAAG,QAAQ;AACpC,KAAI,UAAU,KAAM,QAAO,GAAG,KAAK,MAAM,UAAU,GAAG,CAAC;AACvD,KAAI,UAAU,MAAO,QAAO,GAAG,KAAK,MAAM,UAAU,KAAK,CAAC;AAC1D,QAAO,GAAG,KAAK,MAAM,UAAU,MAAM,CAAC;;AAGxC,SAAS,YAAY,QAAwB;AAC3C,SAAQ,QAAR;EACE,KAAK,SAAW,QAAO,OAAU;EACjC,KAAK,QAAW,QAAO,OAAU,KAAK;EACtC,KAAK,SAAW,QAAO,QAAc,KAAK;EAC1C,KAAK,UAAW,QAAO,MAAU,KAAK,KAAK;EAC3C,QAAgB,QAAO,OAAU,KAAK;;;AAM1C,MAAM,gBAAqC;CACzC,GAAG;CAAS,MAAM;CAAM,OAAO;CAAU,IAAI;CAC7C,KAAK;CAAS,OAAO;CAAO,UAAU;CAAS,OAAO;CACvD;AAED,MAAM,+BAAe,IAAI,KAAkB;AAE3C,SAAS,iBAAiB,SAAsB;CAC9C,IAAI,SAAS,aAAa,IAAI,QAAQ;AACtC,KAAI,OAAQ,QAAO;CACnB,MAAM,QAAQ,cAAc;AAC5B,KAAI,CAAC,MAAO,QAAO;AACnB,UAAS,mBAAmB;EAAE;EAAO,WAAW,MAAM;EAAE,CAAC;AACzD,cAAa,IAAI,SAAS,OAAO;AACjC,QAAO;;;;;;;;;;;;;AAcT,eAAsB,iBACpB,UACA,gBACA,SACA,aACuB;CACvB,MAAM,SAAS,iBAAiB,QAAQ;CACxC,MAAM,SAAuB;EAC3B,gBAAgB;EAChB,YAAY;EACZ,WAAW;EACX,eAAe;EACf,WAAW,KAAK,KAAK;EACtB;AAED,KAAI,CAAC,UAAU,CAAC,kBAAkB,mBAAmB,KACnD,QAAO;CAGT,MAAM,SAAS,qBAAqB;AAGpC,KAAI;EACF,MAAM,CAAC,SAAS,MAAM,OAAO,aAAa;GACxC,SAAS,qBAAqB;GAC9B,KAAK;GACL,cAAc;GACd,MAAM,CAAC,QAAQ,eAAe;GAC/B,CAAC;AACF,SAAO,iBAAiB;SAClB;AAKR,KAAI;EACF,MAAM,CAAC,SAAS,MAAM,OAAO,aAAa;GACxC,SAAS,qBAAqB;GAC9B,KAAK;GACL,cAAc;GACd,MAAM,CAAC,QAAQ,eAAe;GAC/B,CAAC;AACF,SAAO,aAAa;SACd;AAKR,KAAI;AAOF,SAAO,YANO,MAAM,OAAO,aAAa;GACtC,SAAS,qBAAqB;GAC9B,KAAK;GACL,cAAc;GACd,MAAM,CAAC,QAAQ,eAAe;GAC/B,CAAC;SAEI;AAKR,QAAO,gBAAgB;CACvB,MAAM,aAAuB,EAAE;AAE/B,KAAI,OAAO,cAAc,QAAQ,YAAY,gBAAgB,MAAM;EACjE,MAAM,eAAe,OAAO,OAAO,UAAU;AAC7C,MAAI,iBAAiB,YAAY,YAC/B,YAAW,KAAK,gBAAgB,YAAY,YAAY,aAAa,eAAe;;AAMxF,KAAI,OAAO,mBAAmB,QAAQ,OAAO,iBAAiB;MACxD,YAAY,aAAa,QAAQ,YAAY,aAAa,EAC5D,YAAW,KAAK,0DAA0D;;AAI9E,KAAI,WAAW,SAAS,GAAG;AACzB,SAAO,gBAAgB;AACvB,SAAO,eAAe,WAAW,KAAK,KAAK;;AAG7C,QAAO;;;;;;AAOT,eAAsB,4BAA4B,QAG/C;CACD,MAAM,OAAO,iBAAiB,OAAO;AACrC,KAAI,KAAK,OAAO,WAAW,EAAG,QAAO;CAErC,MAAM,kBAAkB,oBAAoB;AAyC5C,QAAO;EAAE,QAtCQ,MAAM,QAAQ,IAC7B,KAAK,OAAO,IAAI,OAAO,MAAM;AAE3B,OAAI,CADW,gBAAgB,KAAK,EAAE,SAAS,CAClC,QAAO;GAIpB,MAAM,OAFW,qBAAqB,OAAO,CACrB,MAAK,MAAK,EAAE,OAAO,EAAE,SAAS,EACjC,YAAY;AACjC,OAAI,CAAC,QAAQ,SAAS,KAAM,QAAO;AAEnC,OAAI;IACF,MAAM,UAAU,MAAM,iBACpB,EAAE,UACF,MACA,EAAE,SACF,EACD;AACD,MAAE,UAAU;AAGZ,QAAI,QAAQ,iBAAiB,QAAQ,aACnC,MAAK,OAAO,KAAK;KACf,UAAU;KACV,UAAU,EAAE;KACZ,YAAY,EAAE;KACd,SAAS,EAAE;KACX,SAAS,6BAA6B,EAAE,WAAW,KAAK,QAAQ;KAChE,QAAQ;KACT,CAAC;WAEE;AAIR,UAAO;IACP,CACH;EAE0B,QAAQ,KAAK;EAAQ;;;;;AAMlD,SAAgB,mBAAmB,SAA+B;CAChE,MAAM,QAAkB,EAAE;AAE1B,KAAI,QAAQ,mBAAmB,MAAM;EACnC,MAAM,WAAW,OAAO,QAAQ,eAAe,GAAG;AAClD,QAAM,KAAK,yBAAyB,SAAS,QAAQ,EAAE,CAAC,MAAM;;AAGhE,KAAI,QAAQ,eAAe,QAAQ,QAAQ,aAAa,GACtD,OAAM,KAAK,4BAA4B,QAAQ,WAAW,UAAU,CAAC,cAAc;AAGrF,KAAI,QAAQ,cAAc,KACxB,OAAM,KAAK,0BAA0B,QAAQ,UAAU,UAAU,GAAG;AAGtE,KAAI,QAAQ,cACV,OAAM,KAAK,gBAAgB,QAAQ,eAAe;AAGpD,QAAO,MAAM,KAAK,KAAK"}