Last active
June 18, 2023 13:46
-
-
Save bellbind/981fe90b937dad423c076efe9fa8883f to your computer and use it in GitHub Desktop.
[helia][browser] Simple example of using Helia IPFS node on browser
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!doctype html> | |
<html> | |
<head> | |
<link rel="icon" href="data:image/x-icon," /> | |
<script type="module" src="./multi-nodes-cdn.js"></script> | |
</head> | |
<body> | |
Open JavaScript console to check running logs | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// This example runs 2 helia nodes for publishing and receiving data with cid string | |
// from CDN | |
import "https://cdn.jsdelivr.net/npm/helia@^1.3.2/dist/index.min.js"; | |
import "https://cdn.jsdelivr.net/npm/@helia/unixfs@^1.3.0/dist/index.min.js"; | |
import "https://cdn.jsdelivr.net/npm/multiformats@^12.0.1/dist/index.min.js"; | |
const helia = Helia, {unixfs} = HeliaUnixfs, {CID} = Multiformats; | |
//import * as helia from "helia"; | |
//import {unixfs} from "@helia/unixfs"; | |
//import {CID} from "multiformats/cid"; | |
//NOTE: current browsers not yet implement ReadableStream[Symbol.asyncIterator] | |
const rsWithAi = rs => { // supply ReadableStream[Symbol.asyncIterator] | |
if (!(Symbol.asyncIterator in rs)) rs[Symbol.asyncIterator] = async function *() { | |
const reader = rs.getReader(); | |
try { | |
while (true) { | |
const {value, done} = await reader.read(); | |
if (done) return; | |
yield value; | |
} | |
} finally { | |
reader.releaseLock(); | |
} | |
}; | |
return rs; | |
}; | |
//[1. start] | |
const node1 = await helia.createHelia(); // default: use memory strores (non-persistent) | |
const node2 = await helia.createHelia(); | |
const node1fs = unixfs(node1); | |
const node2fs = unixfs(node2); | |
console.log("[createHelia]"); | |
//[2. wait network established] | |
//IMPORTANT: For call dial() each other, must await libp2p.getMultiaddrs().length > 0 | |
// - It is a big diffrence from js-ipfs behavior of `await createIpfs()` | |
while (node1.libp2p.getMultiaddrs().length === 0) await new Promise(f => setTimeout(f, 500)); | |
while (node2.libp2p.getMultiaddrs().length === 0) await new Promise(f => setTimeout(f, 500)); | |
console.log("[libp2p.getMultiaddrs]1", node1.libp2p.getMultiaddrs().map(ma => `${ma}`)); | |
console.log("[libp2p.getMultiaddrs]2", node2.libp2p.getMultiaddrs().map(ma => `${ma}`)); | |
// connect nodes directly with dial() | |
for (const ma of node2.libp2p.getMultiaddrs()) { | |
try { | |
//IMPORTANT: some of transports may fail to dial() directly | |
await node1.libp2p.dial(ma); | |
console.log(`[libp2p.dial] success: ${ma}`); | |
break; | |
} catch (error) { | |
console.log(`[libp2p.dial] failed: ${ma}`); | |
} // dial to other ma | |
} | |
//[3. feed content] | |
// example data | |
const blob = new Blob([new TextEncoder().encode("Hello World!")], {type: "text/plain;charset=utf-8"}); | |
console.log("[Blob]", blob); | |
// publish blob as CID with addByteStream() | |
const cid = await node1fs.addByteStream(rsWithAi(blob.stream())); | |
//const cid = await node1fs.addBytes(new Uint8Array(await blob.arrayBuffer())); | |
console.log("[unixfs.addByteStream]", cid); | |
const cidStr = cid.toString(); | |
const cidAlt = CID.parse(cidStr); | |
//NOTE: pins only accept when block data existed in node; cannot reserve pinning cid in advance | |
const ret1 = await node1.pins.add(cidAlt); //NOTE: pins.add() not accept CID string | |
console.log("[pins.add]", ret1); | |
//[4. retrieve content] | |
// NOTE: helia's pins needs stored blocks in blockstore (e.g. cannnot pin before stat()/ls()/cat()) | |
const stat = await node2fs.stat(cidStr); | |
console.log("[unixfs.stat]", stat); | |
const ret2 = await node2.pins.add(cidAlt); //NOTE: accept pins.add(cid) after stat(cid) successed | |
console.log("[pins.add]", ret2); | |
// get CID object from CID string with ls() from @helia/unixfs | |
for await (const entry of node2fs.ls(cidStr)) { | |
console.log("[unixfs.ls]", entry.cid); | |
} | |
// retrieve data with cat(cid or cid-string) | |
const u8as = []; | |
for await (const u8a of node2fs.cat(cidStr)) { | |
console.log("[unixfs.cat]", u8a); | |
u8as.push(u8a.slice()); //NOTE: create non shared Uint8Array instance with u8a.slice() | |
//IMPORTANT: u8a returned maybe shared in helia impl, so it would crush when u8a.buffer is detached by some action | |
// - ReadableStream controller.enqueue(u8a) | |
// - postMessage(msg, [u8a.buffer]) | |
} | |
console.log("[Blob.text]", await (new Blob(u8as).text())); | |
//[5. stop] | |
// stop only helia nodes; unixfs is just a wrapper | |
console.log("[helia.stop]", await Promise.all([node1.stop(), node2.stop()])); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
demo: https://gist.githack.com/bellbind/981fe90b937dad423c076efe9fa8883f/raw/multi-nodes-cdn.html
This example do:
node.libp2p.getMultiaddrs().length > 0
node.libp2p.dial(ma)
Blob
unixfs.addByteStream(rs)
thennode.pins.add(cid)
unixfs.stat(cid)
thennode.pins.add(cid)
Blob
on node2 withunixfs.cat(cid)