{"version":3,"file":"input.mjs","names":[],"sources":["../../src/core/ens.ts","../../src/core/input.ts"],"sourcesContent":["/*\n * ENS (Ethereum Name Service) resolution\n *\n * Resolves .eth names to addresses via public ENS APIs. No API key, no keccak256 dependency — pure\n * HTTP.\n */\n\n/*Public ENS resolution endpoints (fallback chain) */\nconst RESOLVERS = [\n  (name: string) => `https://api.ensideas.com/ens/resolve/${encodeURIComponent(name)}`,\n  (name: string) => `https://api.ensdata.net/${encodeURIComponent(name)}`,\n];\n\n/**\n * Cheap `.eth` shape check. It does not resolve the name or verify ownership.\n *\n * @param {string} input - The `input` value.\n * @returns {boolean} The resulting value.\n */\nexport function isEnsName(input: string): boolean {\n  const lower = input.toLowerCase().trim();\n  return lower.endsWith(\".eth\") && lower.length > 4;\n}\n\n/**\n * Match a 20-byte EVM hex address. This is not a validator for non-EVM chains.\n *\n * @param {string} input - The `input` value.\n * @returns {boolean} The resulting value.\n */\nexport function isAddress(input: string): boolean {\n  return /^0x[0-9a-fA-F]{40}$/.test(input.trim());\n}\n\n/**\n * Resolve an ENS name through public HTTP resolvers.\n *\n * Resolver failures are tried in order and collapse to `null` when every endpoint fails.\n *\n * @param {string} name - The `name` value.\n * @returns {Promise<string | null>} The resulting value.\n */\nexport async function resolveEns(name: string): Promise<string | null> {\n  const normalized = name.toLowerCase().trim();\n\n  for (const buildUrl of RESOLVERS) {\n    const url = buildUrl(normalized);\n    const controller = new AbortController();\n    const timeout = setTimeout(() => controller.abort(), 5000);\n\n    try {\n      const res = await fetch(url, {\n        headers: { Accept: \"application/json\" },\n        signal: controller.signal,\n      });\n\n      if (!res.ok) continue;\n\n      const json = (await res.json()) as Record<string, unknown>;\n      const address = json.address as string | undefined;\n\n      if (address && /^0x[0-9a-fA-F]{40}$/.test(address)) {\n        return address;\n      }\n    } catch {\n      // Try next resolver\n    } finally {\n      clearTimeout(timeout);\n    }\n  }\n\n  return null;\n}\n","/** Address/input resolution — ENS names, raw addresses, tx hashes. */\nimport { NotFoundError } from \"./errors.js\";\nimport type { ChainKey } from \"./types.js\";\nimport { isEnsName, isAddress, resolveEns } from \"./ens.js\";\n\nexport type InputType = \"address\" | \"txhash\" | \"ens\";\n\nconst HEX_HASH_CHAINS: readonly ChainKey[] = [\n  \"bitcoin\",\n  \"litecoin\",\n  \"pepecoin\",\n  \"ecash\",\n  \"tron\",\n  \"cardano\",\n  \"decred\",\n  \"stellar\",\n];\n\nfunction isChainTransactionHash(input: string, chain?: ChainKey): boolean {\n  if (chain !== undefined && HEX_HASH_CHAINS.includes(chain))\n    return /^[0-9a-fA-F]{64}$/.test(input);\n  if (chain === \"solana\") return /^[1-9A-HJ-NP-Za-km-z]{64,88}$/.test(input);\n  return chain === \"sui\" && /^[1-9A-HJ-NP-Za-km-z]{43,44}$/.test(input);\n}\n\n/**\n * Classify raw user input, using chain-specific hash shapes where unambiguous.\n *\n * @param {string} input - The `input` value.\n * @param {ChainKey} chain - The `chain` value.\n * @returns {InputType} The resulting value.\n */\nexport function classifyInput(input: string, chain?: ChainKey): InputType {\n  const trimmed = input.trim();\n  if (chain === \"sui\" && /^0x[0-9a-fA-F]{64}$/.test(trimmed)) return \"address\";\n  if (isChainTransactionHash(trimmed, chain)) return \"txhash\";\n  if (/^0x[0-9a-fA-F]{64}$/.test(trimmed)) return \"txhash\";\n  if (isEnsName(trimmed)) return \"ens\";\n  if (isAddress(trimmed)) return \"address\";\n  return \"address\";\n}\n\n/**\n * Classify an input and resolve ENS names. Addresses and transaction hashes pass through unchanged.\n *\n * @throws {NotFoundError} When an ENS name cannot be resolved.\n *\n * @param {string} input - The `input` value.\n * @param {ChainKey} chain - The `chain` value.\n * @returns {Promise<{ address: string; type: InputType }>} The resulting value.\n */\nexport async function resolveInput(\n  input: string,\n  chain?: ChainKey,\n): Promise<{ address: string; type: InputType }> {\n  const trimmed = input.trim();\n  const type = classifyInput(trimmed, chain);\n\n  if (type === \"txhash\") {\n    return { address: trimmed, type };\n  }\n\n  if (type === \"address\") {\n    return { address: trimmed, type };\n  }\n\n  const resolved = await resolveEns(trimmed);\n  if (!resolved) {\n    throw new NotFoundError(`ENS name ${trimmed}`);\n  }\n  return { address: resolved, type };\n}\n\nconst MAX_TOOL_ADDRESS_BATCH = 20;\n\nfunction parseSerializedAddressList(input: string): readonly string[] | undefined {\n  const trimmed = input.trim();\n  if (!trimmed.startsWith(\"[\")) return undefined;\n\n  let parsed: unknown;\n  try {\n    parsed = JSON.parse(trimmed);\n  } catch {\n    return undefined;\n  }\n\n  if (!Array.isArray(parsed)) return undefined;\n  if (\n    parsed.length === 0 ||\n    parsed.length > MAX_TOOL_ADDRESS_BATCH ||\n    !parsed.every(\n      (item: unknown): item is string => typeof item === \"string\" && item.trim().length > 0,\n    )\n  ) {\n    throw new TypeError(\"Address list must contain 1 to 20 nonblank strings\");\n  }\n  return parsed;\n}\n\n/**\n * Resolve one address or an address list, including lists serialized by a tool host.\n *\n * @throws {NotFoundError} When any ENS name cannot be resolved.\n * @throws {TypeError} When a serialized tool list falls outside its input contract.\n *\n * @param {string | readonly string[]} input - The `input` value.\n * @param {ChainKey} chain - The `chain` value.\n * @returns {Promise<string[]>} The resulting value.\n */\nexport async function resolveAddresses(\n  input: string | readonly string[],\n  chain?: ChainKey,\n): Promise<string[]> {\n  const list = typeof input === \"string\" ? (parseSerializedAddressList(input) ?? [input]) : input;\n  const resolved = await Promise.all(list.map((item) => resolveInput(item, chain)));\n  return resolved.map((entry) => entry.address);\n}\n"],"mappings":";;AAQA,MAAM,YAAY,EACf,SAAiB,wCAAwC,mBAAmB,IAAI,MAChF,SAAiB,2BAA2B,mBAAmB,IAAI,GACtE;;;;;AAQA,SAAgB,UAAU,OAAwB;CAChD,OAAM,sBAAc,KAAc,MAAK,KAAA,CAAA;AACvC;;;;;;;EASF,IAAA;GACE,MAAO,MAAA,MAAA,MAAA,KAAsB;IAC/B,SAAA,EAAA,QAAA,mBAAA;;;;;;;;;CAUA;CACE,OAAM;AAEN;AAGE,IAAA,gBAAiC,4BAAW;CAE5C,qBAAI;CACF,wBAAkB;CAChB,oBAAW;AACX,CAAA;AACF,MAAC,kBAAA;CAED;CAGA;CAEA;CAGF;CAGE;CACF;CACF;CAEA;AACF;;;;;;;CCjEA,MAAM,UAAA,MAAA,KAAuC;CAC3C,IAAA,UAAA,SAAA,sBAAA,KAAA,OAAA,GAAA,OAAA;CACA,IAAA,uBAAA,SAAA,KAAA,GAAA,OAAA;CACA,IAAA,sBAAA,KAAA,OAAA,GAAA,OAAA;CACA,IAAA,UAAA,OAAA,GAAA,OAAA;CACA,IAAA,UAAA,OAAA,GAAA,OAAA;CACA,OAAA;AACA;AAEF,eAAA,aAAA,OAAA,OAAA;CAEA,MAAA,UAAS,MAAA,KAAA;CACP,MAAI,OAAA,cAAuB,SAAA,KAAgB;CAE3C,IAAI,SAAA,UAAU,OAAU;EACxB,SAAO;EACT;;;;;;;;CASA,OAAA;EACE,SAAM;EACN;CACA;AACA;AACA,MAAI,yBAAoB;AACxB,SAAI,2BAA2B,OAAA;CAC/B,MAAA,UAAO,MAAA,KAAA;CACT,IAAA,CAAA,QAAA,WAAA,GAAA,GAAA,OAAA,KAAA;;;;;;;;;;AAWA;AAKE,eAAa,iBAAc,OAAS,OAAK;CAEzC,MAAI,OAAS,OAAA,UACJ,WAAA,2BAAA,KAAA,KAAA,CAAA,KAAA,IAAA;CAAE,QAAA,MAAS,QAAA,IAAA,KAAA,KAAA,SAAA,aAAA,MAAA,KAAA,CAAA,CAAA,EAAA,CAAA,KAAA,UAAA,MAAA,OAAA;AAAS;AAG7B,SAAI,eAAS,WACJ,WAAA,kBAAA,YAAA"}