diff --git a/package.json b/package.json index f55b77b..04e9551 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ipfs-race", - "version": "0.2.2", + "version": "0.2.3", "description": "Resolve an IPFS path using multiple gateways and methods to get data the fastest.", "main": "dist/index.js", "scripts": { diff --git a/src/resolve.ts b/src/resolve.ts index 16b679c..9dbde65 100644 --- a/src/resolve.ts +++ b/src/resolve.ts @@ -44,6 +44,30 @@ interface ResolveOutput { */ async function resolve(uri: string, options?: ResolveOptions): Promise { + // Check if the URI is a base64 encoded string + if (uri.startsWith("data:")) { + const base64Content = uri.split(",")[1]; + if (!base64Content) { + throw new Error("Invalid base64 URI"); + } + const decodedContent = Buffer.from(base64Content, 'base64').toString('utf-8'); + let jsonResponse; + try { + jsonResponse = JSON.parse(decodedContent); + } catch (err) { + throw new Error("Failed to parse base64 encoded JSON"); + } + // Mock a Response object since fetch is not actually called + const response = new Response(JSON.stringify(jsonResponse), { + status: 200, + headers: { 'Content-Type': 'application/json' } + }); + return { + response, + urlResolvedFrom: uri, + }; + } + // The library will check many common spots for a fetch object. Depending on the ecosystem, lots of these variables don't // exist and throw errors, so I put them in try catch block. let fetchOverride: FetchType | undefined = options?.fetchOverride; diff --git a/src/test.ts b/src/test.ts index 0eb3b08..16554c0 100644 --- a/src/test.ts +++ b/src/test.ts @@ -13,6 +13,8 @@ describe("Tests the ipfs-race library.", () => { const testUrlSubdomainWithResult = {"name":"Iron Pigeon #10896 1st Edition","symbol":"XPTCS","description":"Mike Tyson brings his love for pigeons to the Metaverse by making them the second set in the Ex Populus Trading Card System. Mint your own Iron Pigeon and evolve your way to Legendary in the game Final Form. Learn more about Final Form: https://expopulus.com/games/final-form","seller_fee_basis_points":1000,"image":"https://ipfs.io/ipfs/QmbrCdrecFDeuDKukaC97vb3uZ5ioHiWzzb1VncLDKhFGA","external_url":"https://ironpigeons.expopulus.com","attributes":[{"trait_type":"Rarity","value":"rare"},{"trait_type":"Power","value":"286"},{"trait_type":"Health","value":"healthy"},{"trait_type":"Attack","value":"formidable"},{"trait_type":"Ability 1","value":"Evade"},{"trait_type":"House","value":"Machines"},{"trait_type":"Set Name","value":"Iron Pigeons"},{"trait_type":"Set Number","value":"2"},{"trait_type":"Edition","value":"1st Edition"},{"trait_type":"Card Number","value":"10896"},{"trait_type":"Body","value":"shredded white"},{"trait_type":"Costume","value":"high-top fade"},{"trait_type":"Beak","value":"standard"},{"trait_type":"Eyes","value":"pink"},{"trait_type":"Background","value":"blue"}],"collection":{"name":"Iron Pigeons","family":"Ex Populus Trading Card System"},"properties":{"creators":[{"address":"3NXTh2Bw64a72RGGJ2mh8V7YEW2jERGdESjqxyvBoXo1","share":35},{"address":"39gMkx283UsuKNt5ogETWdHHaGyBwbh8sU5cKAEQjU2w","share":15},{"address":"4MnG71K4EXDHFysnnftV9YLDhnUjfxY5jTE34QAx4Pho","share":50}],"files":[{"uri":"https://ipfs.io/ipfs/QmbrCdrecFDeuDKukaC97vb3uZ5ioHiWzzb1VncLDKhFGA","type":"image/png"},{"uri":"https://ipfs.io/ipfs/QmXT3yXQ6dEZawcGTwXjwQhTaDgKMwuTts6fU9S2jyyPoh","type":"image/png"},{"uri":"https://ipfs.io/ipfs/QmUCSep13eQCrNpAnQSwpadtJDgUW4D1CWUUooFYeUMUGt","type":"image/png"},{"uri":"https://ipfs.io/ipfs/QmY9KQYHTMxGHK7VizvmyqDFxR5LgRfEGjkJmuuax7nvVL","type":"image/png"}],"category":"image"}}; const testUrlWith2SubdomainsWhere1IsIpfs = "https://bafybeicqjhjcar5x7jvtz62zc4hbakczvktbu3zvbn3o3wnmqpbxgbdkli.ipfs.nftstorage.link/0.json"; const testUrlWith2SubdomainsWhere1IsIpfsResult = {"name":"Ryder One","symbol":"RO","description":"Wall Street Degens Bid Goodbye Seed phrases” is a special Ryder NFT for Magic Eden created by Quentin Saubadu.","seller_fee_basis_points":500,"image":"https://bafybeihphsiusu2iyw42qjfm7yngieayscfbxmzmjm4zovzt6lxszjelia.ipfs.nftstorage.link/0.png?ext=png","attributes":[],"collection":{"name":"Ryder One","family":"Ryder One"},"properties":{"creators":[{"address":"CxsRPwBU4HdpzEPGVAvX1vxnfSZFDBcMmv8TqBww7Mpw","share":100}],"category":"image","files":[{"uri":"https://bafybeihphsiusu2iyw42qjfm7yngieayscfbxmzmjm4zovzt6lxszjelia.ipfs.nftstorage.link/0.png?ext=png","type":"image/png"}]}}; + const base64Uri = "data:application/json;base64,eyJuYW1lIjogIjIwMjQgTVVSVEVDIC0gUlROIFN0YXJ0LVVwIEFsbGV5ICAyNSIsICJkZXNjcmlwdGlvbiI6ICJUaGFuayB5b3UgZm9yIHZpc2l0aW5nIERldm91ciBhdCBNVVJURUMgMjAyNC5cblZpc2l0IGRldm91ci5pbyB0byBsZWFybiBtb3JlIGFib3V0IERldm91ciEiLCAiYW5pbWF0aW9uX3VybCI6ICJpcGZzOi8vUW1VUG1Uc3ZqYW5tWXhHajlQSExQSHpMcThwRlVEZ21CUlhMTkhrMXhpODlKSC9SRU5ERVIlMjAxLm1wNCIsICJwcm9wZXJ0aWVzIjogeyJudW1iZXIiOiAyNSwgIm5hbWUiOiAiMjAyNCBNVVJURUMgLSBSVE4gU3RhcnQtVXAgQWxsZXkgIn19"; + const base64UriResult = {"name":"2024 MURTEC - RTN Start-Up Alley 25","description":"Thank you for visiting Devour at MURTEC 2024.\nVisit devour.io to learn more about Devour!","animation_url":"ipfs://QmUPmTsvjanmYxGj9PHLPHzLq8pFUDgmBRXLNHk1xi89JH/RENDER%201.mp4","properties":{"number":25,"name":"2024 MURTEC - RTN Start-Up Alley "}}; it("cid with no path", async () => { const {response, urlResolvedFrom} = await resolve(testCidWithNoPath); @@ -54,6 +56,11 @@ describe("Tests the ipfs-race library.", () => { expect(await response.json(), urlResolvedFrom).to.deep.equal(testUrlWith2SubdomainsWhere1IsIpfsResult); }); + it("base64 encoded JSON result", async () => { + const {response, urlResolvedFrom} = await resolve(base64Uri); + expect(await response.json()).to.deep.equal(base64UriResult); + }); + it("check that each default ipfs gateway to be a valid url", () => { for (const url of defaultIpfsGateways) { expect(isValidHttpUrl(url)).to.be.true;