@thurinlabs/identity-kit 1.3.4 → 1.3.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -64,7 +64,7 @@ Wrap your app (or just the part using identity-kit) in `IdentityKitProvider`. If
64
64
 
65
65
  // With options
66
66
  <IdentityKitProvider
67
- rpcUrl="https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY"
67
+ rpcUrl="https://your-node.example"
68
68
  neynarApiKey="YOUR_NEYNAR_KEY"
69
69
  baseUrl="https://thurin.id"
70
70
  >
@@ -98,7 +98,7 @@ Returns: `ThurinIdentity` with `address`, `ensName`, `ensAvatar`, `claims`, `tot
98
98
 
99
99
  When the identity can't be shown, `error` is a plain `Error` safe to display and `errorKind` says why: `'rpc'` (the RPC didn't answer, so nothing is known), `'not-found'` (the ENS name has no address), or `'read'` (the RPC answers but a registry read failed); `retry()` re-runs the lookups. `ThurinCard` shows these instead of zeros, and a follower count EFP didn't answer for shows `–` (1.3.4).
100
100
 
101
- `ensAvatar` is set only when loading it can't reveal the viewer to the name's owner: IPFS, Arweave, inline data, a content-addressed NFT, or `euc.li` (the ENS app's upload host). A plain `https://` avatar on the owner's own server is left out, since loading it would hand that server every viewer's IP (1.3.3; earlier versions loaded any avatar). The rule is `avatarUrl()` in the core; `useSafeAvatar(name, chainId)` is the hook.
101
+ `ensAvatar` is set only when loading it can't reveal the viewer to the name's owner: IPFS, Arweave, inline data, a content-addressed NFT, or `euc.li` (the ENS app's upload host). IPFS images load through `ipfs.filebase.io`, falling back to Pinata's public gateway if that fails (`IPFS_GATEWAYS`, `avatarFallbacks()`; 1.3.6). A plain `https://` avatar on the owner's own server is left out, since loading it would hand that server every viewer's IP (1.3.3; earlier versions loaded any avatar). The rule is `avatarUrl()` in the core; `useSafeAvatar(name, chainId)` is the hook.
102
102
 
103
103
  ### useAttestations
104
104
 
@@ -244,7 +244,7 @@ const resolver = await publicClient.getEnsResolver({ name: call.name })
244
244
  ```ts
245
245
  import { avatarUrl, parseNftAvatar, nftAvatarImage } from '@thurinlabs/identity-kit/core'
246
246
 
247
- avatarUrl('ipfs://Qm…') // 'https://ipfs.io/ipfs/Qm…'
247
+ avatarUrl('ipfs://Qm…') // 'https://ipfs.filebase.io/ipfs/Qm…' (avatarFallbacks() → Pinata)
248
248
  avatarUrl('https://euc.li/vitalik.eth') // allowed: ENS Labs' host, not the owner's
249
249
  avatarUrl('https://example.com/me.png') // null: the owner's server would see every viewer
250
250
  ```
@@ -300,7 +300,7 @@ For static sites, Jekyll blogs, WordPress, or any HTML page — use the standalo
300
300
  <div
301
301
  data-thurin-card="bendoubleu.eth"
302
302
  data-theme="thurin"
303
- data-rpc-url="https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY"
303
+ data-rpc-url="https://your-node.example"
304
304
  ></div>
305
305
 
306
306
  <script src="https://cdn.jsdelivr.net/npm/@thurinlabs/identity-kit@0/dist/embed.global.js"></script>
@@ -1724,7 +1724,8 @@ async function verifyProof(proof, fingerprint, neynarApiKey) {
1724
1724
  }
1725
1725
 
1726
1726
  // src/core/avatar.ts
1727
- var IPFS_GATEWAY = "https://ipfs.io/ipfs/";
1727
+ var IPFS_GATEWAYS = ["https://ipfs.filebase.io/ipfs/", "https://gateway.pinata.cloud/ipfs/"];
1728
+ var IPFS_GATEWAY = IPFS_GATEWAYS[0];
1728
1729
  var ARWEAVE_GATEWAY = "https://arweave.net/";
1729
1730
  function avatarUrl(uri) {
1730
1731
  if (typeof uri !== "string") return null;
@@ -1739,9 +1740,14 @@ function avatarUrl(uri) {
1739
1740
  if (m) return IPFS_GATEWAY + m[1];
1740
1741
  return null;
1741
1742
  }
1743
+ function avatarFallbacks(url) {
1744
+ if (!url || !url.startsWith(IPFS_GATEWAYS[0])) return [];
1745
+ const path = url.slice(IPFS_GATEWAYS[0].length);
1746
+ return IPFS_GATEWAYS.slice(1).map((g) => g + path);
1747
+ }
1742
1748
  function parseNftAvatar(raw) {
1743
- const m = typeof raw === "string" && raw.trim().match(/^eip155:(\d+)\/(erc721|erc1155):(0x[0-9a-fA-F]{40})\/(\d+)$/);
1744
- return m ? { chainId: Number(m[1]), standard: m[2], contract: m[3], tokenId: BigInt(m[4]) } : null;
1749
+ const m = typeof raw === "string" && raw.trim().match(/^eip155:(\d+)\/(erc721|erc1155):(0x[0-9a-fA-F]{40})\/(\d+)$/i);
1750
+ return m ? { chainId: Number(m[1]), standard: m[2].toLowerCase(), contract: m[3], tokenId: BigInt(m[4]) } : null;
1745
1751
  }
1746
1752
  var NFT_AVATAR_ABI = [
1747
1753
  { type: "function", name: "tokenURI", stateMutability: "view", inputs: [{ type: "uint256" }], outputs: [{ type: "string" }] },
@@ -1755,9 +1761,17 @@ async function nftAvatarImage(tokenUri, tokenId, fetchFn = fetch) {
1755
1761
  else {
1756
1762
  const url = uri.startsWith("data:") ? null : avatarUrl(uri);
1757
1763
  if (!url) return null;
1758
- const res = await fetchFn(url);
1759
- if (!res.ok) return null;
1760
- meta = await res.json();
1764
+ for (const candidate of [url, ...avatarFallbacks(url)]) {
1765
+ try {
1766
+ const res = await fetchFn(candidate);
1767
+ if (res.ok) {
1768
+ meta = await res.json();
1769
+ break;
1770
+ }
1771
+ } catch {
1772
+ }
1773
+ }
1774
+ if (!meta) return null;
1761
1775
  }
1762
1776
  return avatarUrl(meta?.image ?? meta?.image_url);
1763
1777
  }
@@ -2054,9 +2068,11 @@ export {
2054
2068
  proofHref,
2055
2069
  proofSecondaryHref,
2056
2070
  verifyProof,
2071
+ IPFS_GATEWAYS,
2057
2072
  IPFS_GATEWAY,
2058
2073
  ARWEAVE_GATEWAY,
2059
2074
  avatarUrl,
2075
+ avatarFallbacks,
2060
2076
  parseNftAvatar,
2061
2077
  NFT_AVATAR_ABI,
2062
2078
  nftAvatarImage,
@@ -2089,4 +2105,4 @@ export {
2089
2105
  parseRecord,
2090
2106
  fetchRecords
2091
2107
  };
2092
- //# sourceMappingURL=chunk-CF64QQZO.js.map
2108
+ //# sourceMappingURL=chunk-HDBHNN4I.js.map