{"version":3,"sources":["../src/marketplace/utils.ts"],"sourcesContent":["import type {\n  AttributeFilter,\n  Token,\n  TokenContract,\n} from \"@dojoengine/torii-wasm/types\";\nimport { addAddressPadding, getChecksumAddress } from \"starknet\";\nimport type {\n  AttributeFilterInput,\n  AttributeFilterInputValue,\n  NormalizedToken,\n  ResolveTokenImage,\n} from \"./types\";\n\nexport const DEFAULT_PROJECT_ID = \"arcade-main\";\n\nconst IMAGE_CANDIDATE_KEYS = [\n  \"image\",\n  \"image_url\",\n  \"imageUrl\",\n  \"thumbnail\",\n  \"thumbnail_url\",\n  \"animation\",\n  \"animation_url\",\n] as const;\n\nexport function ensureArray(\n  value: AttributeFilterInputValue,\n): Array<string | number | bigint> {\n  if (\n    typeof value === \"string\" ||\n    typeof value === \"number\" ||\n    typeof value === \"bigint\"\n  ) {\n    return [value];\n  }\n\n  if (value && Symbol.iterator in Object(value)) {\n    return Array.from(value as Iterable<string | number | bigint>);\n  }\n\n  return [];\n}\n\nexport function parseJsonSafe<T = any>(\n  value: unknown,\n  fallback: T | null = null,\n): T | null {\n  if (value == null) {\n    return fallback;\n  }\n\n  if (typeof value !== \"string\") {\n    return value as T;\n  }\n\n  try {\n    return JSON.parse(value) as T;\n  } catch (_error) {\n    return fallback;\n  }\n}\n\nexport function inferImageFromMetadata(metadata: any): string | undefined {\n  if (!metadata) return undefined;\n\n  for (const key of IMAGE_CANDIDATE_KEYS) {\n    const candidate = metadata?.[key];\n    if (typeof candidate === \"string\" && candidate.length > 0) {\n      return candidate;\n    }\n  }\n\n  if (metadata?.properties && typeof metadata.properties === \"object\") {\n    for (const key of IMAGE_CANDIDATE_KEYS) {\n      const candidate = metadata.properties?.[key];\n      if (typeof candidate === \"string\" && candidate.length > 0) {\n        return candidate;\n      }\n    }\n  }\n\n  return undefined;\n}\n\nexport function normalizeAttributeFilters(\n  filters?: AttributeFilterInput,\n): AttributeFilter[] {\n  if (!filters) return [];\n\n  return Object.entries(filters).flatMap(([traitName, traitValues]) => {\n    if (traitValues == null) return [];\n\n    return ensureArray(traitValues).map((traitValue) => ({\n      trait_name: traitName,\n      trait_value: traitValue.toString(),\n    }));\n  });\n}\n\nexport function resolveProjects(\n  projects: string[] | undefined,\n  defaultProject: string = DEFAULT_PROJECT_ID,\n): string[] {\n  if (!projects || projects.length === 0) {\n    return [defaultProject];\n  }\n\n  return Array.from(new Set(projects.filter(Boolean)));\n}\n\nexport function normalizeTokenIds(tokenIds?: string[]): string[] {\n  if (!tokenIds || tokenIds.length === 0) return [];\n  return tokenIds\n    .map((id) => {\n      try {\n        return BigInt(id).toString();\n      } catch (_error) {\n        return id;\n      }\n    })\n    .filter((id): id is string => typeof id === \"string\" && id.length > 0);\n}\n\nexport async function normalizeTokens(\n  tokens: Token[],\n  projectId: string,\n  options: {\n    fetchImages: boolean;\n    resolveTokenImage?: ResolveTokenImage;\n  },\n): Promise<NormalizedToken[]> {\n  const { fetchImages, resolveTokenImage } = options;\n  const resolver = resolveTokenImage ?? defaultResolveTokenImage;\n\n  const resolved = await Promise.all(\n    tokens.map(async (token) => {\n      const checksumAddress = getChecksumAddress(token.contract_address);\n      const metadata = parseJsonSafe(token.metadata, token.metadata);\n\n      let image: string | undefined;\n      if (fetchImages) {\n        const maybeImage = await resolver(token, { projectId });\n        if (typeof maybeImage === \"string\" && maybeImage.length > 0) {\n          image = maybeImage;\n        }\n\n        if (!image) {\n          const inferred = inferImageFromMetadata(metadata);\n          if (inferred) {\n            image = inferred;\n          }\n        }\n      }\n\n      return {\n        ...token,\n        contract_address: checksumAddress,\n        metadata,\n        image,\n      };\n    }),\n  );\n\n  return resolved;\n}\n\nasync function fetchToriiStatic(url: string): Promise<string | undefined> {\n  try {\n    const response = await fetch(url, { method: \"HEAD\" });\n    if (response.ok) {\n      return url;\n    }\n  } catch (_error) {\n    // Ignore network errors and fall back to metadata inference\n  }\n  return undefined;\n}\n\nexport async function defaultResolveTokenImage(\n  token: Token,\n  context: { projectId: string },\n  fetchImage = false,\n): Promise<string | undefined> {\n  const { projectId } = context;\n  if (!token.contract_address || !token.token_id) {\n    return undefined;\n  }\n\n  const url = `https://api.cartridge.gg/x/${projectId}/torii/static/${addAddressPadding(token.contract_address)}/${addAddressPadding(token.token_id)}/image`;\n  return fetchImage ? fetchToriiStatic(url) : url;\n}\n\nexport async function defaultResolveContractImage(\n  contract: TokenContract,\n  context: { projectId: string },\n): Promise<string | undefined> {\n  const { projectId } = context;\n  if (!contract.contract_address) return undefined;\n\n  const url = `https://api.cartridge.gg/x/${projectId}/torii/static/${addAddressPadding(contract.contract_address)}/image`;\n  return fetchToriiStatic(url);\n}\n"],"mappings":"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","names":[]}