{"version":3,"file":"viem.mjs","sources":["../../src/signers/http.ts","../../src/signers/validation.ts","../../src/signers/utils.ts","../../src/signers/viem.ts","../../src/signers/vault.ts"],"sourcesContent":["import type { SignerConfig } from '../types/signers';\n\nfunction sanitizeErrorMessage(status: number, text: string): string {\n  // Sanitize error messages to avoid leaking sensitive server information\n  let errorMessage = `Emblem signer error ${status}`;\n\n  if (status >= 500) {\n    errorMessage += ': Internal server error';\n  } else if (status === 401 || status === 403) {\n    errorMessage += ': Authentication failed';\n  } else if (status === 404) {\n    errorMessage += ': Resource not found';\n  } else if (status === 405) {\n    errorMessage += ': Method not allowed';\n  } else if (text) {\n    // For 4xx client errors, include limited error details\n    errorMessage += `: ${text.substring(0, 200)}`; // Limit to 200 chars\n  }\n\n  return errorMessage;\n}\n\nasync function resolveAuthHeaders(config: SignerConfig): Promise<Record<string, string>> {\n  // Priority: custom headers -> jwt/getJwt/sdk -> apiKey (deprecated)\n  if (typeof config.getAuthHeaders === 'function') {\n    const h = await config.getAuthHeaders();\n    if (h && typeof h === 'object') return h;\n  }\n\n  const tok =\n    config.jwt ??\n    (typeof config.getJwt === 'function' ? await config.getJwt() : undefined) ??\n    config.sdk?.getSession()?.authToken ??\n    undefined;\n\n  if (tok) {\n    return { Authorization: `Bearer ${tok}` };\n  }\n\n  // apiKey is deprecated but still supported as fallback\n  if (config.apiKey) {\n    return { 'x-api-key': config.apiKey };\n  }\n\n  throw new Error(\n    'No authentication available: provide jwt, getJwt(), getAuthHeaders(), sdk, or apiKey'\n  );\n}\n\nexport async function emblemPost<T = unknown>(\n  path: string,\n  body: unknown,\n  config: SignerConfig\n): Promise<T> {\n  const baseUrl = config.baseUrl ?? 'https://api.emblemvault.ai';\n  const authHeaders = await resolveAuthHeaders(config);\n  const res = await fetch(`${baseUrl}${path}`, {\n    method: 'POST',\n    headers: {\n      'content-type': 'application/json',\n      ...authHeaders,\n    },\n    body: JSON.stringify(body, (_key: string, value: unknown) =>\n      typeof value === 'bigint' ? value.toString() : value\n    ),\n  });\n\n  if (!res.ok) {\n    const text = await res.text().catch(() => '');\n    throw new Error(sanitizeErrorMessage(res.status, text));\n  }\n\n  return res.json() as Promise<T>;\n}\n\nexport async function emblemGet<T = unknown>(path: string, config: SignerConfig): Promise<T> {\n  const baseUrl = config.baseUrl ?? 'https://api.emblemvault.ai';\n  const authHeaders = await resolveAuthHeaders(config);\n  const res = await fetch(`${baseUrl}${path}`, {\n    method: 'GET',\n    headers: authHeaders,\n  });\n\n  if (!res.ok) {\n    const text = await res.text().catch(() => '');\n    throw new Error(sanitizeErrorMessage(res.status, text));\n  }\n\n  return res.json() as Promise<T>;\n}\n","import type { SignerConfig } from '../types/signers';\n\n/**\n * Environment detection utilities for warning about unsafe usage patterns\n */\n\n/**\n * Detect if code is running in a browser environment\n */\nexport function isBrowserEnvironment(): boolean {\n  return typeof window !== 'undefined' && typeof document !== 'undefined';\n}\n\n/**\n * Check if we're in a Node.js server environment\n */\nexport function isNodeEnvironment(): boolean {\n  return (\n    typeof process !== 'undefined' && process.versions != null && process.versions.node != null\n  );\n}\n\n/**\n * Validate baseUrl format\n */\nexport function validateBaseUrl(baseUrl?: string): void {\n  if (!baseUrl) return; // undefined is ok, will use default\n\n  if (!baseUrl.startsWith('http://') && !baseUrl.startsWith('https://')) {\n    throw new Error('baseUrl must be a valid HTTP(S) URL');\n  }\n\n  // Warn about http (not https)\n  if (\n    baseUrl.startsWith('http://') &&\n    !baseUrl.includes('localhost') &&\n    !baseUrl.includes('127.0.0.1')\n  ) {\n    console.warn(\n      '[Emblem Security Warning] baseUrl uses HTTP instead of HTTPS. This is insecure for production use.'\n    );\n  }\n}\n\n/**\n * Validate Ethereum address format\n */\nexport function validateEthereumAddress(address: string): void {\n  if (!address || typeof address !== 'string') {\n    throw new Error('Address is required');\n  }\n\n  if (!address.startsWith('0x')) {\n    throw new Error('Address must start with 0x');\n  }\n\n  if (!/^0x[0-9a-fA-F]{40}$/.test(address)) {\n    throw new Error('Invalid Ethereum address format');\n  }\n}\n\n/**\n * Validate vault ID\n */\nexport function validateVaultId(vaultId: string): void {\n  if (!vaultId || typeof vaultId !== 'string') {\n    throw new Error('vaultId is required');\n  }\n\n  if (vaultId.trim() === '') {\n    throw new Error('vaultId cannot be empty');\n  }\n}\n\n/**\n * Safe number conversion with bounds checking\n */\nexport function toSafeNumber(value: unknown, fieldName: string): number {\n  const num = Number(value);\n\n  if (!Number.isSafeInteger(num)) {\n    throw new Error(\n      `${fieldName} value ${value} exceeds safe integer range (max: ${Number.MAX_SAFE_INTEGER})`\n    );\n  }\n\n  return num;\n}\n\n/**\n * Extended config with security options\n */\nexport interface SignerSecurityConfig extends SignerConfig {\n  /**\n   * Enable debug logging for security-related checks\n   * @default false\n   */\n  debugSecurity?: boolean;\n}\n\n/**\n * Validate signer configuration\n */\nexport function validateSignerConfig(config: SignerSecurityConfig): void {\n  // Validate auth: require at least one method\n  const hasJwt = !!config.jwt;\n  const hasGetJwt = typeof config.getJwt === 'function';\n  const hasHeaders = typeof config.getAuthHeaders === 'function';\n  const hasSdk = !!config.sdk && typeof config.sdk.getSession === 'function';\n  const hasApiKey = !!config.apiKey;\n\n  if (!hasJwt && !hasGetJwt && !hasHeaders && !hasSdk && !hasApiKey) {\n    throw new Error('Authentication required: provide jwt, getJwt(), getAuthHeaders(), sdk, or apiKey');\n  }\n\n  // Validate baseUrl if provided\n  if (config.baseUrl) {\n    validateBaseUrl(config.baseUrl);\n  }\n\n  // Security audit logging\n  if (config.debugSecurity) {\n    console.log('[Emblem Security Debug]', {\n      environment: isBrowserEnvironment() ? 'browser' : 'node',\n      hasBaseUrl: !!config.baseUrl,\n      timestamp: new Date().toISOString(),\n    });\n  }\n}\n","import type { Hex } from '../types/signers';\nimport { toSafeNumber } from './validation';\n\nexport function toHexIfBigInt(v: unknown): unknown {\n  return typeof v === 'bigint' ? '0x' + v.toString(16) : v;\n}\n\n/**\n * viem txs sometimes have bigint / hex / optional fields. Ethers serializers\n * accept hex strings for numeric fields. Normalize where helpful.\n */\nexport function normalizeTxForEmblem(tx: Record<string, unknown>): Record<string, unknown> {\n  const out: Record<string, unknown> = { ...tx };\n\n  if (out.value !== undefined) out.value = toHexIfBigInt(out.value);\n  if (out.gas !== undefined) {\n    out.gasLimit = toHexIfBigInt(out.gas);\n    delete out.gas;\n  }\n  if (out.gasLimit !== undefined) out.gasLimit = toHexIfBigInt(out.gasLimit);\n  if (out.gasPrice !== undefined) out.gasPrice = toHexIfBigInt(out.gasPrice);\n  if (out.maxFeePerGas !== undefined) out.maxFeePerGas = toHexIfBigInt(out.maxFeePerGas);\n  if (out.maxPriorityFeePerGas !== undefined)\n    out.maxPriorityFeePerGas = toHexIfBigInt(out.maxPriorityFeePerGas);\n  if (out.nonce !== undefined) out.nonce = toSafeNumber(out.nonce, 'nonce');\n  if (out.chainId !== undefined) out.chainId = toSafeNumber(out.chainId, 'chainId');\n\n  // Some backends only accept legacy fields; fold EIP-1559 into gasPrice and drop unsupported keys\n  if (out.maxFeePerGas !== undefined || out.maxPriorityFeePerGas !== undefined) {\n    if (out.gasPrice === undefined && out.maxFeePerGas !== undefined) {\n      out.gasPrice = out.maxFeePerGas;\n    }\n    delete out.maxFeePerGas;\n    delete out.maxPriorityFeePerGas;\n  }\n\n  // Remove fields commonly unsupported by legacy serializers\n  delete out.type;\n  delete out.accessList;\n  delete out.account;\n  delete out.chain;\n  delete out.from;\n\n  return out;\n}\n\nexport function isHexString(value: unknown): value is Hex {\n  return typeof value === 'string' && /^0x[0-9a-fA-F]*$/.test(value);\n}\n\nexport function bytesToHex(bytes: ArrayLike<number>): Hex {\n  let out = '0x';\n  for (let i = 0; i < bytes.length; i++) {\n    out += (bytes[i] as number).toString(16).padStart(2, '0');\n  }\n  return out as Hex;\n}\n","import type { Hex, SignerConfig, SignerVaultInfo } from '../types/signers';\nimport { emblemPost } from './http';\nimport { bytesToHex, isHexString, normalizeTxForEmblem } from './utils';\nimport { fetchVaultInfo } from './vault';\n\n// Minimal viem types to avoid hard dependency\ninterface ViemAccount {\n  address: Hex;\n  signMessage: (args: { message: unknown }) => Promise<Hex>;\n  signTypedData: (typedData: unknown) => Promise<Hex>;\n  signTransaction: (tx: unknown, opts?: unknown) => Promise<Hex>;\n  type: 'local';\n  source: 'custom';\n  publicKey?: Hex;\n}\n\nexport async function toViemAccount(\n  config: SignerConfig,\n  infoOverride?: SignerVaultInfo\n): Promise<ViemAccount> {\n  const info = infoOverride ?? (await fetchVaultInfo(config));\n  const { evmAddress, vaultId } = info;\n\n  // Dynamically import viem's toAccount\n  // String concatenation prevents bundlers from statically analyzing the import\n  // DO NOT CHANGE THIS PATTERN - using a literal import('viem/accounts') breaks Next.js/Webpack builds\n  // when viem is not installed. The variable indirection is intentional. - Shannon\n  const moduleName = 'viem/accounts';\n  const { toAccount } = await import(/* @vite-ignore */ moduleName);\n\n  return toAccount({\n    address: evmAddress,\n\n    async signMessage({ message }: { message: unknown }): Promise<Hex> {\n      let payload: string;\n      let isRaw = false;\n\n      if (typeof message === 'string') {\n        payload = message;\n        isRaw = false;\n      } else if (message && typeof (message as { raw?: unknown }).raw !== 'undefined') {\n        const raw = (message as { raw: unknown }).raw;\n        payload = typeof raw === 'string' ? raw : bytesToHex(raw as ArrayLike<number>);\n        isRaw = true;\n      } else if (message instanceof Uint8Array) {\n        payload = bytesToHex(message);\n        isRaw = false;\n      } else if (isHexString(message)) {\n        payload = message as string;\n        isRaw = false;\n      } else {\n        // Don't silently convert objects to \"[object Object]\"\n        throw new Error(\n          `Unsupported message type: ${typeof message}. Expected string, Uint8Array, or hex string.`\n        );\n      }\n\n      const data = await emblemPost<{\n        signerAddress: string;\n        signature: Hex;\n      }>('/sign-eth-message', { vaultId, message: payload, raw: isRaw }, config);\n\n      return data.signature;\n    },\n\n    async signTypedData(typedData: unknown): Promise<Hex> {\n      const { domain, types, primaryType, message } = typedData as {\n        domain: unknown;\n        types: unknown;\n        primaryType?: string;\n        message: unknown;\n      };\n      const data = await emblemPost<{\n        signerAddress: string;\n        signature: Hex;\n      }>('/sign-typed-message', { vaultId, domain, types, primaryType, message }, config);\n\n      return data.signature;\n    },\n\n    async signTransaction(tx: unknown, _opts?: unknown): Promise<Hex> {\n      const normalizedTx = normalizeTxForEmblem(tx as Record<string, unknown>);\n\n      const data = await emblemPost<{\n        signedTransaction: Hex;\n      }>('/sign-eth-tx', { vaultId, transaction: normalizedTx }, config);\n\n      return data.signedTransaction;\n    },\n  }) as ViemAccount;\n}\n","import type { SignerConfig, SignerVaultInfo, Hex } from '../types/signers';\nimport { emblemPost } from './http';\n\nexport async function fetchVaultInfo(config: SignerConfig): Promise<SignerVaultInfo> {\n  // Note: The server only supports POST for /vault/info\n  const data: Partial<{\n    vaultId: string;\n    address: string;\n    evmAddress: Hex;\n    created_by?: string;\n  }> = await emblemPost('/vault/info', {}, config);\n\n  // Validate required response data (vaultId + evmAddress are required for EVM)\n  if (!data || !data.vaultId || !data.evmAddress) {\n    throw new Error('Invalid vault info response: missing required fields');\n  }\n\n  if (!String(data.evmAddress).startsWith('0x')) {\n    throw new Error('Invalid evmAddress format in response');\n  }\n\n  return {\n    vaultId: data.vaultId,\n    tokenId: data.vaultId,\n    address: data.address || '', // Solana address may be absent; keep optional\n    evmAddress: data.evmAddress as Hex,\n    created_by: data.created_by,\n  };\n}\n"],"names":["async","emblemPost","path","body","config","baseUrl","authHeaders","getAuthHeaders","h","tok","jwt","getJwt","undefined","sdk","getSession","authToken","Authorization","apiKey","Error","resolveAuthHeaders","res","fetch","method","headers","JSON","stringify","_key","value","toString","ok","text","catch","status","errorMessage","substring","sanitizeErrorMessage","json","toSafeNumber","fieldName","num","Number","isSafeInteger","MAX_SAFE_INTEGER","toHexIfBigInt","v","bytesToHex","bytes","out","i","length","padStart","toViemAccount","infoOverride","info","data","vaultId","evmAddress","String","startsWith","tokenId","address","created_by","fetchVaultInfo","toAccount","import","signMessage","message","payload","isRaw","raw","Uint8Array","test","signature","signTypedData","typedData","domain","types","primaryType","signTransaction","tx","_opts","normalizedTx","gas","gasLimit","gasPrice","maxFeePerGas","maxPriorityFeePerGas","nonce","chainId","type","accessList","account","chain","from","normalizeTxForEmblem","transaction","signedTransaction"],"mappings":"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"}