├── .github ├── dependabot.yml └── workflows │ ├── ci.yml │ ├── generated-pr.yml │ └── stale.yml ├── .gitignore ├── README.md ├── examples ├── helia-101 │ ├── .github │ │ ├── pull_request_template.md │ │ └── workflows │ │ │ └── sync.yml │ ├── .gitignore │ ├── 101-basics.js │ ├── 102-unixfs-dirs.js │ ├── 103-glob-unixfs.js │ ├── 201-storage.js │ ├── 301-networking.js │ ├── 302-mdns.js │ ├── 303-metrics.js │ ├── 401-pinning.js │ ├── 402-providing.js │ ├── 403-block-brokers-routers.js │ ├── 501-ipns.js │ ├── README.md │ ├── package.json │ └── test │ │ └── index.spec.js ├── helia-browser-verified-fetch │ ├── .github │ │ ├── pull_request_template.md │ │ └── workflows │ │ │ └── sync.yml │ ├── .gitignore │ ├── README.md │ ├── index.html │ ├── package.json │ ├── src │ │ ├── App.tsx │ │ ├── Output.tsx │ │ ├── constants.ts │ │ └── main.tsx │ ├── test │ │ ├── blockstore.js │ │ ├── fixtures.js │ │ ├── index.spec.js │ │ └── vite.config.js │ ├── tsconfig.json │ └── vite.config.js ├── helia-cjs │ ├── .github │ │ ├── pull_request_template.md │ │ └── workflows │ │ │ └── sync.yml │ ├── .gitignore │ ├── README.md │ ├── index.js │ ├── package.json │ └── test │ │ └── index.spec.mjs ├── helia-create-car │ ├── .github │ │ ├── pull_request_template.md │ │ └── workflows │ │ │ └── sync.yml │ ├── .gitignore │ ├── README.md │ ├── index.html │ ├── package.json │ ├── public │ │ ├── helia-create-car-demo.gif │ │ └── vite.svg │ ├── src │ │ ├── App.css │ │ ├── App.jsx │ │ ├── components │ │ │ ├── CarCreator.jsx │ │ │ └── FileUploader.jsx │ │ ├── hooks │ │ │ ├── useFiles.js │ │ │ └── useHelia.jsx │ │ ├── index.css │ │ ├── main.jsx │ │ └── provider │ │ │ ├── FileProvider.jsx │ │ │ └── HeliaProvider.jsx │ ├── test │ │ └── index.spec.js │ └── vite.config.js ├── helia-electron │ ├── .github │ │ ├── pull_request_template.md │ │ └── workflows │ │ │ └── sync.yml │ ├── .gitignore │ ├── README.md │ ├── helia.mjs │ ├── index.html │ ├── main.js │ ├── package.json │ ├── renderer.js │ └── test │ │ └── index.spec.mjs ├── helia-esbuild │ ├── .github │ │ ├── pull_request_template.md │ │ └── workflows │ │ │ └── sync.yml │ ├── README.md │ ├── esbuild.js │ ├── img │ │ ├── 1.png │ │ └── 2.png │ ├── package.json │ ├── src │ │ ├── index.html │ │ ├── index.js │ │ └── style.css │ └── test │ │ └── index.spec.js ├── helia-jest-typescript │ ├── .github │ │ ├── pull_request_template.md │ │ └── workflows │ │ │ └── sync.yml │ ├── README.md │ ├── jest.config.json │ ├── package.json │ ├── src │ │ └── index.ts │ ├── test │ │ └── index.spec.ts │ └── tsconfig.json ├── helia-jest │ ├── .github │ │ ├── pull_request_template.md │ │ └── workflows │ │ │ └── sync.yml │ ├── README.md │ ├── jest.config.json │ ├── package.json │ ├── src │ │ └── index.js │ └── test │ │ └── index.spec.js ├── helia-lan-discovery │ ├── .github │ │ ├── pull_request_template.md │ │ └── workflows │ │ │ └── sync.yml │ ├── README.md │ ├── package.json │ └── src │ │ ├── client.js │ │ ├── server.js │ │ └── utils.js ├── helia-nestjs │ ├── .eslintrc.cjs │ ├── .github │ │ ├── pull_request_template.md │ │ └── workflows │ │ │ └── sync.yml │ ├── .prettierrc │ ├── README.md │ ├── nest-cli.json │ ├── package.json │ ├── src │ │ ├── app.controller.spec.ts │ │ ├── app.controller.ts │ │ ├── app.module.ts │ │ ├── app.service.ts │ │ └── main.ts │ ├── test │ │ ├── app.e2e-spec.ts │ │ └── jest-e2e.json │ ├── tsconfig.build.json │ └── tsconfig.json ├── helia-nextjs │ ├── .github │ │ ├── pull_request_template.md │ │ └── workflows │ │ │ └── sync.yml │ ├── README.md │ ├── components │ │ └── ipfs.js │ ├── next.config.js │ ├── package.json │ ├── pages │ │ ├── _app.js │ │ └── index.js │ ├── public │ │ └── vercel.svg │ ├── styles │ │ ├── Home.module.css │ │ └── globals.css │ └── test │ │ └── index.spec.js ├── helia-parcel │ ├── .github │ │ ├── pull_request_template.md │ │ └── workflows │ │ │ └── sync.yml │ ├── README.md │ ├── img │ │ ├── 1.png │ │ └── 2.png │ ├── package.json │ ├── src │ │ ├── index.html │ │ ├── index.js │ │ └── style.css │ └── test │ │ └── index.spec.js ├── helia-remote-pinning │ ├── .github │ │ ├── pull_request_template.md │ │ └── workflows │ │ │ └── sync.yml │ ├── .gitignore │ ├── README.md │ ├── bootstrapper.js │ ├── package.json │ ├── pinning-service.js │ ├── publisher │ │ ├── index.html │ │ └── index.js │ ├── resolver │ │ ├── index.html │ │ └── index.js │ ├── test │ │ └── index.spec.js │ ├── vite.config-publisher.js │ └── vite.config-resolver.js ├── helia-script-tag │ ├── .github │ │ ├── pull_request_template.md │ │ └── workflows │ │ │ └── sync.yml │ ├── .gitignore │ ├── README.md │ ├── index.html │ ├── package.json │ ├── src │ │ └── index.js │ ├── test │ │ └── index.spec.js │ └── vite.config.js ├── helia-ts-node │ ├── .github │ │ ├── pull_request_template.md │ │ └── workflows │ │ │ └── sync.yml │ ├── README.md │ ├── package.json │ ├── src │ │ └── index.ts │ ├── test │ │ └── index.spec.js │ └── tsconfig.json ├── helia-typescript │ ├── .github │ │ ├── pull_request_template.md │ │ └── workflows │ │ │ └── sync.yml │ ├── .gitignore │ ├── README.md │ ├── package.json │ ├── src │ │ └── index.ts │ ├── test │ │ └── index.spec.js │ └── tsconfig.json ├── helia-vite │ ├── .github │ │ ├── pull_request_template.md │ │ └── workflows │ │ │ └── sync.yml │ ├── .gitignore │ ├── README.md │ ├── index.html │ ├── package.json │ ├── public │ │ ├── hello-helia.gif │ │ └── vite.svg │ ├── src │ │ ├── App.css │ │ ├── App.jsx │ │ ├── hooks │ │ │ ├── useCommitText.jsx │ │ │ └── useHelia.jsx │ │ ├── index.css │ │ ├── main.jsx │ │ └── provider │ │ │ └── HeliaProvider.jsx │ ├── test │ │ └── index.spec.js │ └── vite.config.js ├── helia-vue │ ├── .github │ │ ├── pull_request_template.md │ │ └── workflows │ │ │ └── sync.yml │ ├── .gitignore │ ├── README.md │ ├── index.html │ ├── package.json │ ├── public │ │ └── demo-vue.gif │ ├── src │ │ ├── App.vue │ │ ├── HeliaApi │ │ │ ├── useCommitText.js │ │ │ └── useUnixFS.js │ │ ├── assets │ │ │ ├── base.css │ │ │ ├── logo.svg │ │ │ └── main.css │ │ ├── components │ │ │ ├── TextCommiter.vue │ │ │ └── UnixFSManager.vue │ │ ├── main.js │ │ └── plugins │ │ │ └── HeliaProviderPlugin.js │ ├── test │ │ └── index.spec.js │ └── vite.config.js └── helia-webpack │ ├── .github │ ├── pull_request_template.md │ └── workflows │ │ └── sync.yml │ ├── README.md │ ├── img │ └── 1.png │ ├── package.json │ ├── public │ └── index.html │ ├── src │ ├── app.css │ ├── app.js │ ├── index.js │ └── ipfs-logo.svg │ ├── test │ └── index.spec.js │ └── webpack.config.js └── package.json /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: npm 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | time: "11:00" 8 | open-pull-requests-limit: 50 9 | commit-message: 10 | prefix: "deps" 11 | prefix-development: "deps(dev)" 12 | - package-ecosystem: "github-actions" 13 | directory: "/" 14 | schedule: 15 | interval: "weekly" 16 | commit-message: 17 | prefix: ci 18 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | branches: 9 | - '**' 10 | 11 | concurrency: 12 | group: ${{ github.head_ref || github.ref_name }} 13 | cancel-in-progress: true 14 | 15 | jobs: 16 | 17 | examples: 18 | runs-on: ubuntu-latest 19 | name: Test ${{ matrix.project }} 20 | strategy: 21 | fail-fast: false 22 | matrix: 23 | project: 24 | - helia-101 25 | - helia-browser-verified-fetch 26 | - helia-cjs 27 | - helia-electron 28 | - helia-esbuild 29 | - helia-jest 30 | - helia-jest-typescript 31 | - helia-nestjs 32 | - helia-nextjs 33 | - helia-parcel 34 | - helia-remote-pinning 35 | - helia-script-tag 36 | - helia-ts-node 37 | - helia-typescript 38 | - helia-vite 39 | - helia-vue 40 | - helia-webpack 41 | - helia-create-car 42 | - helia-lan-discovery 43 | defaults: 44 | run: 45 | working-directory: examples/${{ matrix.project }} 46 | steps: 47 | - uses: actions/checkout@v4 48 | - uses: actions/setup-node@v4 49 | with: 50 | node-version: lts/* 51 | - name: Install dependencies 52 | run: npm install 53 | - name: Install Playwright 54 | run: npx -y playwright install --with-deps 55 | # This step has been observed to hang indefinitely when failing to download browsers. 56 | timeout-minutes: 10 57 | - name: Run tests 58 | run: npm run test 59 | # This step has been observed to hang indefinitely when the browsers weren't correctly downloaded. 60 | timeout-minutes: 10 61 | 62 | monorepo: 63 | runs-on: ubuntu-latest 64 | name: Test monorepo 65 | steps: 66 | - uses: actions/checkout@v4 67 | - uses: actions/setup-node@v4 68 | with: 69 | node-version: lts/* 70 | - name: Install dependencies 71 | run: npm install 72 | - name: Install Playwright 73 | run: npx -y playwright install --with-deps 74 | # This step has been observed to hang indefinitely when failing to download browsers. 75 | timeout-minutes: 10 76 | - name: Run linting 77 | run: npm run lint 78 | - name: Run tests 79 | run: npm run test -- --concurrency 1 80 | # This step has been observed to hang indefinitely when the browsers weren't correctly downloaded. 81 | timeout-minutes: 10 82 | 83 | push-changes: 84 | name: Push changes 85 | runs-on: ubuntu-latest 86 | needs: [monorepo, examples] 87 | if: github.event_name == 'push' && github.ref == 'refs/heads/main' 88 | strategy: 89 | fail-fast: true 90 | matrix: 91 | project: 92 | - helia-101 93 | - helia-cjs 94 | - helia-browser-verified-fetch 95 | - helia-electron 96 | - helia-esbuild 97 | - helia-jest 98 | - helia-jest-typescript 99 | - helia-nestjs 100 | - helia-parcel 101 | - helia-remote-pinning 102 | - helia-nextjs 103 | - helia-script-tag 104 | - helia-ts-node 105 | - helia-typescript 106 | - helia-vite 107 | - helia-vue 108 | - helia-webpack 109 | - helia-create-car 110 | - helia-lan-discovery 111 | steps: 112 | - uses: convictional/trigger-workflow-and-wait@f69fa9eedd3c62a599220f4d5745230e237904be 113 | with: 114 | owner: ipfs-examples 115 | repo: ${{ matrix.project }} 116 | github_token: ${{ secrets.REPO_PULL_TOKEN }} 117 | workflow_file_name: sync.yml 118 | -------------------------------------------------------------------------------- /.github/workflows/generated-pr.yml: -------------------------------------------------------------------------------- 1 | name: Close Generated PRs 2 | 3 | on: 4 | schedule: 5 | - cron: '0 0 * * *' 6 | workflow_dispatch: 7 | 8 | permissions: 9 | issues: write 10 | pull-requests: write 11 | 12 | jobs: 13 | stale: 14 | uses: ipdxco/unified-github-workflows/.github/workflows/reusable-generated-pr.yml@v1 15 | -------------------------------------------------------------------------------- /.github/workflows/stale.yml: -------------------------------------------------------------------------------- 1 | name: Close Stale Issues 2 | 3 | on: 4 | schedule: 5 | - cron: '0 0 * * *' 6 | workflow_dispatch: 7 | 8 | permissions: 9 | issues: write 10 | pull-requests: write 11 | 12 | jobs: 13 | stale: 14 | uses: ipdxco/unified-github-workflows/.github/workflows/reusable-stale-issue.yml@v1 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | build 3 | dist 4 | .docs 5 | .coverage 6 | node_modules 7 | package-lock.json 8 | yarn.lock 9 | .DS_Store 10 | .next 11 | .vscode 12 | test-results 13 | playwright-report 14 | .parcel-cache 15 | .envrc 16 | .tool-versions 17 | .env 18 | -------------------------------------------------------------------------------- /examples/helia-101/.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | # ⚠️ IMPORTANT ⚠️ 2 | 3 | # Please do not create a Pull Request for this repository 4 | 5 | The contents of this repository are automatically synced from the parent [Helia Examples Project](https://github.com/ipfs-examples/helia-examples) so any changes made to the standalone repository will be lost after the next sync. 6 | 7 | Please open a PR against [IPFS Examples](https://github.com/ipfs-examples/helia-examples) instead. 8 | 9 | ## Contributing 10 | 11 | Contributions are what make the open source community such an amazing place to be learn, inspire, and create. Any contributions you make are **greatly appreciated**. 12 | 13 | 1. Fork the [Helia Examples Project](https://github.com/ipfs-examples/helia-examples) 14 | 2. Create your Feature Branch (`git checkout -b feature/amazing-example`) 15 | 3. Commit your Changes (`git commit -a -m 'feat: add some amazing example'`) 16 | 4. Push to the Branch (`git push origin feature/amazing-example`) 17 | 5. Open a Pull Request 18 | -------------------------------------------------------------------------------- /examples/helia-101/.github/workflows/sync.yml: -------------------------------------------------------------------------------- 1 | name: pull 2 | 3 | on: 4 | workflow_dispatch 5 | 6 | jobs: 7 | sync: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v2 11 | - name: Pull from another repository 12 | uses: ipfs-examples/actions-pull-directory-from-repo@main 13 | with: 14 | source-repo: ipfs-examples/helia-examples 15 | source-folder-path: examples/${{ github.event.repository.name }} 16 | source-branch: main 17 | target-branch: main 18 | git-username: github-actions 19 | git-email: github-actions@github.com 20 | -------------------------------------------------------------------------------- /examples/helia-101/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | build 3 | dist 4 | blockstore 5 | datastore 6 | .docs 7 | .coverage 8 | node_modules 9 | package-lock.json 10 | yarn.lock 11 | -------------------------------------------------------------------------------- /examples/helia-101/102-unixfs-dirs.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | // @ts-check 3 | 4 | import { createHeliaHTTP } from '@helia/http' 5 | import { unixfs as UnixFS } from '@helia/unixfs' 6 | 7 | const helia = await createHeliaHTTP() 8 | 9 | // UnixFS allows you to encode files and directories such that they are 10 | // addressed by CIDs 11 | const unixfs = UnixFS(helia) 12 | 13 | 14 | const encoder = new TextEncoder() 15 | const contentCid = await unixfs.addBytes( 16 | encoder.encode('UnixFS is a standard for encoding files and directories in IPFS.'), 17 | ) 18 | const contentCid2 = await unixfs.addBytes( 19 | encoder.encode('everything in UnixFS has a CID'), 20 | ) 21 | console.log('Added file:', contentCid.toString()) 22 | 23 | // Create an empty root directory 24 | let rootDirectoryCid = await unixfs.addDirectory() 25 | console.log('rootDirectoryCid (empty):', rootDirectoryCid) 26 | 27 | 28 | // Create a new empty UnixFS directory to which we will add files 29 | let myBooks = await unixfs.addDirectory() 30 | console.log('myBooks (empty):', myBooks) 31 | 32 | // Create a another empty UnixFS directory to which we will add files 33 | let myJournal = await unixfs.addDirectory() 34 | console.log('myJournal (empty):', myJournal) 35 | 36 | // Add the first file to `my-books`. cp will return the cid of the updated 37 | // target directory 38 | myBooks = await unixfs.cp(contentCid, myBooks, 'hello.txt') 39 | 40 | // Add the second file to `my-journal` 41 | myJournal = await unixfs.cp(contentCid2, myJournal, 'hello2.txt') 42 | 43 | // Add the `my-books` and `my-journal` directories to the root directory 44 | rootDirectoryCid = await unixfs.cp(myBooks, rootDirectoryCid, 'my-books') 45 | rootDirectoryCid = await unixfs.cp(myJournal, rootDirectoryCid, 'my-journal') 46 | 47 | 48 | await listDirectoryContents(rootDirectoryCid) 49 | 50 | // Function to list directory contents recursively 51 | async function listDirectoryContents(cid, indent = '') { 52 | for await (const entry of unixfs.ls(cid)) { 53 | console.log(`${indent}${entry.name} (${entry.type})`) 54 | if (entry.type === 'directory') { 55 | await listDirectoryContents(entry.cid, indent + ' ') 56 | } 57 | } 58 | } 59 | 60 | 61 | -------------------------------------------------------------------------------- /examples/helia-101/103-glob-unixfs.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | // @ts-check 3 | 4 | import * as nodefs from 'fs' 5 | import { pipeline } from 'stream/promises' 6 | import { createHeliaHTTP } from '@helia/http' 7 | import { car as CAR } from '@helia/car' 8 | import { unixfs, globSource } from '@helia/unixfs' 9 | 10 | // This is a script showing how to use globSource to merkelize files 11 | // and directories from your local file system into a CAR file. 12 | 13 | // Take the first argument as the path of the folder to add 14 | const args = process.argv.slice(2) 15 | 16 | // Example of how to use arguments 17 | if (args.length === 0) { 18 | console.error('No argument provided') 19 | console.error('Usage: node 103-glob-unixfs.js ') 20 | } 21 | 22 | const path = args[0] 23 | const outputCarFile = args[1] 24 | 25 | // Check if the path exists 26 | if (!nodefs.existsSync(path)) { 27 | console.error('Path does not exist') 28 | process.exit(1) 29 | } 30 | 31 | const helia = await createHeliaHTTP() 32 | 33 | // UnixFS allows you to encode files and directories such that they are 34 | // addressed by CIDs and can be retrieved by other nodes on the network 35 | const fs = unixfs(helia) 36 | 37 | // Glob source will recursively add all files and directories in the path 38 | const source = globSource(path, '**/*', { 39 | hidden: false, // ignore hidden files 40 | }) 41 | 42 | let last 43 | // add all files and directories in the path and wrap it all in a directory 44 | for await (const entry of fs.addAll(source, { wrapWithDirectory: true })) { 45 | console.log(entry.cid, entry.path, entry.unixfs?.fileSize(), entry.unixfs?.type ?? 'raw') 46 | last = entry.cid 47 | } 48 | 49 | if (!last) { 50 | console.error('No CID found') 51 | process.exit(1) 52 | } 53 | 54 | const car = CAR(helia) 55 | 56 | const out = nodefs.createWriteStream(outputCarFile) 57 | 58 | // stream the car file to the output file 59 | await pipeline(car.stream(last), out) 60 | 61 | console.log(`Wrote car file to ${outputCarFile}`) 62 | -------------------------------------------------------------------------------- /examples/helia-101/201-storage.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | // @ts-check 3 | import { createHeliaHTTP } from '@helia/http' 4 | import { unixfs } from '@helia/unixfs' 5 | import { MemoryBlockstore } from 'blockstore-core' 6 | import { FsBlockstore } from 'blockstore-fs' 7 | 8 | // the blockstore is where we store the blocks that make up files. 9 | // By default, the in-memory blockstore is used. 10 | // Other blockstores are available: 11 | // - https://www.npmjs.com/package/blockstore-fs - a filesystem blockstore (for use in node) 12 | // - https://www.npmjs.com/package/blockstore-idb - an IndexDB blockstore (for use in browsers) 13 | // - https://www.npmjs.com/package/blockstore-level - a LevelDB blockstore (for node or browsers) 14 | 15 | // Create a new Helia node with an in-memory blockstore 16 | const helia1 = await createHeliaHTTP({ 17 | blockstore: new MemoryBlockstore() 18 | }) 19 | 20 | // create a UnixFS filesystem on top of Helia 21 | const fs1 = unixfs(helia1) 22 | 23 | // we will use this TextEncoder to turn strings into Uint8Arrays 24 | const encoder = new TextEncoder() 25 | 26 | const message = 'Hello World 201' 27 | 28 | // add the bytes to your node and receive a unique content identifier 29 | const cid1 = await fs1.addBytes(encoder.encode(message)) 30 | 31 | console.log('Added file contents:', message) 32 | 33 | // Create a new Helia node with a filesystem blockstore 34 | const helia2 = await createHeliaHTTP({ 35 | blockstore: new FsBlockstore('./blockstore') 36 | }) 37 | 38 | const fs2 = unixfs(helia2) 39 | try { 40 | // Check if the CID is in the blockstore, which will be true if we ran this 41 | // script before 42 | // `offline: true` will prevent the node from trying to fetch the block from 43 | // the network 44 | const stats = await fs2.stat(cid1, { offline: true }) 45 | console.log(`Found ${cid1.toString()} in blockstore:`, stats) 46 | } catch (error) { 47 | console.log("CID can't be found in the blockstore. We will add it now.") 48 | // If the CID is not in the blockstore, we will add it now 49 | const cid2 = await fs2.addBytes(encoder.encode(message)) 50 | console.log('Added file:', cid2.toString()) 51 | } 52 | 53 | await helia1.stop() 54 | await helia2.stop() 55 | -------------------------------------------------------------------------------- /examples/helia-101/301-networking.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | // @ts-check 3 | 4 | import { noise } from '@chainsafe/libp2p-noise' 5 | import { yamux } from '@chainsafe/libp2p-yamux' 6 | import { unixfs } from '@helia/unixfs' 7 | import { bootstrap } from '@libp2p/bootstrap' 8 | import { identify } from '@libp2p/identify' 9 | import { tcp } from '@libp2p/tcp' 10 | import { MemoryBlockstore } from 'blockstore-core' 11 | import { MemoryDatastore } from 'datastore-core' 12 | import { createHelia } from 'helia' 13 | import { createLibp2p } from 'libp2p' 14 | 15 | async function createNode () { 16 | // the blockstore is where we store the blocks that make up files 17 | const blockstore = new MemoryBlockstore() 18 | 19 | // application-specific data lives in the datastore 20 | const datastore = new MemoryDatastore() 21 | 22 | // libp2p is the networking layer that underpins Helia 23 | const libp2p = await createLibp2p({ 24 | datastore, 25 | addresses: { 26 | listen: [ 27 | '/ip4/127.0.0.1/tcp/0' 28 | ] 29 | }, 30 | transports: [ 31 | tcp() 32 | ], 33 | connectionEncrypters: [ 34 | noise() 35 | ], 36 | streamMuxers: [ 37 | yamux() 38 | ], 39 | peerDiscovery: [ 40 | bootstrap({ 41 | list: [ 42 | '/dnsaddr/bootstrap.libp2p.io/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN', 43 | '/dnsaddr/bootstrap.libp2p.io/p2p/QmQCU2EcMqAqQPR2i9bChDtGNJchTbq5TbXJJ16u19uLTa', 44 | '/dnsaddr/bootstrap.libp2p.io/p2p/QmbLHAnMoJPWSCR5Zhtx6BHJX9KiKNN6tpvbUcqanj75Nb', 45 | '/dnsaddr/bootstrap.libp2p.io/p2p/QmcZf59bWwK5XFi76CZX8cbJ4BhTzzA3gU1ZjYZcYW3dwt' 46 | ] 47 | }) 48 | ], 49 | services: { 50 | identify: identify() 51 | } 52 | }) 53 | 54 | return await createHelia({ 55 | datastore, 56 | blockstore, 57 | libp2p 58 | }) 59 | } 60 | 61 | // create two helia nodes 62 | const node1 = await createNode() 63 | const node2 = await createNode() 64 | 65 | // connect them together 66 | const multiaddrs = node2.libp2p.getMultiaddrs() 67 | await node1.libp2p.dial(multiaddrs) 68 | 69 | // create a filesystem on top of Helia, in this case it's UnixFS 70 | const fs = unixfs(node1) 71 | 72 | // we will use this TextEncoder to turn strings into Uint8Arrays 73 | const encoder = new TextEncoder() 74 | 75 | // add the bytes to your node and receive a unique content identifier 76 | const cid = await fs.addBytes(encoder.encode('Hello World 301')) 77 | 78 | console.log('Added file:', cid.toString()) 79 | 80 | // create a filesystem on top of the second Helia node 81 | const fs2 = unixfs(node2) 82 | 83 | // this decoder will turn Uint8Arrays into strings 84 | const decoder = new TextDecoder() 85 | let text = '' 86 | 87 | // use the second Helia node to fetch the file from the first Helia node 88 | for await (const chunk of fs2.cat(cid)) { 89 | text += decoder.decode(chunk, { 90 | stream: true 91 | }) 92 | } 93 | 94 | console.log('Fetched file contents:', text) 95 | 96 | await node1.stop() 97 | await node2.stop() 98 | -------------------------------------------------------------------------------- /examples/helia-101/302-mdns.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | // @ts-check 3 | import { noise } from '@chainsafe/libp2p-noise' 4 | import { yamux } from '@chainsafe/libp2p-yamux' 5 | import { unixfs } from '@helia/unixfs' 6 | import { bootstrap } from '@libp2p/bootstrap' 7 | import { identify } from '@libp2p/identify' 8 | import { tcp } from '@libp2p/tcp' 9 | import { MemoryBlockstore } from 'blockstore-core' 10 | import { MemoryDatastore } from 'datastore-core' 11 | import { createHelia } from 'helia' 12 | import { createLibp2p } from 'libp2p' 13 | import { mdns } from '@libp2p/mdns' 14 | 15 | async function createNode () { 16 | // the blockstore is where we store the blocks that make up files 17 | const blockstore = new MemoryBlockstore() 18 | 19 | // application-specific data lives in the datastore 20 | const datastore = new MemoryDatastore() 21 | 22 | // libp2p is the networking layer that underpins Helia 23 | const libp2p = await createLibp2p({ 24 | datastore, 25 | addresses: { 26 | listen: [ 27 | '/ip4/127.0.0.1/tcp/0' 28 | ] 29 | }, 30 | transports: [ 31 | tcp() 32 | ], 33 | connectionEncrypters: [ 34 | noise() 35 | ], 36 | streamMuxers: [ 37 | yamux() 38 | ], 39 | peerDiscovery: [ 40 | mdns() 41 | ], 42 | services: { 43 | identify: identify() 44 | } 45 | }) 46 | 47 | return await createHelia({ 48 | datastore, 49 | blockstore, 50 | libp2p 51 | }) 52 | } 53 | 54 | // create two helia nodes 55 | const node1 = await createNode() 56 | 57 | // listen for peer discovery events triggered by the mdns peer discovery module 58 | node1.libp2p.addEventListener('peer:discovery', async (evt) => { 59 | console.log(evt) 60 | console.log(evt.detail) 61 | console.log( 62 | `Discovered new peer (${evt.detail.id.toString()}) via MDNS. Dialling...`, 63 | evt.detail.multiaddrs.map((ma) => ma.toString()) 64 | ) 65 | try { 66 | await node1.libp2p.dial(evt.detail.multiaddrs) // dial the new peer 67 | console.log(`Successfully dialed peer (${evt.detail.id.toString()})`) 68 | } catch (err) { 69 | console.error(`Failed to dial peer (${evt.detail.id.toString()}):`, err) 70 | } 71 | }) 72 | 73 | // create a second helia node 74 | const node2 = await createNode() 75 | 76 | // create a filesystem on top of Helia, in this case it's UnixFS 77 | const fs = unixfs(node1) 78 | 79 | // we will use this TextEncoder to turn strings into Uint8Arrays 80 | const encoder = new TextEncoder() 81 | 82 | // add the bytes to your node and receive a unique content identifier 83 | const cid = await fs.addBytes(encoder.encode('Hello World 301')) 84 | 85 | console.log('Added file:', cid.toString()) 86 | 87 | // create a filesystem on top of the second Helia node 88 | const fs2 = unixfs(node2) 89 | 90 | // this decoder will turn Uint8Arrays into strings 91 | const decoder = new TextDecoder() 92 | let text = '' 93 | 94 | // use the second Helia node to fetch the file from the first Helia node 95 | for await (const chunk of fs2.cat(cid)) { 96 | text += decoder.decode(chunk, { 97 | stream: true 98 | }) 99 | } 100 | 101 | console.log('Fetched file contents:', text) 102 | 103 | await node1.stop() 104 | await node2.stop() 105 | -------------------------------------------------------------------------------- /examples/helia-101/303-metrics.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | // @ts-check 3 | import { unixfs } from '@helia/unixfs' 4 | import { createServer } from 'node:http' 5 | import { prometheusMetrics } from '@libp2p/prometheus-metrics' 6 | import { createHelia } from 'helia' 7 | import { register } from 'prom-client' 8 | 9 | import { FsBlockstore } from 'blockstore-fs' 10 | import { FsDatastore } from 'datastore-fs' 11 | 12 | const helia = await createHelia({ 13 | blockstore: new FsBlockstore('./blockstore'), 14 | datastore: new FsDatastore('./datastore'), 15 | libp2p: { 16 | metrics: prometheusMetrics(), 17 | } 18 | }) 19 | 20 | 21 | // log when our addresses changes 22 | helia.libp2p.addEventListener('self:peer:update', (evt) => { 23 | console.log( 24 | 'self:peer:update', 25 | evt.detail.peer.addresses.map( 26 | (a) => a.multiaddr.encapsulate('/p2p/' + evt.detail.peer.id.toString())) 27 | ) 28 | }) 29 | 30 | const metricsServer = createServer((req, res) => { 31 | if (req.url === '/metrics' && req.method === 'GET') { 32 | register.metrics() 33 | .then((metrics) => { 34 | res.writeHead(200, { 'Content-Type': 'text/plain' }) 35 | res.end(metrics) 36 | }, (err) => { 37 | console.error('could not read metrics', err) 38 | res.writeHead(500, { 'Content-Type': 'text/plain' }) 39 | res.end('Internal Server Error') 40 | }) 41 | } else { 42 | res.writeHead(404, { 'Content-Type': 'text/plain' }) 43 | res.end('Not Found') 44 | } 45 | }) 46 | metricsServer.listen(9999, '0.0.0.0') 47 | console.info('Metrics server listening', `0.0.0.0:9999`) 48 | 49 | 50 | console.log('Created Helia node with PeerID:', helia.libp2p.peerId.toString()) 51 | 52 | 53 | 54 | const fs = unixfs(helia) 55 | 56 | // we will use this TextEncoder to turn strings into Uint8Arrays 57 | const encoder = new TextEncoder() 58 | 59 | const text = 'Hello World 🗺️🌎🌍🌏 303!' 60 | 61 | // add the bytes to your node and receive a unique content identifier 62 | let cid = await fs.addFile({ 63 | content: encoder.encode(text), 64 | path: './hello-world.txt' 65 | }) 66 | console.log('Added file:', cid.toString()) 67 | 68 | 69 | // Provide the block to the DHT so that other nodes can find and retrieve it 70 | await helia.routing.provide(cid) 71 | 72 | console.log('CID provided to the DHT:', cid.toString()) 73 | -------------------------------------------------------------------------------- /examples/helia-101/401-pinning.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | // @ts-check 3 | import { unixfs } from '@helia/unixfs' 4 | import { createHelia } from 'helia' 5 | 6 | const helia = await createHelia() 7 | 8 | // log when our addresses changes 9 | helia.libp2p.addEventListener('self:peer:update', (evt) => { 10 | console.log( 11 | 'self:peer:update', 12 | evt.detail.peer.addresses.map((a) => a.multiaddr.toString()) 13 | ) 14 | }) 15 | 16 | console.log('Created Helia node with PeerID:', helia.libp2p.peerId.toString()) 17 | 18 | // create a filesystem on top of Helia, in this case it's UnixFS 19 | const fs = unixfs(helia) 20 | 21 | // we will use this TextEncoder to turn strings into Uint8Arrays 22 | const encoder = new TextEncoder() 23 | 24 | const text = 'Hello World 🗺️🌎🌍🌏 401!' 25 | 26 | // add the bytes to your node and receive a unique content identifier 27 | let cid = await fs.addFile({ 28 | content: encoder.encode(text), 29 | path: './hello-world.txt' 30 | }) 31 | console.log('Added file:', cid.toString()) 32 | 33 | // Run garbage collection to remove unpinned blocks 34 | await helia.gc({ 35 | onProgress: (evt) => { 36 | console.info('gc event', evt.type, evt.detail) 37 | } 38 | }) 39 | 40 | // This will fail because the block is not pinned 41 | try { 42 | // offline to avoid fetching the block from the network 43 | const stats = await fs.stat(cid, { offline: true }) 44 | console.log('Stats:', stats) 45 | } catch (err) { 46 | if (err?.name === 'NotFoundError') { 47 | console.log('Block not found, as expected') 48 | } else { 49 | throw err 50 | } 51 | } 52 | 53 | // Add the same bytes again, this time we will pin them 54 | cid = await fs.addFile({ 55 | content: encoder.encode(text), 56 | path: './hello-world.txt' 57 | }) 58 | console.log('Added file again:', cid.toString()) 59 | 60 | // Pin the block and add some metadata 61 | for await (const pinnedCid of helia.pins.add(cid, { 62 | metadata: { 63 | added: new Date().toISOString(), 64 | addedBy: '401-providing example' 65 | } 66 | })) { 67 | console.log('Pinned CID to prevent garbage collection:', pinnedCid.toString()) 68 | } 69 | 70 | const pin = await helia.pins.get(cid) 71 | console.log('Pin:', pin) 72 | 73 | await helia.stop() 74 | -------------------------------------------------------------------------------- /examples/helia-101/402-providing.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | // @ts-check 3 | import { unixfs } from '@helia/unixfs' 4 | import { createHelia } from 'helia' 5 | 6 | const helia = await createHelia() 7 | 8 | // log when our addresses changes 9 | helia.libp2p.addEventListener('self:peer:update', (evt) => { 10 | console.log( 11 | 'self:peer:update', 12 | evt.detail.peer.addresses.map((a) => a.multiaddr.toString()) 13 | ) 14 | }) 15 | 16 | console.log('Created Helia node with PeerID:', helia.libp2p.peerId.toString()) 17 | 18 | // create a filesystem on top of Helia, in this case it's UnixFS 19 | const fs = unixfs(helia) 20 | 21 | // we will use this TextEncoder to turn strings into Uint8Arrays 22 | const encoder = new TextEncoder() 23 | 24 | const text = 'Hello World 🗺️🌎🌍🌏 402!' 25 | 26 | // add the bytes to your node and receive a unique content identifier 27 | let cid = await fs.addFile({ 28 | content: encoder.encode(text), 29 | path: './hello-world.txt' 30 | }) 31 | console.log('Added file:', cid.toString()) 32 | 33 | // Provide the block to the DHT so that other nodes can find and retrieve it 34 | await helia.routing.provide(cid, { 35 | signal: AbortSignal.timeout(120_000) // Set a timeout of 120 seconds 36 | }) 37 | 38 | console.log('CID provided to the DHT:', cid.toString()) 39 | -------------------------------------------------------------------------------- /examples/helia-101/403-block-brokers-routers.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | // @ts-check 3 | import { CID } from 'multiformats/cid' 4 | import { createHelia } from 'helia' 5 | import { bitswap, trustlessGateway } from '@helia/block-brokers' 6 | import { httpGatewayRouting, libp2pRouting, delegatedHTTPRouting } from '@helia/routers' 7 | import { unixfs } from '@helia/unixfs' 8 | 9 | const cid = CID.parse('bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi') // a known CID with multiple providers 10 | 11 | // Block brokers and routers are used to handle block retrieval and routing in Helia 12 | // Routers find providers for blocks, which are then passed to 13 | // the block broker to retrieve the block 14 | // Helia supports two block brokers (both are enabled by default): 15 | // - `bitswap`: uses the bitswap protocol over libp2p connections 16 | // - `trustlessGateway`: uses the trustless gateways over HTTP(S) 17 | // 18 | // Trustless gateways are either recursive public gateways or gateway 19 | // providers (that are found via the delegated routing endpoint) 20 | // See https://docs.ipfs.tech/concepts/ipfs-gateway/#gateway-types 21 | // 22 | // Routers handle content and peer routing, meaning they find peers (or gateways) 23 | // providing blocks 24 | // Helia comes with three routers and will race between them to find a provider 25 | // for a given CID: 26 | // - `libp2pRouting`: uses libp2p (which uses the DHT and HTTP delegated routing) 27 | // to find providers 28 | // - `delegatedHTTPRouting`: uses an HTTP delegated routing endpoint to find 29 | // providers. (not needed if libp2p routing is used which already has a 30 | // delegated routing router) 31 | // - `httpGatewayRouting`: uses statically IPFS gateways as a provider. 32 | // Useful as a fallback when direct providers cannot be found or for 33 | // when you know in advance that a gateway is the provider for CIDs 34 | 35 | 36 | // This node will use the delegated routing endpoint to find providers and 37 | // bitswap over libp2p connections to retrieve blocks 38 | const bitswapDelegatedRoutingNode = await createHelia({ 39 | blockBrokers: [bitswap()], 40 | routers: [delegatedHTTPRouting('https://delegated-ipfs.dev')], 41 | }) 42 | const fsBitswap = unixfs(bitswapDelegatedRoutingNode) 43 | 44 | console.log('Stats:', await fsBitswap.stat(cid)) 45 | 46 | // This node will just try fetching blocks IPFS gateways as a provider and trustless gateways over HTTP(S) to find providers 47 | const gatewayNode = await createHelia({ 48 | blockBrokers: [trustlessGateway()], 49 | routers: [httpGatewayRouting({ gateways: ['https://ipfs.io', 'https://w3s.link'] })], 50 | }) 51 | 52 | const fsGateways = unixfs(gatewayNode) 53 | console.log('Stats:', await fsGateways.stat(cid)) 54 | 55 | await bitswapDelegatedRoutingNode.stop() 56 | await gatewayNode.stop() 57 | -------------------------------------------------------------------------------- /examples/helia-101/501-ipns.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | // @ts-check 3 | 4 | import { createHelia } from 'helia' 5 | import { ipns as IPNS } from '@helia/ipns' 6 | import { dagCbor } from '@helia/dag-cbor' 7 | import { generateKeyPair } from '@libp2p/crypto/keys' 8 | 9 | 10 | 11 | const helia = await createHelia() 12 | const dcbor = dagCbor(helia) 13 | const ipns = IPNS(helia) 14 | 15 | // create a keypair to publish an IPNS name 16 | const privateKey = await generateKeyPair('Ed25519') 17 | 18 | 19 | console.log(`Created IPNS name: 20 | ${privateKey.publicKey.toCID()} (as CID) 21 | ${privateKey.publicKey.toString()} (as base58btc string)`) 22 | 23 | 24 | // Use dag-cbor to encode a 25 | const cid = await dcbor.add({ 26 | message: 'IPNS can be used to publish mutable pointers to immutable data', 27 | repo: 'https://github.com/ipfs-examples/helia-examples' 28 | }) 29 | 30 | try { 31 | const record = await ipns.publish(privateKey, cid, { 32 | lifetime: 1000 * 60 * 60 * 24 * 30, // 30 days 33 | ttl: 1000 * 60, // 1 minute TTL 34 | signal: AbortSignal.timeout(60_000), // 60 seconds timeout for publishing 35 | }) 36 | console.log('Published IPNS record: ', record) 37 | 38 | } catch (error) { 39 | console.error('Error publishing IPNS record:', error) 40 | } 41 | 42 | 43 | await helia.stop() 44 | -------------------------------------------------------------------------------- /examples/helia-101/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "helia-101", 3 | "version": "1.0.0", 4 | "private": true, 5 | "type": "module", 6 | "description": "Getting started with Helia", 7 | "license": "MIT", 8 | "scripts": { 9 | "101-basics": "node 101-basics.js", 10 | "201-storage": "node 201-storage.js", 11 | "301-networking": "node 301-networking.js", 12 | "302-mdns": "node 302-mdns.js", 13 | "303-metrics": "node 303-metrics.js", 14 | "401-pinning": "node 401-pinning.js", 15 | "402-providing": "node 402-providing.js", 16 | "403-block-brokers": "node 403-block-brokers.js", 17 | "501-ipns": "node 501-ipns.js", 18 | "test": "test-node-example test/*" 19 | }, 20 | "dependencies": { 21 | "@chainsafe/libp2p-noise": "^16.1.2", 22 | "@chainsafe/libp2p-yamux": "^7.0.1", 23 | "@helia/car": "^4.0.4", 24 | "@helia/dag-cbor": "^4.0.3", 25 | "@helia/http": "^2.0.5", 26 | "@helia/ipns": "^8.2.0", 27 | "@helia/unixfs": "^5.0.0", 28 | "@libp2p/bootstrap": "^11.0.35", 29 | "@libp2p/crypto": "^5.1.1", 30 | "@libp2p/identify": "^3.0.29", 31 | "@libp2p/prometheus-metrics": "^4.3.18", 32 | "@libp2p/tcp": "^10.1.10", 33 | "blockstore-core": "^5.0.2", 34 | "blockstore-fs": "^2.0.2", 35 | "datastore-core": "^10.0.2", 36 | "datastore-fs": "^10.0.2", 37 | "helia": "^5.3.0", 38 | "libp2p": "^2.8.5", 39 | "prom-client": "^15.1.3" 40 | }, 41 | "devDependencies": { 42 | "test-ipfs-example": "^1.3.3" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /examples/helia-101/test/index.spec.js: -------------------------------------------------------------------------------- 1 | import path from 'path' 2 | import { fileURLToPath } from 'url' 3 | import { waitForOutput } from 'test-ipfs-example/node' 4 | 5 | const __dirname = path.dirname(fileURLToPath(import.meta.url)) 6 | 7 | await waitForOutput('Added file contents: Hello World 101', 'node', [path.resolve(__dirname, '../101-basics.js')]) 8 | 9 | await waitForOutput('myJournal (empty):', 'node', [path.resolve(__dirname, '../102-unixfs-dirs.js')]) 10 | 11 | await waitForOutput('Wrote car file to output.car', 'node', [path.resolve(__dirname, '../103-glob-unixfs.js'), 'test', 'output.car']) 12 | 13 | await waitForOutput('Added file contents: Hello World 201', 'node', [path.resolve(__dirname, '../201-storage.js')]) 14 | 15 | await waitForOutput('Fetched file contents: Hello World 301', 'node', [path.resolve(__dirname, '../301-networking.js')]) 16 | 17 | await waitForOutput('Discovered new peer', 'node', [path.resolve(__dirname, '../302-mdns.js')]) 18 | 19 | await waitForOutput('Metrics server listening', 'node', [path.resolve(__dirname, '../303-metrics.js')]) 20 | 21 | await waitForOutput('Pinned CID', 'node', [path.resolve(__dirname, '../401-pinning.js')]) 22 | 23 | await waitForOutput('CID provided to the DHT:', 'node', [path.resolve(__dirname, '../402-providing.js')]) 24 | 25 | await waitForOutput('Stats:', 'node', [path.resolve(__dirname, '../403-block-brokers-routers.js')]) 26 | 27 | await waitForOutput('Published IPNS record:', 'node', [path.resolve(__dirname, '../501-ipns.js')]) 28 | -------------------------------------------------------------------------------- /examples/helia-browser-verified-fetch/.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | # ⚠️ IMPORTANT ⚠️ 2 | 3 | # Please do not create a Pull Request for this repository 4 | 5 | The contents of this repository are automatically synced from the parent [Helia Examples Project](https://github.com/ipfs-examples/helia-examples) so any changes made to the standalone repository will be lost after the next sync. 6 | 7 | Please open a PR against [IPFS Examples](https://github.com/ipfs-examples/helia-examples) instead. 8 | 9 | ## Contributing 10 | 11 | Contributions are what make the open source community such an amazing place to be learn, inspire, and create. Any contributions you make are **greatly appreciated**. 12 | 13 | 1. Fork the [Helia Examples Project](https://github.com/ipfs-examples/helia-examples) 14 | 2. Create your Feature Branch (`git checkout -b feature/amazing-example`) 15 | 3. Commit your Changes (`git commit -a -m 'feat: add some amazing example'`) 16 | 4. Push to the Branch (`git push origin feature/amazing-example`) 17 | 5. Open a Pull Request 18 | -------------------------------------------------------------------------------- /examples/helia-browser-verified-fetch/.github/workflows/sync.yml: -------------------------------------------------------------------------------- 1 | name: pull 2 | 3 | on: 4 | workflow_dispatch 5 | 6 | jobs: 7 | sync: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v2 11 | - name: Pull from another repository 12 | uses: ipfs-examples/actions-pull-directory-from-repo@main 13 | with: 14 | source-repo: ipfs-examples/helia-examples 15 | source-folder-path: examples/${{ github.event.repository.name }} 16 | source-branch: main 17 | target-branch: main 18 | git-username: github-actions 19 | git-email: github-actions@github.com 20 | -------------------------------------------------------------------------------- /examples/helia-browser-verified-fetch/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /examples/helia-browser-verified-fetch/README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | Helia logo 4 | 5 |

6 | 7 |

Browser Verified Retrieval with @helia/verified-fetch

8 | 9 |

10 | 11 |
12 | Explore the docs 13 | · 14 | View in Dev Environment 15 | · 16 | Report Bug 17 | · 18 | Request Feature/Example 19 |

20 | 21 | 22 | ## Table of Contents 23 | 24 | - [Getting Started](#getting-started) 25 | - [Installation and Running example](#installation-and-running-example) 26 | 27 | ## Getting Started 28 | 29 | ### Installation and Running example 30 | 31 | ```console 32 | npm install 33 | npm run dev 34 | ``` 35 | -------------------------------------------------------------------------------- /examples/helia-browser-verified-fetch/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Verified Retrieval with @helia/verified-fetch 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /examples/helia-browser-verified-fetch/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "helia-browser-verified-fetch", 3 | "private": true, 4 | "version": "0.0.0", 5 | "description": "Browser Verified Retrieval with @helia/verified-fetch", 6 | "type": "module", 7 | "scripts": { 8 | "clean": "rimraf ./dist", 9 | "start": "vite", 10 | "dev": "vite", 11 | "build": "tsc && vite build", 12 | "preview": "vite preview", 13 | "test": "vite build -c ./test/vite.config.js && test-browser-example test" 14 | }, 15 | "dependencies": { 16 | "@helia/verified-fetch": "^2.3.1", 17 | "@sgtpooki/file-type": "^1.0.1", 18 | "react": "^19.0.0", 19 | "react-dom": "^19.0.0" 20 | }, 21 | "devDependencies": { 22 | "@playwright/test": "^1.42.1", 23 | "@types/react": "^19.0.1", 24 | "@types/react-dom": "^19.0.1", 25 | "@vitejs/plugin-react": "^4.2.1", 26 | "test-ipfs-example": "^1.0.0", 27 | "typescript": "^5.4.3", 28 | "vite": "^6.0.9" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /examples/helia-browser-verified-fetch/src/Output.tsx: -------------------------------------------------------------------------------- 1 | interface OutputProps { 2 | output: string | React.JSX.Element 3 | loading?: string 4 | err: string 5 | } 6 | 7 | export function Output ({ output, loading, err }: OutputProps): React.JSX.Element { 8 | if (err.length > 0) { 9 | return ( 10 |
11 |
{err}
12 |
13 | ) 14 | } 15 | 16 | if(loading) { 17 | return 18 | } 19 | 20 | if (typeof output === 'string') { 21 | return ( 22 |
23 | {output.length > 0 && ( 24 |
25 |             {`${output}`}
26 |           
27 | )} 28 |
29 | ) 30 | } 31 | 32 | return output 33 | } 34 | 35 | export function Loading ({ message }: { message: string }): React.JSX.Element { 36 | return ( 37 |
38 |
39 |         
40 |         {message}
41 |       
42 |
43 | ) 44 | } 45 | 46 | const Spinner = (): React.JSX.Element => ( 47 | 63 | ) 64 | -------------------------------------------------------------------------------- /examples/helia-browser-verified-fetch/src/constants.ts: -------------------------------------------------------------------------------- 1 | export const helpText = `Example Paths 2 | ========= 3 | IPNS name 👇 4 | ipns://k51qzi5uqu5dhp48cti0590jyvwgxssrii0zdf19pyfsxwoqomqvfg6bg8qj3s 5 | 6 | DNSLink 👇 7 | ipns://tokens.uniswap.org 8 | 9 | JSON 👇 10 | ipfs://bagaaieracglt4ey6qsxtvzqsgwnsw3b6p2tb7nmx5wdgxur2zia7q6nnzh7q 11 | 12 | DAG-CBOR 👇 13 | ipfs://bafyreicnokmhmrnlp2wjhyk2haep4tqxiptwfrp2rrs7rzq7uk766chqvq 14 | 15 | UnixFS JSON 👇 16 | ipfs://bafybeia5ci747h54m2ybc4rf6yqdtm6nzdisxv57pk66fgubjsnnja6wq4 17 | 18 | dag-json 👇 19 | ipfs://baguqeerasords4njcts6vs7qvdjfcvgnume4hqohf65zsfguprqphs3icwea 20 | 21 | UnixFS image 👇 22 | ipfs://bafybeicklkqcnlvtiscr2hzkubjwnwjinvskffn4xorqeduft3wq7vm5u4 23 | 24 | UnixFS video 👇 25 | ipfs://bafybeicq6y27fphdisjtxaybzxold7dczhvxiiyn3bvkyht7b36lveerrm 26 | ` 27 | -------------------------------------------------------------------------------- /examples/helia-browser-verified-fetch/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom/client' 3 | import App from './App.tsx' 4 | 5 | ReactDOM.createRoot(document.getElementById('root')!).render( 6 | 7 | 8 | , 9 | ) 10 | -------------------------------------------------------------------------------- /examples/helia-browser-verified-fetch/test/blockstore.js: -------------------------------------------------------------------------------- 1 | import { MemoryBlockstore as OriginalMemoryBlockstore } from 'blockstore-core' 2 | import fixtures from './fixtures.js' 3 | 4 | /** 5 | * Custom memory blockstore module which we pre-load with fixture blocks for testing 6 | */ 7 | export class MemoryBlockstore extends OriginalMemoryBlockstore { 8 | constructor () { 9 | super() 10 | 11 | // prefill blockstore with test fixtures 12 | Object.values(fixtures).forEach((fixture) => { 13 | this.put(fixture.cid, fixture.data) 14 | }) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /examples/helia-browser-verified-fetch/test/fixtures.js: -------------------------------------------------------------------------------- 1 | import { CID } from 'multiformats/cid' 2 | 3 | export default { 4 | json: { 5 | cid: CID.parse('bagaaierasords4njcts6vs7qvdjfcvgnume4hqohf65zsfguprqphs3icwea'), 6 | data: Uint8Array.from([123, 34, 104, 101, 108, 108, 111, 34, 58, 34, 119, 111, 114, 108, 100, 34, 125]) 7 | }, 8 | dagJson: { 9 | cid: CID.parse('baguqeerasords4njcts6vs7qvdjfcvgnume4hqohf65zsfguprqphs3icwea'), 10 | data: Uint8Array.from([123, 34, 104, 101, 108, 108, 111, 34, 58, 34, 119, 111, 114, 108, 100, 34, 125]) 11 | }, 12 | dagCbor: { 13 | cid: CID.parse('bafyreidykglsfhoixmivffc5uwhcgshx4j465xwqntbmu43nb2dzqwfvae'), 14 | data: Uint8Array.from([161, 101, 104, 101, 108, 108, 111, 101, 119, 111, 114, 108, 100]) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /examples/helia-browser-verified-fetch/test/index.spec.js: -------------------------------------------------------------------------------- 1 | import { setup, expect } from 'test-ipfs-example/browser' 2 | import fixtures from './fixtures.js' 3 | 4 | // Setup 5 | const test = setup() 6 | 7 | test.describe('Use @helia/verified-fetch With react and vite', () => { 8 | // DOM 9 | const ipfsPathInput = '#ipfs-path' 10 | const fetchOutput = '#output' 11 | const fetchAutoBtn = '#button-fetch-auto' 12 | 13 | test.beforeEach(async ({ servers, page }) => { 14 | await page.goto(servers[0].url) 15 | }) 16 | 17 | test('should properly render ui with the ipfs path input and display JSON', async ({ page }) => { 18 | // wait for helia node to be online 19 | const ipfsPath = await page.locator(ipfsPathInput) 20 | await expect(ipfsPath).toHaveClass(/bg-gray-50/) 21 | 22 | await page.fill(ipfsPathInput, `ipfs://${fixtures.json.cid.toString()}`) 23 | await page.click(fetchAutoBtn) 24 | await page.locator(fetchOutput).waitFor('visible') 25 | 26 | const output = await page.locator(fetchOutput) 27 | await expect(output).toContainText( 28 | '{ "hello": "world" }', 29 | { timeout: 2000 } 30 | ) 31 | }) 32 | 33 | test('should properly render ui with the ipfs path input and display DAG-JSON', async ({ page }) => { 34 | // wait for helia node to be online 35 | const ipfsPath = await page.locator(ipfsPathInput) 36 | await expect(ipfsPath).toHaveClass(/bg-gray-50/) 37 | 38 | await page.fill(ipfsPathInput, `ipfs://${fixtures.dagJson.cid.toString()}`) 39 | await page.click(fetchAutoBtn) 40 | await page.locator(fetchOutput).waitFor('visible') 41 | 42 | const output = await page.locator(fetchOutput) 43 | await expect(output).toContainText( 44 | '{ "hello": "world" }', 45 | { timeout: 2000 } 46 | ) 47 | }) 48 | 49 | test('should properly render ui with the ipfs path input and display DAG-CBOR', async ({ page }) => { 50 | // wait for helia node to be online 51 | const ipfsPath = await page.locator(ipfsPathInput) 52 | await expect(ipfsPath).toHaveClass(/bg-gray-50/) 53 | 54 | await page.fill(ipfsPathInput, `ipfs://${fixtures.dagCbor.cid.toString()}`) 55 | await page.click(fetchAutoBtn) 56 | await page.locator(fetchOutput).waitFor('visible') 57 | 58 | const output = await page.locator(fetchOutput) 59 | await expect(output).toContainText( 60 | '{ "hello": "world" }', 61 | { timeout: 2000 } 62 | ) 63 | }) 64 | }) 65 | -------------------------------------------------------------------------------- /examples/helia-browser-verified-fetch/test/vite.config.js: -------------------------------------------------------------------------------- 1 | import { resolve } from 'path' 2 | import defaultConfig from '../vite.config.js' 3 | 4 | // override resolution of `blockstore-core` module so we can pre-fill a memory 5 | // blockstore with test data 6 | defaultConfig.resolve ??= {} 7 | defaultConfig.resolve.alias ??= {} 8 | defaultConfig.resolve.alias['blockstore-core/dist/src/memory.js'] = resolve(process.cwd(), 'test/blockstore.js') 9 | 10 | // https://vitejs.dev/config/ 11 | export default defaultConfig 12 | -------------------------------------------------------------------------------- /examples/helia-browser-verified-fetch/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "useDefineForClassFields": true, 5 | "lib": ["ES2020", "DOM", "DOM.Iterable"], 6 | "module": "ESNext", 7 | "skipLibCheck": true, 8 | 9 | /* Bundler mode */ 10 | "moduleResolution": "bundler", 11 | "allowImportingTsExtensions": true, 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "noEmit": true, 15 | "jsx": "react-jsx", 16 | 17 | /* Linting */ 18 | "strict": true, 19 | "noUnusedLocals": true, 20 | "noUnusedParameters": true, 21 | "noFallthroughCasesInSwitch": true 22 | }, 23 | "include": ["src"] 24 | } 25 | -------------------------------------------------------------------------------- /examples/helia-browser-verified-fetch/vite.config.js: -------------------------------------------------------------------------------- 1 | import react from '@vitejs/plugin-react' 2 | import { defineConfig } from 'vite' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()] 7 | }) 8 | -------------------------------------------------------------------------------- /examples/helia-cjs/.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | # ⚠️ IMPORTANT ⚠️ 2 | 3 | # Please do not create a Pull Request for this repository 4 | 5 | The contents of this repository are automatically synced from the parent [Helia Examples Project](https://github.com/ipfs-examples/helia-examples) so any changes made to the standalone repository will be lost after the next sync. 6 | 7 | Please open a PR against [IPFS Examples](https://github.com/ipfs-examples/helia-examples) instead. 8 | 9 | ## Contributing 10 | 11 | Contributions are what make the open source community such an amazing place to be learn, inspire, and create. Any contributions you make are **greatly appreciated**. 12 | 13 | 1. Fork the [Helia Examples Project](https://github.com/ipfs-examples/helia-examples) 14 | 2. Create your Feature Branch (`git checkout -b feature/amazing-example`) 15 | 3. Commit your Changes (`git commit -a -m 'feat: add some amazing example'`) 16 | 4. Push to the Branch (`git push origin feature/amazing-example`) 17 | 5. Open a Pull Request 18 | -------------------------------------------------------------------------------- /examples/helia-cjs/.github/workflows/sync.yml: -------------------------------------------------------------------------------- 1 | name: pull 2 | 3 | on: 4 | workflow_dispatch 5 | 6 | jobs: 7 | sync: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v2 11 | - name: Pull from another repository 12 | uses: ipfs-examples/actions-pull-directory-from-repo@main 13 | with: 14 | source-repo: ipfs-examples/helia-examples 15 | source-folder-path: examples/${{ github.event.repository.name }} 16 | source-branch: main 17 | target-branch: main 18 | git-username: github-actions 19 | git-email: github-actions@github.com 20 | -------------------------------------------------------------------------------- /examples/helia-cjs/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | build 3 | dist 4 | .docs 5 | .coverage 6 | node_modules 7 | package-lock.json 8 | yarn.lock 9 | -------------------------------------------------------------------------------- /examples/helia-cjs/index.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | 3 | // this file is regular CommonJS 4 | 5 | async function main () { 6 | const { createHelia } = await import('helia') 7 | const { unixfs } = await import('@helia/unixfs') 8 | 9 | // create a Helia node 10 | const helia = await createHelia() 11 | 12 | // print out our node's PeerId 13 | console.log(helia.libp2p.peerId) 14 | 15 | // create a filesystem on top of Helia, in this case it's UnixFS 16 | const fs = unixfs(helia) 17 | 18 | // we will use this TextEncoder to turn strings into Uint8Arrays 19 | const encoder = new TextEncoder() 20 | 21 | // add the bytes to your node and receive a unique content identifier 22 | const cid = await fs.addBytes(encoder.encode('Hello World 101'), helia.blockstore) 23 | 24 | console.log('Added file:', cid.toString()) 25 | 26 | // this decoder will turn Uint8Arrays into strings 27 | const decoder = new TextDecoder() 28 | let text = '' 29 | 30 | for await (const chunk of fs.cat(cid)) { 31 | text += decoder.decode(chunk, { 32 | stream: true 33 | }) 34 | } 35 | 36 | console.log('Added file contents:', text) 37 | } 38 | 39 | main().catch(err => { 40 | console.error(err) 41 | process.exit(1) 42 | }) 43 | -------------------------------------------------------------------------------- /examples/helia-cjs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "helia-cjs", 3 | "version": "1.0.0", 4 | "private": true, 5 | "type": "commonjs", 6 | "description": "Using Helia from CommonJS", 7 | "license": "MIT", 8 | "main": "index.js", 9 | "scripts": { 10 | "start": "node index.js", 11 | "serve": "npm run start", 12 | "test": "test-node-example test/*" 13 | }, 14 | "dependencies": { 15 | "@helia/unixfs": "^4.0.0", 16 | "helia": "^5.0.0" 17 | }, 18 | "devDependencies": { 19 | "test-ipfs-example": "^1.0.0" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /examples/helia-cjs/test/index.spec.mjs: -------------------------------------------------------------------------------- 1 | import { waitForOutput } from 'test-ipfs-example/node' 2 | import path from 'path' 3 | import { fileURLToPath } from 'url' 4 | 5 | const __dirname = path.dirname(fileURLToPath(import.meta.url)) 6 | 7 | await waitForOutput('Added file contents: Hello World 101', 'node', [path.resolve(__dirname, '../index.js')]) 8 | -------------------------------------------------------------------------------- /examples/helia-create-car/.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | # ⚠️ IMPORTANT ⚠️ 2 | 3 | # Please do not create a Pull Request for this repository 4 | 5 | The contents of this repository are automatically synced from the parent [Helia Examples Project](https://github.com/ipfs-examples/helia-examples) so any changes made to the standalone repository will be lost after the next sync. 6 | 7 | Please open a PR against [IPFS Examples](https://github.com/ipfs-examples/helia-examples) instead. 8 | 9 | ## Contributing 10 | 11 | Contributions are what make the open source community such an amazing place to be learn, inspire, and create. Any contributions you make are **greatly appreciated**. 12 | 13 | 1. Fork the [Helia Examples Project](https://github.com/ipfs-examples/helia-examples) 14 | 2. Create your Feature Branch (`git checkout -b feature/amazing-example`) 15 | 3. Commit your Changes (`git commit -a -m 'feat: add some amazing example'`) 16 | 4. Push to the Branch (`git push origin feature/amazing-example`) 17 | 5. Open a Pull Request 18 | -------------------------------------------------------------------------------- /examples/helia-create-car/.github/workflows/sync.yml: -------------------------------------------------------------------------------- 1 | name: pull 2 | 3 | on: 4 | workflow_dispatch 5 | 6 | jobs: 7 | sync: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v2 11 | - name: Pull from another repository 12 | uses: ipfs-examples/actions-pull-directory-from-repo@main 13 | with: 14 | source-repo: ipfs-examples/helia-examples 15 | source-folder-path: examples/${{ github.event.repository.name }} 16 | source-branch: main 17 | target-branch: main 18 | git-username: github-actions 19 | git-email: github-actions@github.com 20 | -------------------------------------------------------------------------------- /examples/helia-create-car/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | /test-results/ 26 | -------------------------------------------------------------------------------- /examples/helia-create-car/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Helia Create Car Example 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /examples/helia-create-car/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "helia-create-car", 3 | "version": "0.0.0", 4 | "description": "Using Helia with vite - upload files and get a car file back", 5 | "private": true, 6 | "type": "module", 7 | "scripts": { 8 | "clean": "rimraf ./dist", 9 | "dev": "vite", 10 | "start": "vite", 11 | "build": "vite build", 12 | "preview": "vite preview", 13 | "test": "npm run build && test-browser-example test" 14 | }, 15 | "dependencies": { 16 | "@helia/car": "^4.0.0", 17 | "@helia/unixfs": "^4.0.0", 18 | "@ipld/car": "^5.1.1", 19 | "helia": "^5.0.0", 20 | "prop-types": "^15.8.1", 21 | "react": "^19.0.0", 22 | "react-dom": "^19.0.0", 23 | "rimraf": "^6.0.1" 24 | }, 25 | "devDependencies": { 26 | "@playwright/test": "^1.31.2", 27 | "@types/react": "^19.0.1", 28 | "@types/react-dom": "^19.0.1", 29 | "@vitejs/plugin-react": "^4.0.0", 30 | "test-ipfs-example": "^1.0.0", 31 | "vite": "^6.0.9" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /examples/helia-create-car/public/helia-create-car-demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ipfs-examples/helia-examples/45a9729403e56fe07af54364a31cade86d447e74/examples/helia-create-car/public/helia-create-car-demo.gif -------------------------------------------------------------------------------- /examples/helia-create-car/public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/helia-create-car/src/App.css: -------------------------------------------------------------------------------- 1 | #root { 2 | max-width: 1280px; 3 | margin: 0 auto; 4 | padding: 2rem; 5 | text-align: center; 6 | } 7 | 8 | .logo { 9 | height: 6em; 10 | padding: 1.5em; 11 | will-change: filter; 12 | transition: filter 300ms; 13 | } 14 | .logo:hover { 15 | filter: drop-shadow(0 0 2em #646cffaa); 16 | } 17 | .logo.react:hover { 18 | filter: drop-shadow(0 0 2em #61dafbaa); 19 | } 20 | 21 | @keyframes logo-spin { 22 | from { 23 | transform: rotate(0deg); 24 | } 25 | to { 26 | transform: rotate(360deg); 27 | } 28 | } 29 | 30 | @media (prefers-reduced-motion: no-preference) { 31 | a:nth-of-type(2) .logo { 32 | animation: logo-spin infinite 20s linear; 33 | } 34 | } 35 | 36 | .card { 37 | padding: 2em; 38 | } 39 | 40 | .read-the-docs { 41 | color: #888; 42 | } 43 | -------------------------------------------------------------------------------- /examples/helia-create-car/src/App.jsx: -------------------------------------------------------------------------------- 1 | import { React } from 'react' 2 | import './App.css' 3 | import CarCreator from '@/components/CarCreator' 4 | import FileUploader from '@/components/FileUploader' 5 | import { useHelia } from '@/hooks/useHelia' 6 | import FileProvider from '@/provider/FileProvider' 7 | 8 | function App () { 9 | const { error, starting } = useHelia() 10 | 11 | let statusColor = 'green' 12 | if (error) { 13 | statusColor = 'red' 14 | } else if (starting) { 15 | statusColor = 'yellow' 16 | } 17 | 18 | return ( 19 |
20 |
Helia Status
28 |
29 | 30 | 31 | 32 | 33 |
34 | ) 35 | } 36 | 37 | export default App 38 | -------------------------------------------------------------------------------- /examples/helia-create-car/src/components/FileUploader.jsx: -------------------------------------------------------------------------------- 1 | import React, { useCallback } from 'react' 2 | import { useFiles } from '@/hooks/useFiles' 3 | 4 | /** 5 | * @returns {React.FunctionComponent} 6 | */ 7 | export default function FileUploader () { 8 | const { files, setFiles } = useFiles() 9 | const handleFileEvent = useCallback((e) => { 10 | const filesToUpload = Array.prototype.slice.call(e.target.files) 11 | 12 | setFiles(filesToUpload) 13 | }, [files]) 14 | 15 | return ( 16 | <> 17 | 22 | 23 |
24 | {files.map((file, idx) => ( 25 |
26 | {file.name} 27 |
28 | ))} 29 |
30 | 31 | ) 32 | } 33 | -------------------------------------------------------------------------------- /examples/helia-create-car/src/hooks/useFiles.js: -------------------------------------------------------------------------------- 1 | import { useContext } from 'react' 2 | import { FileContext } from '../provider/FileProvider' 3 | 4 | export const useFiles = () => { 5 | const { files, setFiles } = useContext(FileContext) 6 | return { files, setFiles } 7 | } 8 | -------------------------------------------------------------------------------- /examples/helia-create-car/src/hooks/useHelia.jsx: -------------------------------------------------------------------------------- 1 | import { useContext } from 'react' 2 | import { HeliaContext } from '@/provider/HeliaProvider' 3 | 4 | export const useHelia = () => { 5 | const { helia, fs, error, starting } = useContext(HeliaContext) 6 | return { helia, fs, error, starting } 7 | } 8 | -------------------------------------------------------------------------------- /examples/helia-create-car/src/index.css: -------------------------------------------------------------------------------- 1 | :root { 2 | font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; 3 | line-height: 1.5; 4 | font-weight: 400; 5 | 6 | color-scheme: light dark; 7 | color: rgba(255, 255, 255, 0.87); 8 | background-color: #242424; 9 | 10 | font-synthesis: none; 11 | text-rendering: optimizeLegibility; 12 | -webkit-font-smoothing: antialiased; 13 | -moz-osx-font-smoothing: grayscale; 14 | -webkit-text-size-adjust: 100%; 15 | } 16 | 17 | a { 18 | font-weight: 500; 19 | color: #646cff; 20 | text-decoration: inherit; 21 | } 22 | a:hover { 23 | color: #535bf2; 24 | } 25 | 26 | body { 27 | margin: 0; 28 | display: flex; 29 | min-width: 320px; 30 | min-height: 100vh; 31 | } 32 | 33 | h1 { 34 | font-size: 3.2em; 35 | line-height: 1.1; 36 | } 37 | 38 | button { 39 | border-radius: 8px; 40 | border: 1px solid transparent; 41 | padding: 0.6em 1.2em; 42 | font-size: 1em; 43 | font-weight: 500; 44 | font-family: inherit; 45 | background-color: #1a1a1a; 46 | cursor: pointer; 47 | transition: border-color 0.25s; 48 | } 49 | button:hover { 50 | border-color: #646cff; 51 | } 52 | button:focus, 53 | button:focus-visible { 54 | outline: 4px auto -webkit-focus-ring-color; 55 | } 56 | 57 | @media (prefers-color-scheme: light) { 58 | :root { 59 | color: #213547; 60 | background-color: #ffffff; 61 | } 62 | a:hover { 63 | color: #747bff; 64 | } 65 | button { 66 | background-color: #f9f9f9; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /examples/helia-create-car/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom/client' 3 | import App from './App' 4 | import './index.css' 5 | import { HeliaProvider } from '@/provider/HeliaProvider' 6 | 7 | ReactDOM.createRoot(document.getElementById('root')).render( 8 | 9 | 10 | 11 | 12 | 13 | ) 14 | -------------------------------------------------------------------------------- /examples/helia-create-car/src/provider/FileProvider.jsx: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types' 2 | import React, { createContext, useState } from 'react' 3 | 4 | export const FileContext = createContext({ 5 | files: /** @type {File[]} */([]), 6 | setFiles: /** @type {(files: File[]) => void} */() => {} 7 | }) 8 | 9 | /** 10 | * @param {object} param0 11 | * @param {React.ReactNode} param0.children 12 | * @returns 13 | */ 14 | export default function FileProvider ({ children }) { 15 | const [files, setFiles] = useState(/** @type {File[]} */([])) 16 | const providerValue = { 17 | files, 18 | setFiles 19 | } 20 | return ( 21 | 22 | {children} 23 | 24 | ) 25 | } 26 | 27 | FileProvider.propTypes = { 28 | children: PropTypes.node.isRequired 29 | } 30 | -------------------------------------------------------------------------------- /examples/helia-create-car/src/provider/HeliaProvider.jsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | 3 | import { unixfs } from '@helia/unixfs' 4 | import { createHelia } from 'helia' 5 | import PropTypes from 'prop-types' 6 | import { 7 | React, 8 | useEffect, 9 | useState, 10 | useCallback, 11 | createContext 12 | } from 'react' 13 | 14 | export const HeliaContext = createContext({ 15 | helia: null, 16 | fs: null, 17 | error: false, 18 | starting: true 19 | }) 20 | 21 | export const HeliaProvider = ({ children }) => { 22 | const [helia, setHelia] = useState(null) 23 | const [fs, setFs] = useState(null) 24 | const [starting, setStarting] = useState(true) 25 | const [error, setError] = useState(null) 26 | 27 | const startHelia = useCallback(async () => { 28 | if (helia) { 29 | console.info('helia already started') 30 | } else if (window.helia) { 31 | console.info('found a windowed instance of helia, populating ...') 32 | setHelia(window.helia) 33 | setFs(unixfs(helia)) 34 | setStarting(false) 35 | } else { 36 | try { 37 | console.info('Starting Helia') 38 | const helia = await createHelia() 39 | setHelia(helia) 40 | setFs(unixfs(helia)) 41 | setStarting(false) 42 | } catch (e) { 43 | console.error(e) 44 | setError(true) 45 | } 46 | } 47 | }, []) 48 | 49 | useEffect(() => { 50 | startHelia() 51 | }, []) 52 | 53 | return ( 54 | {children} 62 | ) 63 | } 64 | 65 | HeliaProvider.propTypes = { 66 | children: PropTypes.node.isRequired 67 | } 68 | -------------------------------------------------------------------------------- /examples/helia-create-car/test/index.spec.js: -------------------------------------------------------------------------------- 1 | import { createReadStream } from 'node:fs' 2 | import { unixfs } from '@helia/unixfs' 3 | import { CarReader } from '@ipld/car' 4 | import { createHelia } from 'helia' 5 | import { setup, expect } from 'test-ipfs-example/browser' 6 | 7 | // Setup 8 | const test = setup() 9 | 10 | const filesToUpload = [ 11 | './public/helia-create-car-demo.gif', 12 | './public/vite.svg' 13 | ] 14 | 15 | const expectedCarCID = 'bafybeifsknwjwoby7gmnqlzj236rcq47q5pskkum7svsevsod5a74caxry' 16 | 17 | test.describe('Use Helia With react and vite', () => { 18 | // DOM 19 | const heliaStatus = '#heliaStatus' 20 | const fileInput = '#FileUploaderInput' 21 | const downloadCarFileButton = '#downloadCarFile' 22 | const cidOutput = '#carFileCID' 23 | 24 | // async dependent variables 25 | let expectedCIDs 26 | let helia 27 | let heliaFs 28 | 29 | test.beforeEach(async ({ servers, page }) => { 30 | await page.goto(servers[0].url) 31 | 32 | helia = await createHelia({ 33 | start: false 34 | }) 35 | heliaFs = unixfs(helia) 36 | 37 | expectedCIDs = await Promise.all(filesToUpload.map(async (file) => { 38 | const cid = await heliaFs.addByteStream(createReadStream(file)) 39 | return cid.toString() 40 | })) 41 | }) 42 | 43 | test('files can be converted to a valid car file', async ({ page }) => { 44 | // wait for helia node to be online 45 | const status = await page.locator(heliaStatus) 46 | await expect(status).toHaveCSS( 47 | 'border-color', 48 | 'rgb(0, 128, 0)', // green 49 | { timeout: 7000 } 50 | ) 51 | 52 | // select the files to upload 53 | await page.setInputFiles(fileInput, filesToUpload) 54 | 55 | // make sure the output car CID matches the expected CID 56 | await page.waitForSelector(cidOutput) 57 | const cidOutputContent = await page.textContent(cidOutput) 58 | 59 | expect(cidOutputContent).toContain(expectedCarCID) 60 | 61 | // download the car file 62 | const [download] = await Promise.all([ 63 | page.waitForEvent('download'), // wait for download to start 64 | page.click(downloadCarFileButton) 65 | ]) 66 | 67 | // car available for debugging 68 | await download.saveAs('./test-results/helia-create-car-demo.car') 69 | const reader = await CarReader.fromIterable(createReadStream('./test-results/helia-create-car-demo.car')) 70 | 71 | // expect the root of the car file we downloaded and then created from the saved file, to match the 72 | // car file CID listed on the page. 73 | const roots = await reader.getRoots() 74 | expect(roots[0].toString()).toStrictEqual(cidOutputContent) 75 | 76 | // get all the CIDs in the car file 77 | const carFileCids = [] 78 | for await (const cid of reader.cids()) { 79 | carFileCids.push(cid.toString()) 80 | } 81 | expect(carFileCids).toStrictEqual([ 82 | 'bafybeifsknwjwoby7gmnqlzj236rcq47q5pskkum7svsevsod5a74caxry', // root CID 83 | expectedCIDs[0], // CID for filesToUpload[0] 84 | expectedCIDs[1], // CID for filesToUpload[1] 85 | 'bafkreida4xmb4fq4zgkm42xrox4oshesowi35e66kfiqsyv6xerymi3coq', 86 | 'bafkreicz7xeu5jx77kng5tbieuh2bp7ffzjzwn77wpwiy7d3oy6mrbbd4e' 87 | ]) 88 | }) 89 | }) 90 | -------------------------------------------------------------------------------- /examples/helia-create-car/vite.config.js: -------------------------------------------------------------------------------- 1 | import path from 'path' 2 | import react from '@vitejs/plugin-react' 3 | import { defineConfig } from 'vite' 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | resolve: { 8 | alias: [{ find: '@', replacement: path.resolve(__dirname, '/src') }] 9 | } 10 | }) 11 | -------------------------------------------------------------------------------- /examples/helia-electron/.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | # ⚠️ IMPORTANT ⚠️ 2 | 3 | # Please do not create a Pull Request for this repository 4 | 5 | The contents of this repository are automatically synced from the parent [Helia Examples Project](https://github.com/ipfs-examples/helia-examples) so any changes made to the standalone repository will be lost after the next sync. 6 | 7 | Please open a PR against [IPFS Examples](https://github.com/ipfs-examples/helia-examples) instead. 8 | 9 | ## Contributing 10 | 11 | Contributions are what make the open source community such an amazing place to be learn, inspire, and create. Any contributions you make are **greatly appreciated**. 12 | 13 | 1. Fork the [Helia Examples Project](https://github.com/ipfs-examples/helia-examples) 14 | 2. Create your Feature Branch (`git checkout -b feature/amazing-example`) 15 | 3. Commit your Changes (`git commit -a -m 'feat: add some amazing example'`) 16 | 4. Push to the Branch (`git push origin feature/amazing-example`) 17 | 5. Open a Pull Request 18 | -------------------------------------------------------------------------------- /examples/helia-electron/.github/workflows/sync.yml: -------------------------------------------------------------------------------- 1 | name: pull 2 | 3 | on: 4 | workflow_dispatch 5 | 6 | jobs: 7 | sync: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v2 11 | - name: Pull from another repository 12 | uses: ipfs-examples/actions-pull-directory-from-repo@main 13 | with: 14 | source-repo: ipfs-examples/helia-examples 15 | source-folder-path: examples/${{ github.event.repository.name }} 16 | source-branch: main 17 | target-branch: main 18 | git-username: github-actions 19 | git-email: github-actions@github.com 20 | -------------------------------------------------------------------------------- /examples/helia-electron/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | build 3 | dist 4 | .docs 5 | .coverage 6 | node_modules 7 | package-lock.json 8 | yarn.lock 9 | -------------------------------------------------------------------------------- /examples/helia-electron/helia.mjs: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | 3 | import { createHelia } from 'helia' 4 | 5 | export async function createNode () { 6 | return await createHelia() 7 | } 8 | -------------------------------------------------------------------------------- /examples/helia-electron/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Helia Electron 8 | 9 | 10 | 11 | 12 |

Helia in electron!

13 |

now check your console

14 | 15 | 16 | -------------------------------------------------------------------------------- /examples/helia-electron/main.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | 3 | const { app, BrowserWindow } = require('electron') 4 | 5 | let mainWindow 6 | 7 | function createWindow () { 8 | mainWindow = new BrowserWindow({ 9 | width: 800, 10 | height: 600, 11 | webPreferences: { 12 | nodeIntegration: true 13 | } 14 | }) 15 | 16 | // and load the index.html of the app. 17 | mainWindow.loadFile('index.html') 18 | 19 | // Open the DevTools. 20 | mainWindow.webContents.openDevTools() 21 | 22 | // Emitted when the window is closed. 23 | mainWindow.on('closed', () => { 24 | mainWindow = null 25 | }) 26 | } 27 | 28 | app.on('ready', async () => { 29 | createWindow() 30 | 31 | try { 32 | // Helia is an ESM-only module but Electron currently only supports CJS 33 | // at the top level, so we have to use dynamic imports to load it 34 | const { createNode } = await import('./helia.mjs') 35 | const node = await createNode() 36 | const id = node.libp2p.peerId 37 | console.log(id) 38 | } catch (err) { 39 | console.error(err) 40 | } 41 | }) 42 | 43 | // Quit when all windows are closed. 44 | app.on('window-all-closed', () => { 45 | if (process.platform !== 'darwin') { 46 | app.quit() 47 | } 48 | }) 49 | 50 | app.on('activate', () => { 51 | if (mainWindow === null) { 52 | createWindow() 53 | } 54 | }) 55 | 56 | // In this file you can include the rest of your app's specific main process 57 | // code. You can also put them in separate files and require them here. 58 | -------------------------------------------------------------------------------- /examples/helia-electron/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "helia-in-electron", 3 | "version": "1.0.0", 4 | "private": true, 5 | "description": "A minimal Electron application with Helia", 6 | "keywords": [ 7 | "Electron", 8 | "IPFS", 9 | "Example" 10 | ], 11 | "main": "main.js", 12 | "scripts": { 13 | "clean": "echo 'Nothing to clean...'", 14 | "start": "electron .", 15 | "serve": "npm run start", 16 | "test": "xvfb-maybe test-node-example test/*" 17 | }, 18 | "dependencies": { 19 | "helia": "^5.0.0" 20 | }, 21 | "devDependencies": { 22 | "electron": "^35.0.3", 23 | "electron-rebuild": "^3.1.1", 24 | "test-ipfs-example": "^1.0.0", 25 | "xvfb-maybe": "^0.2.1" 26 | }, 27 | "greenkeeper": { 28 | "ignore": [ 29 | "electron" 30 | ] 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /examples/helia-electron/renderer.js: -------------------------------------------------------------------------------- 1 | // This file is required by the index.html file and will 2 | // be executed in the renderer process for that window. 3 | // All of the Node.js APIs are available in this process. 4 | -------------------------------------------------------------------------------- /examples/helia-electron/test/index.spec.mjs: -------------------------------------------------------------------------------- 1 | import { waitForOutput } from 'test-ipfs-example/node' 2 | import path from 'path' 3 | import { fileURLToPath } from 'url' 4 | 5 | const __dirname = path.dirname(fileURLToPath(import.meta.url)) 6 | 7 | await waitForOutput('PeerId', 'electron', [ 8 | // workaround for https://github.com/jprichardson/electron-mocha/issues/195 9 | '--no-sandbox', 10 | path.resolve(`${__dirname}/../main.js`) 11 | ]) 12 | 13 | // in CI sometimes the process fails to exit even after we've seen the output 14 | // we are waiting for - if we've got this far the test has passed so just exit 15 | // cleanly 16 | process.exit(0) 17 | -------------------------------------------------------------------------------- /examples/helia-esbuild/.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | # ⚠️ IMPORTANT ⚠️ 2 | 3 | # Please do not create a Pull Request for this repository 4 | 5 | The contents of this repository are automatically synced from the parent [Helia Examples Project](https://github.com/ipfs-examples/helia-examples) so any changes made to the standalone repository will be lost after the next sync. 6 | 7 | Please open a PR against [IPFS Examples](https://github.com/ipfs-examples/helia-examples) instead. 8 | 9 | ## Contributing 10 | 11 | Contributions are what make the open source community such an amazing place to be learn, inspire, and create. Any contributions you make are **greatly appreciated**. 12 | 13 | 1. Fork the [Helia Examples Project](https://github.com/ipfs-examples/helia-examples) 14 | 2. Create your Feature Branch (`git checkout -b feature/amazing-example`) 15 | 3. Commit your Changes (`git commit -a -m 'feat: add some amazing example'`) 16 | 4. Push to the Branch (`git push origin feature/amazing-example`) 17 | 5. Open a Pull Request 18 | -------------------------------------------------------------------------------- /examples/helia-esbuild/.github/workflows/sync.yml: -------------------------------------------------------------------------------- 1 | name: pull 2 | 3 | on: 4 | workflow_dispatch 5 | 6 | jobs: 7 | sync: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v2 11 | - name: Pull from another repository 12 | uses: ipfs-examples/actions-pull-directory-from-repo@main 13 | with: 14 | source-repo: ipfs-examples/helia-examples 15 | source-folder-path: examples/${{ github.event.repository.name }} 16 | source-branch: main 17 | target-branch: main 18 | git-username: github-actions 19 | git-email: github-actions@github.com 20 | -------------------------------------------------------------------------------- /examples/helia-esbuild/esbuild.js: -------------------------------------------------------------------------------- 1 | import { build } from 'esbuild' 2 | 3 | build({ 4 | entryPoints: ['./src/index.js'], 5 | outfile: './dist/index.js', 6 | sourcemap: 'linked', 7 | minify: true, 8 | bundle: true, 9 | define: { 10 | 'process.env.NODE_DEBUG': 'false', 11 | global: 'globalThis' 12 | } 13 | }) 14 | .catch(() => process.exit(1)) 15 | -------------------------------------------------------------------------------- /examples/helia-esbuild/img/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ipfs-examples/helia-examples/45a9729403e56fe07af54364a31cade86d447e74/examples/helia-esbuild/img/1.png -------------------------------------------------------------------------------- /examples/helia-esbuild/img/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ipfs-examples/helia-examples/45a9729403e56fe07af54364a31cade86d447e74/examples/helia-esbuild/img/2.png -------------------------------------------------------------------------------- /examples/helia-esbuild/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "helia-esbuild", 3 | "version": "1.0.0", 4 | "private": true, 5 | "type": "module", 6 | "description": "Bundle Helia with esbuild", 7 | "keywords": [], 8 | "license": "ISC", 9 | "author": "", 10 | "scripts": { 11 | "clean": "rimraf ./dist", 12 | "build": "mkdir -p dist && cp src/index.html src/style.css dist && node esbuild.js", 13 | "start": "npm run build && esbuild --servedir=dist", 14 | "test": "npm run build && test-browser-example test" 15 | }, 16 | "browserslist": "last 1 Chrome version", 17 | "dependencies": { 18 | "@helia/unixfs": "^4.0.0", 19 | "helia": "^5.0.0" 20 | }, 21 | "devDependencies": { 22 | "@babel/core": "^7.14.8", 23 | "@playwright/test": "^1.12.3", 24 | "esbuild": "^0.25.1", 25 | "playwright": "^1.12.3", 26 | "process": "^0.11.10", 27 | "rimraf": "^6.0.1", 28 | "test-ipfs-example": "^1.0.0" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /examples/helia-esbuild/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Bundle Helia with esbuild 8 | 9 | 14 | 19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | Helia logo 33 | 34 |
35 | 36 |
37 |

Add data to Helia

38 | 39 |
40 | 41 | 49 | 50 | 51 | 59 | 60 | 77 |
78 | 79 |

Output

80 | 81 |
82 |
83 |
84 |
85 |
86 | 87 |

Listening addresses

88 | 89 |
90 |
91 |
92 | 93 |

Peers

94 | 95 |
96 |
97 |
98 | 99 |

Dial Queue

100 | 101 |
102 |
103 |
104 |
105 | 106 | 107 | -------------------------------------------------------------------------------- /examples/helia-esbuild/src/style.css: -------------------------------------------------------------------------------- 1 | ::placeholder { 2 | color: rgb(0 0 0 / 30%); 3 | } 4 | 5 | form { 6 | margin: 1.25rem 0; 7 | } 8 | 9 | .window { 10 | display: flex; 11 | flex-direction: column; 12 | background: #222; 13 | color: #fff; 14 | height: 400px; 15 | } 16 | 17 | .window .header { 18 | flex-basis: 3rem; 19 | background: #c6c6c6; 20 | position: relative; 21 | } 22 | 23 | .window .header:after { 24 | content: ". . ."; 25 | position: absolute; 26 | left: 12px; 27 | right: 0; 28 | top: -3px; 29 | font-family: "Times New Roman", Times, serif; 30 | font-size: 96px; 31 | color: #fff; 32 | line-height: 0; 33 | letter-spacing: -12px; 34 | } 35 | 36 | .terminal { 37 | margin: 20px; 38 | font-family: monospace; 39 | font-size: 16px; 40 | overflow: auto; 41 | flex: 1; 42 | } 43 | 44 | .terminal a, .terminal a:active, .terminal a:visited { 45 | color: #0cb892 46 | } 47 | 48 | .terminal a:hover { 49 | color: #fff 50 | } 51 | 52 | .terminal p { 53 | margin: 0; 54 | margin-bottom: 0.5em; 55 | } 56 | -------------------------------------------------------------------------------- /examples/helia-esbuild/test/index.spec.js: -------------------------------------------------------------------------------- 1 | import { setup, expect } from 'test-ipfs-example/browser' 2 | 3 | // Setup 4 | const test = setup() 5 | 6 | test.describe('bundle ipfs with esbuild:', () => { 7 | // DOM 8 | const nameInput = '#file-name' 9 | const contentInput = '#file-content' 10 | const submitBtn = '#add-submit' 11 | const output = '#output' 12 | 13 | test.beforeEach(async ({ servers, page }) => { 14 | await page.goto(servers[0].url) 15 | }) 16 | 17 | test('should initialize a Helia node and add/get a file', async ({ page }) => { 18 | const outputLocator = page.locator(output) 19 | await expect(outputLocator).toHaveText(/Helia node ready/) 20 | 21 | const fileName = 'test.txt' 22 | const fileContent = 'Hello world!' 23 | 24 | await page.fill(nameInput, fileName) 25 | await page.fill(contentInput, fileContent) 26 | await page.click(submitBtn) 27 | 28 | await page.waitForSelector(`${output}:has-text("/bafkreigaknpexyvxt76zgkitavbwx6ejgfheup5oybpm77f3pxzrvwpfdi")`) 29 | 30 | const outputContent = await page.textContent(output) 31 | expect(outputContent).toContain('https://ipfs.io/ipfs/bafkreigaknpexyvxt76zgkitavbwx6ejgfheup5oybpm77f3pxzrvwpfdi') 32 | expect(outputContent).toContain(fileName) 33 | expect(outputContent).toContain(fileContent) 34 | }) 35 | }) 36 | -------------------------------------------------------------------------------- /examples/helia-jest-typescript/.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | # ⚠️ IMPORTANT ⚠️ 2 | 3 | # Please do not create a Pull Request for this repository 4 | 5 | The contents of this repository are automatically synced from the parent [Helia Examples Project](https://github.com/ipfs-examples/helia-examples) so any changes made to the standalone repository will be lost after the next sync. 6 | 7 | Please open a PR against [IPFS Examples](https://github.com/ipfs-examples/helia-examples) instead. 8 | 9 | ## Contributing 10 | 11 | Contributions are what make the open source community such an amazing place to be learn, inspire, and create. Any contributions you make are **greatly appreciated**. 12 | 13 | 1. Fork the [Helia Examples Project](https://github.com/ipfs-examples/helia-examples) 14 | 2. Create your Feature Branch (`git checkout -b feature/amazing-example`) 15 | 3. Commit your Changes (`git commit -a -m 'feat: add some amazing example'`) 16 | 4. Push to the Branch (`git push origin feature/amazing-example`) 17 | 5. Open a Pull Request 18 | -------------------------------------------------------------------------------- /examples/helia-jest-typescript/.github/workflows/sync.yml: -------------------------------------------------------------------------------- 1 | name: pull 2 | 3 | on: 4 | workflow_dispatch 5 | 6 | jobs: 7 | sync: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v2 11 | - name: Pull from another repository 12 | uses: ipfs-examples/actions-pull-directory-from-repo@main 13 | with: 14 | source-repo: ipfs-examples/helia-examples 15 | source-folder-path: examples/${{ github.event.repository.name }} 16 | source-branch: main 17 | target-branch: main 18 | git-username: github-actions 19 | git-email: github-actions@github.com 20 | -------------------------------------------------------------------------------- /examples/helia-jest-typescript/jest.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "moduleFileExtensions": [ 3 | "js", 4 | "json", 5 | "ts" 6 | ], 7 | "rootDir": "test", 8 | "testRegex": ".*\\.spec\\.ts$", 9 | "transform": { 10 | "^.+\\.(t|j)s$": ["ts-jest", { 11 | "useESM": true 12 | }] 13 | }, 14 | "preset": "ts-jest/presets/default-esm", 15 | "moduleNameMapper": { 16 | "^(\\.{1,2}/.*)\\.[jt]s$": "$1" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /examples/helia-jest-typescript/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "helia-jest-typescript", 3 | "version": "1.0.0", 4 | "private": true, 5 | "type": "module", 6 | "description": "Test Helia with Jest and TypeScript", 7 | "scripts": { 8 | "test": "NODE_OPTIONS=--experimental-vm-modules jest" 9 | }, 10 | "dependencies": { 11 | "helia": "^5.0.0" 12 | }, 13 | "devDependencies": { 14 | "@types/jest": "^29.5.2", 15 | "jest": "^29.6.2", 16 | "ts-jest": "^29.1.1", 17 | "typescript": "^5.1.6" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /examples/helia-jest-typescript/src/index.ts: -------------------------------------------------------------------------------- 1 | import { createHelia } from 'helia' 2 | import type { HeliaLibp2p } from 'helia' 3 | 4 | export async function createHeliaNode (): Promise { 5 | const node = await createHelia() 6 | 7 | return node 8 | } 9 | -------------------------------------------------------------------------------- /examples/helia-jest-typescript/test/index.spec.ts: -------------------------------------------------------------------------------- 1 | /* eslint-env jest */ 2 | 3 | import { createHeliaNode } from '../src/index.js' 4 | import type { HeliaLibp2p } from 'helia' 5 | 6 | describe('Helia', () => { 7 | let helia: HeliaLibp2p 8 | 9 | beforeEach(async () => { 10 | helia = await createHeliaNode() 11 | }) 12 | 13 | afterEach(async () => { 14 | if (helia != null) { 15 | await helia.stop() 16 | } 17 | }) 18 | 19 | describe('libp2p', () => { 20 | it('should have a peer id', async () => { 21 | expect(helia.libp2p.peerId).toBeTruthy() 22 | }) 23 | }) 24 | }) 25 | -------------------------------------------------------------------------------- /examples/helia-jest-typescript/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "ES2022", 4 | "target": "ES2021", 5 | "moduleResolution": "node", 6 | 7 | // only necessary for the example linting check, your project may not need 8 | // these settings 9 | "strictNullChecks": true, 10 | "skipLibCheck": true 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /examples/helia-jest/.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | # ⚠️ IMPORTANT ⚠️ 2 | 3 | # Please do not create a Pull Request for this repository 4 | 5 | The contents of this repository are automatically synced from the parent [Helia Examples Project](https://github.com/ipfs-examples/helia-examples) so any changes made to the standalone repository will be lost after the next sync. 6 | 7 | Please open a PR against [IPFS Examples](https://github.com/ipfs-examples/helia-examples) instead. 8 | 9 | ## Contributing 10 | 11 | Contributions are what make the open source community such an amazing place to be learn, inspire, and create. Any contributions you make are **greatly appreciated**. 12 | 13 | 1. Fork the [Helia Examples Project](https://github.com/ipfs-examples/helia-examples) 14 | 2. Create your Feature Branch (`git checkout -b feature/amazing-example`) 15 | 3. Commit your Changes (`git commit -a -m 'feat: add some amazing example'`) 16 | 4. Push to the Branch (`git push origin feature/amazing-example`) 17 | 5. Open a Pull Request 18 | -------------------------------------------------------------------------------- /examples/helia-jest/.github/workflows/sync.yml: -------------------------------------------------------------------------------- 1 | name: pull 2 | 3 | on: 4 | workflow_dispatch 5 | 6 | jobs: 7 | sync: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v2 11 | - name: Pull from another repository 12 | uses: ipfs-examples/actions-pull-directory-from-repo@main 13 | with: 14 | source-repo: ipfs-examples/helia-examples 15 | source-folder-path: examples/${{ github.event.repository.name }} 16 | source-branch: main 17 | target-branch: main 18 | git-username: github-actions 19 | git-email: github-actions@github.com 20 | -------------------------------------------------------------------------------- /examples/helia-jest/jest.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "moduleFileExtensions": [ 3 | "js", 4 | "json" 5 | ], 6 | "rootDir": "test", 7 | "testRegex": ".*\\.spec\\.js$" 8 | } 9 | -------------------------------------------------------------------------------- /examples/helia-jest/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "helia-jest", 3 | "version": "1.0.0", 4 | "private": true, 5 | "type": "module", 6 | "description": "Test Helia with Jest", 7 | "scripts": { 8 | "test": "NODE_OPTIONS=--experimental-vm-modules jest" 9 | }, 10 | "dependencies": { 11 | "helia": "^5.0.0" 12 | }, 13 | "devDependencies": { 14 | "jest": "^29.6.2" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /examples/helia-jest/src/index.js: -------------------------------------------------------------------------------- 1 | import { createHelia } from 'helia' 2 | 3 | export async function createHeliaNode () { 4 | const node = await createHelia() 5 | 6 | return node 7 | } 8 | -------------------------------------------------------------------------------- /examples/helia-jest/test/index.spec.js: -------------------------------------------------------------------------------- 1 | /* eslint-env jest */ 2 | 3 | import { createHeliaNode } from '../src/index.js' 4 | 5 | describe('Helia', () => { 6 | let helia 7 | 8 | beforeEach(async () => { 9 | helia = await createHeliaNode() 10 | }) 11 | 12 | afterEach(async () => { 13 | if (helia != null) { 14 | await helia.stop() 15 | } 16 | }) 17 | 18 | describe('libp2p', () => { 19 | it('should have a peer id', async () => { 20 | expect(helia.libp2p.peerId).toBeTruthy() 21 | }) 22 | }) 23 | }) 24 | -------------------------------------------------------------------------------- /examples/helia-lan-discovery/.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | # ⚠️ IMPORTANT ⚠️ 2 | 3 | # Please do not create a Pull Request for this repository 4 | 5 | The contents of this repository are automatically synced from the parent [Helia Examples Project](https://github.com/ipfs-examples/helia-examples) so any changes made to the standalone repository will be lost after the next sync. 6 | 7 | Please open a PR against [IPFS Examples](https://github.com/ipfs-examples/helia-examples) instead. 8 | 9 | ## Contributing 10 | 11 | Contributions are what make the open source community such an amazing place to be learn, inspire, and create. Any contributions you make are **greatly appreciated**. 12 | 13 | 1. Fork the [Helia Examples Project](https://github.com/ipfs-examples/helia-examples) 14 | 2. Create your Feature Branch (`git checkout -b feature/amazing-example`) 15 | 3. Commit your Changes (`git commit -a -m 'feat: add some amazing example'`) 16 | 4. Push to the Branch (`git push origin feature/amazing-example`) 17 | 5. Open a Pull Request 18 | -------------------------------------------------------------------------------- /examples/helia-lan-discovery/.github/workflows/sync.yml: -------------------------------------------------------------------------------- 1 | name: pull 2 | 3 | on: 4 | workflow_dispatch 5 | 6 | jobs: 7 | sync: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v2 11 | - name: Pull from another repository 12 | uses: ipfs-examples/actions-pull-directory-from-repo@main 13 | with: 14 | source-repo: ipfs-examples/helia-examples 15 | source-folder-path: examples/${{ github.event.repository.name }} 16 | source-branch: main 17 | target-branch: main 18 | git-username: github-actions 19 | git-email: github-actions@github.com 20 | -------------------------------------------------------------------------------- /examples/helia-lan-discovery/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "helia-lan-discovery", 3 | "version": "1.0.0", 4 | "private": true, 5 | "type": "module", 6 | "description": "Get two helia nodes to connect on LAN without supplying address", 7 | "scripts": { 8 | "server": "node src/server.js", 9 | "client": "node src/client.js", 10 | "test": "npm run test1 && npm run test2", 11 | "test1": "npm run server & npm run client", 12 | "test2": "npm run client & npm run server" 13 | }, 14 | "dependencies": { 15 | "@helia/dag-cbor": "^4.0.0", 16 | "helia": "^5.0.0", 17 | "it-length-prefixed-stream": "^2.0.1", 18 | "multiformats": "^13.0.1" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /examples/helia-lan-discovery/src/client.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | // client.js 3 | import { dagCbor } from '@helia/dag-cbor' 4 | import { createHelia } from 'helia' 5 | import { lpStream } from 'it-length-prefixed-stream' 6 | import { CID } from 'multiformats/cid' 7 | import { PROTOCOL } from './utils.js' 8 | 9 | const helia = await createHelia() 10 | const heliaDagCbor = dagCbor(helia) 11 | 12 | helia.libp2p.addEventListener('peer:discovery', async (event) => { 13 | const remotePeerId = event.detail.id 14 | 15 | console.log('client discovered server: %s', remotePeerId) 16 | 17 | // dial custom protocol - the expected interaction is: 18 | // 19 | // 1. client opens stream to server 20 | // 2. server sends CID to client 21 | // 3. client responds with ACK message 22 | // 4. both ends close the stream 23 | helia.libp2p.dialProtocol(remotePeerId, PROTOCOL) 24 | .then(async stream => { 25 | // lpStream will prefix every message sent with the length and handle 26 | // reading the correct number of bytes from the remote 27 | const lp = lpStream(stream) 28 | 29 | try { 30 | console.log('client reading CID') 31 | const bytes = await lp.read() 32 | const cid = CID.decode(bytes) 33 | 34 | console.log('client requesting data for CID %s', cid) 35 | const data = await heliaDagCbor.get(cid) 36 | console.log('client got CID data:', data) 37 | 38 | console.log('client sending ACK') 39 | await lp.write(new TextEncoder().encode('ACK')) 40 | 41 | console.log('client close stream') 42 | await lp.unwrap().close() 43 | } catch (err) { 44 | console.error('client error:', err) 45 | lp.unwrap().abort(err) 46 | } finally { 47 | console.log('client finished') 48 | await helia.stop() 49 | process.exit(0) 50 | } 51 | }) 52 | }) 53 | -------------------------------------------------------------------------------- /examples/helia-lan-discovery/src/server.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | // server.js 3 | import { dagCbor } from '@helia/dag-cbor' 4 | import { createHelia } from 'helia' 5 | import { lpStream } from 'it-length-prefixed-stream' 6 | import { PROTOCOL } from './utils.js' 7 | 8 | const helia = await createHelia() 9 | const heliaDagCbor = dagCbor(helia) 10 | 11 | // 12 | const str = `${new Date().toLocaleString()}: My test string that you only know if I send you the CID for it or you could guess but you'd have to be very lucky` 13 | const cid = await heliaDagCbor.add(str) 14 | const cidString = cid.toString() 15 | 16 | console.log('CID: %s', cidString) 17 | 18 | // handle custom protocol - the expected interaction is: 19 | // 20 | // 1. client opens stream to server 21 | // 2. server sends CID to client 22 | // 3. client responds with ACK message 23 | // 4. both ends close the stream 24 | helia.libp2p.handle(PROTOCOL, ({ stream }) => { 25 | // lpStream will prefix every message send with the length and handle 26 | // reading the correct number of bytes from the remote 27 | const lp = lpStream(stream) 28 | 29 | Promise.resolve().then(async () => { 30 | console.log('server sending CID') 31 | await lp.write(cid.bytes) 32 | 33 | console.log('server waiting for client ACK') 34 | const ack = await lp.read() 35 | 36 | console.info('server received', new TextDecoder().decode(ack.subarray())) 37 | 38 | console.log('server close stream') 39 | await lp.unwrap().close() 40 | }) 41 | .catch(err => { 42 | console.error('server error', err) 43 | lp.unwrap().abort(err) 44 | }) 45 | .finally(async () => { 46 | console.log('server finished') 47 | await helia.stop() 48 | process.exit(0) 49 | }) 50 | }) 51 | -------------------------------------------------------------------------------- /examples/helia-lan-discovery/src/utils.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This is a custom protocol identifier 3 | */ 4 | export const PROTOCOL = '/super-fun/1.0.0' 5 | -------------------------------------------------------------------------------- /examples/helia-nestjs/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | parser: '@typescript-eslint/parser', 3 | parserOptions: { 4 | project: 'tsconfig.json', 5 | tsconfigRootDir: __dirname, 6 | sourceType: 'module', 7 | }, 8 | plugins: ['@typescript-eslint/eslint-plugin'], 9 | extends: [ 10 | 'plugin:@typescript-eslint/recommended', 11 | 'plugin:prettier/recommended', 12 | ], 13 | root: true, 14 | env: { 15 | node: true, 16 | jest: true, 17 | }, 18 | ignorePatterns: ['.eslintrc.js'], 19 | rules: { 20 | '@typescript-eslint/interface-name-prefix': 'off', 21 | '@typescript-eslint/explicit-function-return-type': 'off', 22 | '@typescript-eslint/explicit-module-boundary-types': 'off', 23 | '@typescript-eslint/no-explicit-any': 'off', 24 | }, 25 | }; 26 | -------------------------------------------------------------------------------- /examples/helia-nestjs/.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | # ⚠️ IMPORTANT ⚠️ 2 | 3 | # Please do not create a Pull Request for this repository 4 | 5 | The contents of this repository are automatically synced from the parent [Helia Examples Project](https://github.com/ipfs-examples/helia-examples) so any changes made to the standalone repository will be lost after the next sync. 6 | 7 | Please open a PR against [IPFS Examples](https://github.com/ipfs-examples/helia-examples) instead. 8 | 9 | ## Contributing 10 | 11 | Contributions are what make the open source community such an amazing place to be learn, inspire, and create. Any contributions you make are **greatly appreciated**. 12 | 13 | 1. Fork the [Helia Examples Project](https://github.com/ipfs-examples/helia-examples) 14 | 2. Create your Feature Branch (`git checkout -b feature/amazing-example`) 15 | 3. Commit your Changes (`git commit -a -m 'feat: add some amazing example'`) 16 | 4. Push to the Branch (`git push origin feature/amazing-example`) 17 | 5. Open a Pull Request 18 | -------------------------------------------------------------------------------- /examples/helia-nestjs/.github/workflows/sync.yml: -------------------------------------------------------------------------------- 1 | name: pull 2 | 3 | on: 4 | workflow_dispatch 5 | 6 | jobs: 7 | sync: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v2 11 | - name: Pull from another repository 12 | uses: ipfs-examples/actions-pull-directory-from-repo@main 13 | with: 14 | source-repo: ipfs-examples/helia-examples 15 | source-folder-path: examples/${{ github.event.repository.name }} 16 | source-branch: main 17 | target-branch: main 18 | git-username: github-actions 19 | git-email: github-actions@github.com 20 | -------------------------------------------------------------------------------- /examples/helia-nestjs/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "all" 4 | } -------------------------------------------------------------------------------- /examples/helia-nestjs/nest-cli.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/nest-cli", 3 | "collection": "@nestjs/schematics", 4 | "sourceRoot": "src", 5 | "compilerOptions": { 6 | "deleteOutDir": true 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /examples/helia-nestjs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "helia-nestjs", 3 | "version": "0.0.1", 4 | "description": "Use Helia with NestJS", 5 | "license": "UNLICENSED", 6 | "type": "module", 7 | "scripts": { 8 | "build": "nest build", 9 | "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", 10 | "start": "nest start", 11 | "start:dev": "nest start --watch", 12 | "start:debug": "nest start --debug --watch", 13 | "start:prod": "node dist/main", 14 | "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix", 15 | "test": "NODE_OPTIONS=--experimental-vm-modules jest", 16 | "test:watch": "NODE_OPTIONS=--experimental-vm-modules jest --watch", 17 | "test:cov": "NODE_OPTIONS=--experimental-vm-modules jest --coverage", 18 | "test:debug": "NODE_OPTIONS=--experimental-vm-modules node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", 19 | "test:e2e": "NODE_OPTIONS=--experimental-vm-modules jest --config ./test/jest-e2e.json" 20 | }, 21 | "dependencies": { 22 | "@nestjs/common": "^11.0.12", 23 | "@nestjs/core": "^11.0.12", 24 | "@nestjs/platform-express": "^11.0.2", 25 | "helia": "^5.0.0", 26 | "reflect-metadata": "^0.2.1", 27 | "rxjs": "^7.8.1" 28 | }, 29 | "devDependencies": { 30 | "@nestjs/cli": "^11.0.0", 31 | "@nestjs/schematics": "^11.0.0", 32 | "@nestjs/testing": "^11.0.3", 33 | "@types/express": "^5.0.0", 34 | "@types/jest": "^29.5.2", 35 | "@types/node": "^22.7.4", 36 | "@types/supertest": "^6.0.2", 37 | "@typescript-eslint/eslint-plugin": "^7.18.0", 38 | "@typescript-eslint/parser": "^7.18.0", 39 | "eslint": "^8.42.0", 40 | "eslint-config-prettier": "^9.0.0", 41 | "eslint-plugin-prettier": "^5.0.0", 42 | "jest": "^29.5.0", 43 | "prettier": "^3.0.0", 44 | "source-map-support": "^0.5.21", 45 | "supertest": "^7.0.0", 46 | "ts-jest": "^29.1.0", 47 | "ts-loader": "^9.4.3", 48 | "ts-node": "^10.9.1", 49 | "tsconfig-paths": "^4.2.0", 50 | "typescript": "^5.1.3" 51 | }, 52 | "jest": { 53 | "moduleFileExtensions": [ 54 | "js", 55 | "json", 56 | "ts" 57 | ], 58 | "rootDir": "src", 59 | "testRegex": ".*\\.spec\\.ts$", 60 | "transform": { 61 | "^.+\\.(t|j)s$": [ 62 | "ts-jest", 63 | { 64 | "useESM": true 65 | } 66 | ] 67 | }, 68 | "collectCoverageFrom": [ 69 | "**/*.(t|j)s" 70 | ], 71 | "coverageDirectory": "../coverage", 72 | "testEnvironment": "node", 73 | "preset": "ts-jest/presets/default-esm", 74 | "moduleNameMapper": { 75 | "^(\\.{1,2}/.*)\\.[jt]s$": "$1" 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /examples/helia-nestjs/src/app.controller.spec.ts: -------------------------------------------------------------------------------- 1 | import { Test, type TestingModule } from '@nestjs/testing'; 2 | import { AppController } from './app.controller.js'; 3 | import { AppService } from './app.service.js'; 4 | 5 | describe('AppController', () => { 6 | let appController: AppController; 7 | 8 | beforeEach(async () => { 9 | const app: TestingModule = await Test.createTestingModule({ 10 | controllers: [AppController], 11 | providers: [AppService], 12 | }).compile(); 13 | 14 | appController = app.get(AppController); 15 | }); 16 | 17 | describe('root', () => { 18 | it('should return "Helia is running"', async () => { 19 | expect(await appController.getHeliaVersion()).toContain( 20 | 'Helia is running', 21 | ); 22 | 23 | await appController.onApplicationShutdown(); 24 | }); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /examples/helia-nestjs/src/app.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Get } from '@nestjs/common'; 2 | import { AppService } from './app.service.js'; 3 | 4 | @Controller() 5 | export class AppController { 6 | constructor(private readonly appService: AppService) {} 7 | 8 | @Get() 9 | async getHeliaVersion(): Promise { 10 | const helia = await this.appService.getHelia(); 11 | return 'Helia is running, PeerId ' + helia.libp2p.peerId.toString(); 12 | } 13 | 14 | async onApplicationShutdown(): Promise { 15 | await this.appService.onApplicationShutdown(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /examples/helia-nestjs/src/app.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { AppController } from './app.controller.js'; 3 | import { AppService } from './app.service.js'; 4 | 5 | @Module({ 6 | imports: [], 7 | controllers: [AppController], 8 | providers: [AppService], 9 | }) 10 | export class AppModule {} 11 | -------------------------------------------------------------------------------- /examples/helia-nestjs/src/app.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@nestjs/common'; 2 | import type { HeliaLibp2p } from 'helia'; 3 | 4 | @Injectable() 5 | export class AppService { 6 | private helia?: HeliaLibp2p; 7 | 8 | async getHelia(): Promise { 9 | if (this.helia == null) { 10 | const { createHelia } = await import('helia'); 11 | this.helia = await createHelia(); 12 | } 13 | 14 | return this.helia; 15 | } 16 | 17 | async onApplicationShutdown(): Promise { 18 | if (this.helia != null) { 19 | await this.helia.stop(); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /examples/helia-nestjs/src/main.ts: -------------------------------------------------------------------------------- 1 | import { NestFactory } from '@nestjs/core'; 2 | import { AppModule } from './app.module.js'; 3 | 4 | async function bootstrap() { 5 | const app = await NestFactory.create(AppModule); 6 | await app.listen(3000); 7 | } 8 | bootstrap().catch((err) => { 9 | console.error(err); // eslint-disable-line no-console 10 | process.exit(1); 11 | }); 12 | -------------------------------------------------------------------------------- /examples/helia-nestjs/test/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { type INestApplication } from '@nestjs/common'; 2 | import { Test, type TestingModule } from '@nestjs/testing'; 3 | import request from 'supertest'; 4 | import { AppModule } from '../src/app.module.js'; 5 | 6 | describe('AppController (e2e)', () => { 7 | let app: INestApplication; 8 | 9 | beforeEach(async () => { 10 | const moduleFixture: TestingModule = await Test.createTestingModule({ 11 | imports: [AppModule], 12 | }).compile(); 13 | 14 | app = moduleFixture.createNestApplication(); 15 | await app.init(); 16 | }); 17 | 18 | afterEach(async () => { 19 | await app.close(); 20 | }); 21 | 22 | it('/ (GET)', async () => { 23 | return request(app.getHttpServer()) 24 | .get('/') 25 | .expect(200) 26 | .expect(/Helia is running/); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /examples/helia-nestjs/test/jest-e2e.json: -------------------------------------------------------------------------------- 1 | { 2 | "moduleFileExtensions": ["js", "json", "ts"], 3 | "rootDir": ".", 4 | "testEnvironment": "node", 5 | "testRegex": ".e2e-spec.ts$", 6 | "transform": { 7 | "^.+\\.(t|j)s$": ["ts-jest", { 8 | "useESM": true 9 | }] 10 | }, 11 | "preset": "ts-jest/presets/default-esm", 12 | "moduleNameMapper": { 13 | "^(\\.{1,2}/.*)\\.[jt]s$": "$1" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /examples/helia-nestjs/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "exclude": ["node_modules", "test", "dist", "**/*spec.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /examples/helia-nestjs/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "ES2020", 4 | "declaration": true, 5 | "removeComments": true, 6 | "emitDecoratorMetadata": true, 7 | "experimentalDecorators": true, 8 | "allowSyntheticDefaultImports": true, 9 | "target": "ES2020", 10 | "sourceMap": true, 11 | "outDir": "./dist", 12 | "baseUrl": "./", 13 | "incremental": true, 14 | "skipLibCheck": true, 15 | "strictNullChecks": true, 16 | "noImplicitAny": false, 17 | "strictBindCallApply": false, 18 | "forceConsistentCasingInFileNames": false, 19 | "noFallthroughCasesInSwitch": false, 20 | "moduleResolution": "Node" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /examples/helia-nextjs/.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | # ⚠️ IMPORTANT ⚠️ 2 | 3 | # Please do not create a Pull Request for this repository 4 | 5 | The contents of this repository are automatically synced from the parent [Helia Examples Project](https://github.com/ipfs-examples/helia-examples) so any changes made to the standalone repository will be lost after the next sync. 6 | 7 | Please open a PR against [IPFS Examples](https://github.com/ipfs-examples/helia-examples) instead. 8 | 9 | ## Contributing 10 | 11 | Contributions are what make the open source community such an amazing place to be learn, inspire, and create. Any contributions you make are **greatly appreciated**. 12 | 13 | 1. Fork the [Helia Examples Project](https://github.com/ipfs-examples/helia-examples) 14 | 2. Create your Feature Branch (`git checkout -b feature/amazing-example`) 15 | 3. Commit your Changes (`git commit -a -m 'feat: add some amazing example'`) 16 | 4. Push to the Branch (`git push origin feature/amazing-example`) 17 | 5. Open a Pull Request 18 | -------------------------------------------------------------------------------- /examples/helia-nextjs/.github/workflows/sync.yml: -------------------------------------------------------------------------------- 1 | name: pull 2 | 3 | on: 4 | workflow_dispatch 5 | 6 | jobs: 7 | sync: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v2 11 | - name: Pull from another repository 12 | uses: ipfs-examples/actions-pull-directory-from-repo@main 13 | with: 14 | source-repo: ipfs-examples/helia-examples 15 | source-folder-path: examples/${{ github.event.repository.name }} 16 | source-branch: main 17 | target-branch: main 18 | git-username: github-actions 19 | git-email: github-actions@github.com 20 | -------------------------------------------------------------------------------- /examples/helia-nextjs/components/ipfs.js: -------------------------------------------------------------------------------- 1 | import { createHelia } from 'helia' 2 | import { React, useState, useEffect } from 'react' 3 | 4 | const IpfsComponent = () => { 5 | const [id, setId] = useState(null) 6 | const [helia, setHelia] = useState(null) 7 | const [isOnline, setIsOnline] = useState(false) 8 | 9 | useEffect(() => { 10 | const init = async () => { 11 | if (helia) return 12 | 13 | const heliaNode = await createHelia() 14 | 15 | const nodeId = heliaNode.libp2p.peerId.toString() 16 | const nodeIsOnline = heliaNode.libp2p.status === 'started' 17 | 18 | setHelia(heliaNode) 19 | setId(nodeId) 20 | setIsOnline(nodeIsOnline) 21 | } 22 | 23 | init() 24 | }, [helia]) 25 | 26 | if (!helia || !id) { 27 | return

Starting Helia...

28 | } 29 | 30 | return ( 31 |
32 |

ID: {id.toString()}

33 |

Status: {isOnline ? 'Online' : 'Offline'}

34 |
35 | ) 36 | } 37 | 38 | export default IpfsComponent 39 | -------------------------------------------------------------------------------- /examples/helia-nextjs/next.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | reactStrictMode: true, 3 | images: { 4 | loader: 'imgix', 5 | path: 'http://localhost:3000' 6 | }, 7 | output: 'export', 8 | distDir: 'dist' 9 | } 10 | -------------------------------------------------------------------------------- /examples/helia-nextjs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "helia-nextjs", 3 | "version": "1.0.0", 4 | "description": "Using Helia with nextjs", 5 | "private": true, 6 | "type": "module", 7 | "scripts": { 8 | "clean": "rimraf ./dist ./.next", 9 | "dev": "next dev", 10 | "build": "next build", 11 | "serve": "npm run dev", 12 | "start": "next start", 13 | "lint": "next lint", 14 | "test": "npm run build && test-browser-example test" 15 | }, 16 | "dependencies": { 17 | "helia": "^5.0.0", 18 | "next": "^15.1.5", 19 | "react": "^19.0.0", 20 | "react-dom": "^19.0.0" 21 | }, 22 | "devDependencies": { 23 | "@playwright/test": "^1.32.1", 24 | "eslint": "^8.36.0", 25 | "eslint-config-next": "^15.1.5", 26 | "interface-datastore": "^8.2.0", 27 | "playwright": "^1.32.1", 28 | "rimraf": "^6.0.1", 29 | "test-ipfs-example": "^1.0.0" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /examples/helia-nextjs/pages/_app.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types' 2 | import { React } from 'react' 3 | import '../styles/globals.css' 4 | 5 | function MyApp ({ Component, pageProps }) { 6 | return 7 | } 8 | 9 | MyApp.propTypes = { 10 | Component: PropTypes.func, 11 | pageProps: PropTypes.object 12 | } 13 | 14 | export default MyApp 15 | -------------------------------------------------------------------------------- /examples/helia-nextjs/pages/index.js: -------------------------------------------------------------------------------- 1 | import Head from 'next/head' 2 | import Image from 'next/legacy/image' 3 | import { React } from 'react' 4 | import IpfsComponent from '../components/ipfs' 5 | import styles from '../styles/Home.module.css' 6 | 7 | export default function Home () { 8 | return ( 9 |
10 | 11 | Create Next App 12 | 13 | 14 | 15 | 16 |
17 |

18 | Welcome to Next.js! 19 |

20 | 21 |

22 | Get started by editing{' '} 23 | pages/index.js 24 |

25 | 26 | 75 | 76 | 77 |
78 | 79 | 91 |
92 | ) 93 | } 94 | -------------------------------------------------------------------------------- /examples/helia-nextjs/public/vercel.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | -------------------------------------------------------------------------------- /examples/helia-nextjs/styles/Home.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | min-height: 100vh; 3 | padding: 0 0.5rem; 4 | display: flex; 5 | flex-direction: column; 6 | justify-content: center; 7 | align-items: center; 8 | height: 100vh; 9 | } 10 | 11 | .main { 12 | padding: 5rem 0; 13 | flex: 1; 14 | display: flex; 15 | flex-direction: column; 16 | justify-content: center; 17 | align-items: center; 18 | } 19 | 20 | .footer { 21 | width: 100%; 22 | height: 100px; 23 | border-top: 1px solid #eaeaea; 24 | display: flex; 25 | justify-content: center; 26 | align-items: center; 27 | } 28 | 29 | .footer a { 30 | display: flex; 31 | justify-content: center; 32 | align-items: center; 33 | flex-grow: 1; 34 | } 35 | 36 | .title a { 37 | color: #0070f3; 38 | text-decoration: none; 39 | } 40 | 41 | .title a:hover, 42 | .title a:focus, 43 | .title a:active { 44 | text-decoration: underline; 45 | } 46 | 47 | .title { 48 | margin: 0; 49 | line-height: 1.15; 50 | font-size: 4rem; 51 | } 52 | 53 | .title, 54 | .description { 55 | text-align: center; 56 | } 57 | 58 | .description { 59 | line-height: 1.5; 60 | font-size: 1.5rem; 61 | } 62 | 63 | .code { 64 | background: #fafafa; 65 | border-radius: 5px; 66 | padding: 0.75rem; 67 | font-size: 1.1rem; 68 | font-family: Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono, 69 | Bitstream Vera Sans Mono, Courier New, monospace; 70 | } 71 | 72 | .grid { 73 | display: flex; 74 | align-items: center; 75 | justify-content: center; 76 | flex-wrap: wrap; 77 | max-width: 800px; 78 | margin-top: 3rem; 79 | } 80 | 81 | .card { 82 | margin: 1rem; 83 | padding: 1.5rem; 84 | text-align: left; 85 | color: inherit; 86 | text-decoration: none; 87 | border: 1px solid #eaeaea; 88 | border-radius: 10px; 89 | transition: color 0.15s ease, border-color 0.15s ease; 90 | width: 45%; 91 | } 92 | 93 | .card:hover, 94 | .card:focus, 95 | .card:active { 96 | color: #0070f3; 97 | border-color: #0070f3; 98 | } 99 | 100 | .card h2 { 101 | margin: 0 0 1rem 0; 102 | font-size: 1.5rem; 103 | } 104 | 105 | .card p { 106 | margin: 0; 107 | font-size: 1.25rem; 108 | line-height: 1.5; 109 | } 110 | 111 | .logo { 112 | height: 1em; 113 | margin-left: 0.5rem; 114 | } 115 | 116 | @media (max-width: 600px) { 117 | .grid { 118 | width: 100%; 119 | flex-direction: column; 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /examples/helia-nextjs/styles/globals.css: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | padding: 0; 4 | margin: 0; 5 | font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, 6 | Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; 7 | } 8 | 9 | a { 10 | color: inherit; 11 | text-decoration: none; 12 | } 13 | 14 | * { 15 | box-sizing: border-box; 16 | } 17 | -------------------------------------------------------------------------------- /examples/helia-nextjs/test/index.spec.js: -------------------------------------------------------------------------------- 1 | import { setup, expect } from 'test-ipfs-example/browser' 2 | 3 | // Setup 4 | const test = setup() 5 | 6 | test.describe('integrate ipfs with nextjs:', () => { 7 | // DOM 8 | const id = '[data-test=id]' 9 | const status = '[data-test=status]' 10 | 11 | test.beforeEach(async ({ servers, page }) => { 12 | await page.goto(servers[0].url) 13 | }) 14 | 15 | test('should properly initialized a IPFS node and print some properties', async ({ page }) => { 16 | await page.waitForSelector(id) 17 | 18 | expect(await page.isVisible(id)).toBeTruthy() 19 | expect(await page.textContent(status)).toContain('Online') 20 | }) 21 | }) 22 | -------------------------------------------------------------------------------- /examples/helia-parcel/.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | # ⚠️ IMPORTANT ⚠️ 2 | 3 | # Please do not create a Pull Request for this repository 4 | 5 | The contents of this repository are automatically synced from the parent [Helia Examples Project](https://github.com/ipfs-examples/helia-examples) so any changes made to the standalone repository will be lost after the next sync. 6 | 7 | Please open a PR against [IPFS Examples](https://github.com/ipfs-examples/helia-examples) instead. 8 | 9 | ## Contributing 10 | 11 | Contributions are what make the open source community such an amazing place to be learn, inspire, and create. Any contributions you make are **greatly appreciated**. 12 | 13 | 1. Fork the [Helia Examples Project](https://github.com/ipfs-examples/helia-examples) 14 | 2. Create your Feature Branch (`git checkout -b feature/amazing-example`) 15 | 3. Commit your Changes (`git commit -a -m 'feat: add some amazing example'`) 16 | 4. Push to the Branch (`git push origin feature/amazing-example`) 17 | 5. Open a Pull Request 18 | -------------------------------------------------------------------------------- /examples/helia-parcel/.github/workflows/sync.yml: -------------------------------------------------------------------------------- 1 | name: pull 2 | 3 | on: 4 | workflow_dispatch 5 | 6 | jobs: 7 | sync: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v2 11 | - name: Pull from another repository 12 | uses: ipfs-examples/actions-pull-directory-from-repo@main 13 | with: 14 | source-repo: ipfs-examples/helia-examples 15 | source-folder-path: examples/${{ github.event.repository.name }} 16 | source-branch: main 17 | target-branch: main 18 | git-username: github-actions 19 | git-email: github-actions@github.com 20 | -------------------------------------------------------------------------------- /examples/helia-parcel/img/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ipfs-examples/helia-examples/45a9729403e56fe07af54364a31cade86d447e74/examples/helia-parcel/img/1.png -------------------------------------------------------------------------------- /examples/helia-parcel/img/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ipfs-examples/helia-examples/45a9729403e56fe07af54364a31cade86d447e74/examples/helia-parcel/img/2.png -------------------------------------------------------------------------------- /examples/helia-parcel/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "helia-parcel", 3 | "version": "1.0.0", 4 | "private": true, 5 | "type": "module", 6 | "description": "Bundle Helia with parcel", 7 | "keywords": [], 8 | "license": "ISC", 9 | "author": "", 10 | "scripts": { 11 | "clean": "rimraf ./dist ./.cache ./.parcel-cache", 12 | "build": "parcel build src/index.html", 13 | "serve": "parcel serve src/index.html --open -p 8888", 14 | "start": "npm run serve", 15 | "test": "npm run build && playwright test test" 16 | }, 17 | "browserslist": "last 1 Chrome version", 18 | "dependencies": { 19 | "@helia/unixfs": "^4.0.0", 20 | "helia": "^5.0.0" 21 | }, 22 | "devDependencies": { 23 | "@babel/core": "^7.14.8", 24 | "@playwright/test": "^1.12.3", 25 | "parcel": "^2.9.1", 26 | "playwright": "^1.12.3", 27 | "process": "^0.11.10", 28 | "rimraf": "^6.0.1", 29 | "test-ipfs-example": "^1.0.0" 30 | }, 31 | "@parcel/resolver-default": { 32 | "packageExports": true 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /examples/helia-parcel/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Bundle Helia with parcel 8 | 9 | 14 | 19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | Helia logo 33 | 34 |
35 | 36 |
37 |

Add data to Helia

38 | 39 |
40 | 41 | 49 | 50 | 51 | 59 | 60 | 77 |
78 | 79 |

Output

80 | 81 |
82 |
83 |
84 |
85 |
86 | 87 |

Listening addresses

88 | 89 |
90 |
91 |
92 | 93 |

Peers

94 | 95 |
96 |
97 |
98 | 99 |

Dial Queue

100 | 101 |
102 |
103 |
104 |
105 | 106 | 107 | -------------------------------------------------------------------------------- /examples/helia-parcel/src/style.css: -------------------------------------------------------------------------------- 1 | ::placeholder { 2 | color: rgb(0 0 0 / 30%); 3 | } 4 | 5 | form { 6 | margin: 1.25rem 0; 7 | } 8 | 9 | .window { 10 | display: flex; 11 | flex-direction: column; 12 | background: #222; 13 | color: #fff; 14 | height: 400px; 15 | } 16 | 17 | .window .header { 18 | flex-basis: 3rem; 19 | background: #c6c6c6; 20 | position: relative; 21 | } 22 | 23 | .window .header:after { 24 | content: ". . ."; 25 | position: absolute; 26 | left: 12px; 27 | right: 0; 28 | top: -3px; 29 | font-family: "Times New Roman", Times, serif; 30 | font-size: 96px; 31 | color: #fff; 32 | line-height: 0; 33 | letter-spacing: -12px; 34 | } 35 | 36 | .terminal { 37 | margin: 20px; 38 | font-family: monospace; 39 | font-size: 16px; 40 | overflow: auto; 41 | flex: 1; 42 | } 43 | 44 | .terminal a, .terminal a:active, .terminal a:visited { 45 | color: #0cb892 46 | } 47 | 48 | .terminal a:hover { 49 | color: #fff 50 | } 51 | 52 | .terminal p { 53 | margin: 0; 54 | margin-bottom: 0.5em; 55 | } 56 | -------------------------------------------------------------------------------- /examples/helia-parcel/test/index.spec.js: -------------------------------------------------------------------------------- 1 | import { setup, expect } from 'test-ipfs-example/browser' 2 | 3 | // Setup 4 | const test = setup() 5 | 6 | test.describe('bundle ipfs with parcel:', () => { 7 | // DOM 8 | const nameInput = '#file-name' 9 | const contentInput = '#file-content' 10 | const submitBtn = '#add-submit' 11 | const output = '#output' 12 | 13 | test.beforeEach(async ({ servers, page }) => { 14 | await page.goto(servers[0].url) 15 | }) 16 | 17 | /** 18 | * This example is breaks due to `Uncaught Error: Cannot find module 'fs'` when updating helia deps. 19 | * It fails due to `Please configure Helia with a libp2p instance` without upgrading the deps. 20 | * 21 | * @see https://github.com/ipfs-examples/helia-examples/issues/87 22 | */ 23 | test.skip('should initialize a Helia node and add/get a file', async ({ page }) => { 24 | const outputLocator = page.locator(output) 25 | await expect(outputLocator).toHaveText(/Creating Helia node/) 26 | 27 | const fileName = 'test.txt' 28 | const fileContent = 'Hello world!' 29 | 30 | await page.fill(nameInput, fileName) 31 | await page.fill(contentInput, fileContent) 32 | await page.click(submitBtn) 33 | 34 | await page.waitForSelector(`${output}:has-text("/bafkreigaknpexyvxt76zgkitavbwx6ejgfheup5oybpm77f3pxzrvwpfdi")`) 35 | 36 | const outputContent = await page.textContent(output) 37 | expect(outputContent).toContain('https://ipfs.io/ipfs/bafkreigaknpexyvxt76zgkitavbwx6ejgfheup5oybpm77f3pxzrvwpfdi') 38 | expect(outputContent).toContain(fileName) 39 | expect(outputContent).toContain(fileContent) 40 | }) 41 | }) 42 | -------------------------------------------------------------------------------- /examples/helia-remote-pinning/.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | # ⚠️ IMPORTANT ⚠️ 2 | 3 | # Please do not create a Pull Request for this repository 4 | 5 | The contents of this repository are automatically synced from the parent [Helia Examples Project](https://github.com/ipfs-examples/helia-examples) so any changes made to the standalone repository will be lost after the next sync. 6 | 7 | Please open a PR against [IPFS Examples](https://github.com/ipfs-examples/helia-examples) instead. 8 | 9 | ## Contributing 10 | 11 | Contributions are what make the open source community such an amazing place to be learn, inspire, and create. Any contributions you make are **greatly appreciated**. 12 | 13 | 1. Fork the [Helia Examples Project](https://github.com/ipfs-examples/helia-examples) 14 | 2. Create your Feature Branch (`git checkout -b feature/amazing-example`) 15 | 3. Commit your Changes (`git commit -a -m 'feat: add some amazing example'`) 16 | 4. Push to the Branch (`git push origin feature/amazing-example`) 17 | 5. Open a Pull Request 18 | -------------------------------------------------------------------------------- /examples/helia-remote-pinning/.github/workflows/sync.yml: -------------------------------------------------------------------------------- 1 | name: pull 2 | 3 | on: 4 | workflow_dispatch 5 | 6 | jobs: 7 | sync: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v2 11 | - name: Pull from another repository 12 | uses: ipfs-examples/actions-pull-directory-from-repo@main 13 | with: 14 | source-repo: ipfs-examples/helia-examples 15 | source-folder-path: examples/${{ github.event.repository.name }} 16 | source-branch: main 17 | target-branch: main 18 | git-username: github-actions 19 | git-email: github-actions@github.com 20 | -------------------------------------------------------------------------------- /examples/helia-remote-pinning/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | /test-results/ 26 | -------------------------------------------------------------------------------- /examples/helia-remote-pinning/bootstrapper.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | 3 | import { noise } from '@chainsafe/libp2p-noise' 4 | import { yamux } from '@chainsafe/libp2p-yamux' 5 | import { circuitRelayServer } from '@libp2p/circuit-relay-v2' 6 | import { privateKeyFromProtobuf } from '@libp2p/crypto/keys' 7 | import { identify } from '@libp2p/identify' 8 | import { kadDHT } from '@libp2p/kad-dht' 9 | import { tcp } from '@libp2p/tcp' 10 | import { webSockets } from '@libp2p/websockets' 11 | import { createLibp2p } from 'libp2p' 12 | import { fromString } from 'uint8arrays/from-string' 13 | 14 | const boostrapperKey = 'CAESQOr+wN1cDgutS/juD5EjvL+nps5/lsIFrO/AjsNnT+petDqTQaus6teAHNTEY0YP3HZdYd1cPgWtpKHASiC5Ers' 15 | const protobuf = fromString(boostrapperKey, 'base64') 16 | const privateKey = privateKeyFromProtobuf(protobuf) 17 | 18 | const bootstrapper = await createLibp2p({ 19 | privateKey, 20 | addresses: { 21 | listen: [ 22 | '/ip4/0.0.0.0/tcp/64484', 23 | '/ip4/0.0.0.0/tcp/64485/ws' 24 | ] 25 | }, 26 | transports: [ 27 | tcp(), 28 | webSockets() 29 | ], 30 | connectionEncrypters: [ 31 | noise() 32 | ], 33 | streamMuxers: [ 34 | yamux() 35 | ], 36 | services: { 37 | identify: identify(), 38 | kadDHT: kadDHT(), 39 | relay: circuitRelayServer({ 40 | reservations: { 41 | maxReservations: Infinity 42 | } 43 | }) 44 | } 45 | }) 46 | 47 | console.info('bootstrapper listening on') 48 | console.info(bootstrapper.getMultiaddrs().map(ma => ma.toString()).join('\n')) 49 | -------------------------------------------------------------------------------- /examples/helia-remote-pinning/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "helia-remote-pinning", 3 | "version": "0.0.0", 4 | "description": "How to pin content to a remote IPFS pinning service using Helia", 5 | "private": true, 6 | "type": "module", 7 | "scripts": { 8 | "clean": "rimraf ./dist*", 9 | "build": "vite build --config ./vite.config-publisher.js && vite build --config ./vite.config-resolver.js", 10 | "bootstrapper": "node bootstrapper.js", 11 | "pinning-service": "node pinning-service.js", 12 | "publisher": "vite --config ./vite.config-publisher.js", 13 | "resolver": "vite --config ./vite.config-resolver.js", 14 | "test": "npm run build && test-browser-example test" 15 | }, 16 | "dependencies": { 17 | "@chainsafe/libp2p-noise": "^16.0.0", 18 | "@chainsafe/libp2p-yamux": "^7.0.1", 19 | "@helia/pinning-service-api-server": "^1.0.0", 20 | "@helia/remote-pinning": "^2.0.0", 21 | "@libp2p/bootstrap": "^11.0.17", 22 | "@libp2p/circuit-relay-v2": "^3.1.7", 23 | "@libp2p/crypto": "^5.0.9", 24 | "@libp2p/devtools-metrics": "^1.1.6", 25 | "@libp2p/identify": "^3.0.15", 26 | "@libp2p/kad-dht": "^14.2.0", 27 | "@libp2p/tcp": "^10.0.15", 28 | "@libp2p/websockets": "^9.1.2", 29 | "helia": "^5.0.0", 30 | "it-drain": "^3.0.7", 31 | "libp2p": "^2.1.8", 32 | "multiformats": "^13.3.1", 33 | "uint8arrays": "^5.1.0" 34 | }, 35 | "devDependencies": { 36 | "@playwright/test": "^1.37.1", 37 | "@vitejs/plugin-react": "^4.0.4", 38 | "rimraf": "^6.0.1", 39 | "test-ipfs-example": "^1.0.0", 40 | "vite": "^6.0.9" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /examples/helia-remote-pinning/pinning-service.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | 3 | import { createPinningServiceAPIServer } from '@helia/pinning-service-api-server' 4 | import { bootstrap } from '@libp2p/bootstrap' 5 | import { createHelia, libp2pDefaults } from 'helia' 6 | 7 | const libp2p = libp2pDefaults() 8 | libp2p.peerDiscovery = [ 9 | bootstrap({ 10 | list: [ 11 | '/ip4/127.0.0.1/tcp/64484' 12 | ] 13 | }) 14 | ] 15 | 16 | const helia = await createHelia({ 17 | libp2p 18 | }) 19 | 20 | // this access token will be used by the publisher to ensure it is allowed to 21 | // pin new content on the pinning service. in production you should generate 22 | // these yourself and enforce validity, etc. 23 | const accessToken = 'SHHH-VERY-SECRET' 24 | 25 | const server = await createPinningServiceAPIServer(helia, { 26 | validateAccessToken: (token) => { 27 | if (token === accessToken) { 28 | return { 29 | id: accessToken 30 | } 31 | } 32 | 33 | throw new Error('Invalid access token') 34 | }, 35 | listen: { 36 | host: '0.0.0.0', 37 | port: 64486 38 | } 39 | }) 40 | 41 | const address = server.server.address() 42 | 43 | console.info('pinning service listening on') 44 | console.info(`http://127.0.0.1:${address.port}`) 45 | -------------------------------------------------------------------------------- /examples/helia-remote-pinning/publisher/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Remote Pinning 8 | 9 | 10 |
11 |
12 |

Publisher

13 |
14 |
15 |

Status: Finding relay

16 |
17 | 22 |
23 |
24 |
25 |
26 |         
27 |
28 |
29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /examples/helia-remote-pinning/publisher/index.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | 3 | import { heliaWithRemotePins } from '@helia/remote-pinning' 4 | import { bootstrap } from '@libp2p/bootstrap' 5 | import { devToolsMetrics } from '@libp2p/devtools-metrics' 6 | import { createHelia, libp2pDefaults } from 'helia' 7 | import drain from 'it-drain' 8 | import { CID } from 'multiformats/cid' 9 | import * as raw from 'multiformats/codecs/raw' 10 | import { sha256 } from 'multiformats/hashes/sha2' 11 | 12 | const DOM = { 13 | statusOutput: () => document.getElementById('publish-status'), 14 | publishInput: () => document.getElementById('publish-input'), 15 | publishButton: () => document.getElementById('publish-button'), 16 | output: () => document.getElementById('publish-output') 17 | } 18 | 19 | // override default libp2p config to: 20 | // 1. use our local bootstrapper to join the network 21 | // 2. reconfigure the WebSocket transport to allow connecting to insecure local addresses 22 | const libp2p = libp2pDefaults() 23 | libp2p.metrics = devToolsMetrics() 24 | libp2p.peerDiscovery = [ 25 | bootstrap({ 26 | list: [ 27 | '/ip4/127.0.0.1/tcp/64485/ws/p2p/12D3KooWMwuMkgbRWbvmYk2uS7Ekr3ZcWTW5AdDCBDMKAtsVd2jt' 28 | ] 29 | }) 30 | ] 31 | libp2p.connectionGater = { 32 | denyDialMultiaddr: () => false 33 | } 34 | 35 | // create a Helia node that uses our remote pinning service 36 | const helia = heliaWithRemotePins(await createHelia({ 37 | libp2p 38 | }), { 39 | // this is the local pinning service API, for production use, specify the 40 | // self-hosted or third party service/token here 41 | endpointUrl: 'http://127.0.0.1:64486', 42 | accessToken: 'SHHH-VERY-SECRET' 43 | }) 44 | 45 | helia.libp2p.addEventListener('transport:listening', (evt) => { 46 | DOM.statusOutput().innerText = 'Ready' 47 | DOM.publishButton().disabled = false 48 | }) 49 | 50 | // create and pin content 51 | DOM.publishButton().onclick = async (event) => { 52 | event.preventDefault() 53 | 54 | try { 55 | DOM.output().innerText = '' 56 | 57 | const data = new TextEncoder().encode(DOM.publishInput().value ?? '') 58 | const digest = await sha256.digest(data) 59 | const cid = CID.createV1(raw.code, digest) 60 | 61 | DOM.output().innerText += 'Adding data\n' 62 | 63 | // add the pin to the blockstore 64 | await helia.blockstore.put(cid, data) 65 | 66 | DOM.output().innerText += `Pinning ${cid}\n` 67 | 68 | // pin the block using the remote service 69 | await drain(helia.pins.add(cid)) 70 | 71 | DOM.output().innerText += `Pinned ${cid}\n` 72 | } catch (err) { 73 | DOM.output().innerText += `Error ${err}\n` 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /examples/helia-remote-pinning/resolver/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Remote Pinning 8 | 9 | 10 |
11 |
12 |

Resolver

13 |
14 |
15 |
16 | 21 |
22 |
23 |
24 |
25 |         
26 |
27 |
28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /examples/helia-remote-pinning/resolver/index.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | 3 | import { bootstrap } from '@libp2p/bootstrap' 4 | import { devToolsMetrics } from '@libp2p/devtools-metrics' 5 | import { createHelia, libp2pDefaults } from 'helia' 6 | import { CID } from 'multiformats/cid' 7 | 8 | const DOM = { 9 | resolveInput: () => document.getElementById('resolve-input'), 10 | resolveButton: () => document.getElementById('resolve-button'), 11 | output: () => document.getElementById('resolve-output') 12 | } 13 | 14 | // override default libp2p config to: 15 | // 1. use our local bootstrapper to join the network 16 | // 2. reconfigure the WebSocket transport to allow connecting to insecure local addresses 17 | const libp2p = libp2pDefaults() 18 | libp2p.metrics = devToolsMetrics() 19 | libp2p.peerDiscovery = [ 20 | bootstrap({ 21 | list: [ 22 | '/ip4/127.0.0.1/tcp/64485/ws/p2p/12D3KooWMwuMkgbRWbvmYk2uS7Ekr3ZcWTW5AdDCBDMKAtsVd2jt' 23 | ] 24 | }) 25 | ] 26 | libp2p.connectionGater = { 27 | denyDialMultiaddr: () => false 28 | } 29 | 30 | // create a Helia node 31 | const helia = await createHelia({ 32 | libp2p 33 | }) 34 | 35 | // create and pin content 36 | DOM.resolveButton().onclick = async (event) => { 37 | event.preventDefault() 38 | 39 | try { 40 | DOM.output().innerText = '' 41 | 42 | const cid = CID.parse(DOM.resolveInput().value ?? '') 43 | 44 | DOM.output().innerText += `Resolving ${cid}\n` 45 | 46 | // load the pinned block 47 | const data = await helia.blockstore.get(cid, { 48 | signal: AbortSignal.timeout(5000) 49 | }) 50 | 51 | // show the contents 52 | DOM.output().innerText += `Resolved ${new TextDecoder().decode(data)}\n` 53 | } catch (err) { 54 | DOM.output().innerText += `Error ${err.toString()}\n` 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /examples/helia-remote-pinning/vite.config-publisher.js: -------------------------------------------------------------------------------- 1 | export default { 2 | build: { 3 | target: 'es2022', 4 | outDir: './dist', 5 | emptyOutDir: true 6 | }, 7 | optimizeDeps: { 8 | esbuildOptions: { target: 'es2022', supported: { bigint: true } } 9 | }, 10 | server: { 11 | open: true 12 | }, 13 | root: './publisher' 14 | } 15 | -------------------------------------------------------------------------------- /examples/helia-remote-pinning/vite.config-resolver.js: -------------------------------------------------------------------------------- 1 | export default { 2 | build: { 3 | target: 'es2022', 4 | outDir: './dist', 5 | emptyOutDir: true 6 | }, 7 | optimizeDeps: { 8 | esbuildOptions: { target: 'es2022', supported: { bigint: true } } 9 | }, 10 | server: { 11 | open: true 12 | }, 13 | root: './resolver' 14 | } 15 | -------------------------------------------------------------------------------- /examples/helia-script-tag/.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | # ⚠️ IMPORTANT ⚠️ 2 | 3 | # Please do not create a Pull Request for this repository 4 | 5 | The contents of this repository are automatically synced from the parent [Helia Examples Project](https://github.com/ipfs-examples/helia-examples) so any changes made to the standalone repository will be lost after the next sync. 6 | 7 | Please open a PR against [Helia Examples](https://github.com/ipfs-examples/helia-examples) instead. 8 | 9 | ## Contributing 10 | 11 | Contributions are what make the open source community such an amazing place to be learn, inspire, and create. Any contributions you make are **greatly appreciated**. 12 | 13 | 1. Fork the [Helia Examples Project](https://github.com/ipfs-examples/helia-examples) 14 | 2. Create your Feature Branch (`git checkout -b feature/amazing-example`) 15 | 3. Commit your Changes (`git commit -a -m 'feat: add some amazing example'`) 16 | 4. Push to the Branch (`git push origin feature/amazing-example`) 17 | 5. Open a Pull Request 18 | -------------------------------------------------------------------------------- /examples/helia-script-tag/.github/workflows/sync.yml: -------------------------------------------------------------------------------- 1 | name: pull 2 | 3 | on: 4 | workflow_dispatch 5 | 6 | jobs: 7 | sync: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v2 11 | - name: Pull from another repository 12 | uses: ipfs-examples/actions-pull-directory-from-repo@main 13 | with: 14 | source-repo: ipfs-examples/helia-examples 15 | source-folder-path: examples/${{ github.event.repository.name }} 16 | source-branch: main 17 | target-branch: main 18 | git-username: github-actions 19 | git-email: github-actions@github.com 20 | -------------------------------------------------------------------------------- /examples/helia-script-tag/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | build 3 | dist 4 | .docs 5 | .coverage 6 | node_modules 7 | package-lock.json 8 | yarn.lock 9 | -------------------------------------------------------------------------------- /examples/helia-script-tag/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "helia-script-tag", 3 | "version": "1.0.0", 4 | "private": true, 5 | "type": "module", 6 | "description": "Using Helia in the browser via a script tag", 7 | "keywords": [], 8 | "license": "MIT", 9 | "scripts": { 10 | "clean": "rimraf ./dist ./.cache ./node_modules/.vite", 11 | "build": "vite build", 12 | "serve": "vite dev --port 8888", 13 | "start": "npm run serve", 14 | "test": "npm run build && test-browser-example test" 15 | }, 16 | "browserslist": "last 1 Chrome version", 17 | "devDependencies": { 18 | "@babel/core": "^7.14.8", 19 | "@playwright/test": "^1.12.3", 20 | "playwright": "^1.12.3", 21 | "process": "^0.11.10", 22 | "rimraf": "^6.0.1", 23 | "test-ipfs-example": "^1.0.0", 24 | "vite": "^6.0.9" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /examples/helia-script-tag/src/index.js: -------------------------------------------------------------------------------- 1 | /* global Helia, BlockstoreCore, DatastoreCore, HeliaUnixfs */ 2 | 3 | const statusValueEl = document.getElementById('statusValue') 4 | const discoveredPeerCountEl = document.getElementById('discoveredPeerCount') 5 | const connectedPeerCountEl = document.getElementById('connectedPeerCount') 6 | const connectedPeersListEl = document.getElementById('connectedPeersList') 7 | const logEl = document.getElementById('runningLog') 8 | const nodeIdEl = document.getElementById('nodeId') 9 | 10 | document.addEventListener('DOMContentLoaded', async () => { 11 | const helia = window.helia = await instantiateHeliaNode() 12 | window.heliaFs = await HeliaUnixfs.unixfs(helia) 13 | 14 | helia.libp2p.addEventListener('peer:discovery', (evt) => { 15 | window.discoveredPeers.set(evt.detail.id.toString(), evt.detail) 16 | addToLog(`Discovered peer ${evt.detail.id.toString()}`) 17 | }) 18 | 19 | helia.libp2p.addEventListener('peer:connect', (evt) => { 20 | addToLog(`Connected to ${evt.detail.toString()}`) 21 | }) 22 | helia.libp2p.addEventListener('peer:disconnect', (evt) => { 23 | addToLog(`Disconnected from ${evt.detail.toString()}`) 24 | }) 25 | 26 | setInterval(() => { 27 | statusValueEl.innerHTML = helia.libp2p.status === 'started' ? 'Online' : 'Offline' 28 | updateConnectedPeers() 29 | updateDiscoveredPeers() 30 | }, 500) 31 | 32 | const id = await helia.libp2p.peerId.toString() 33 | 34 | nodeIdEl.innerHTML = id 35 | 36 | /** 37 | * You can write more code here to use it. 38 | * 39 | * https://github.com/ipfs/helia 40 | * - helia.start 41 | * - helia.stop 42 | * 43 | * https://github.com/ipfs/helia-unixfs 44 | * - heliaFs.addBytes 45 | * - heliaFs.addFile 46 | * - heliaFs.ls 47 | * - heliaFs.cat 48 | */ 49 | }) 50 | 51 | function ms2TimeString (a) { 52 | const k = a % 1e3 53 | const s = a / 1e3 % 60 | 0 54 | const m = a / 6e4 % 60 | 0 55 | const h = a / 36e5 % 24 | 0 56 | 57 | return (h ? (h < 10 ? '0' + h : h) + ':' : '00:') + 58 | (m < 10 ? 0 : '') + m + ':' + 59 | (s < 10 ? 0 : '') + s + ':' + 60 | (k < 100 ? k < 10 ? '00' : 0 : '') + k 61 | } 62 | 63 | const getLogLineEl = (msg) => { 64 | const logLine = document.createElement('span') 65 | logLine.innerHTML = `${ms2TimeString(performance.now())} - ${msg}` 66 | 67 | return logLine 68 | } 69 | const addToLog = (msg) => { 70 | logEl.appendChild(getLogLineEl(msg)) 71 | } 72 | 73 | let heliaInstance = null 74 | const instantiateHeliaNode = async () => { 75 | // application-specific data lives in the datastore 76 | const datastore = new DatastoreCore.MemoryDatastore() 77 | const blockstore = new BlockstoreCore.MemoryBlockstore() 78 | 79 | if (heliaInstance != null) { 80 | return heliaInstance 81 | } 82 | 83 | heliaInstance = await Helia.createHelia({ 84 | datastore, 85 | blockstore 86 | }) 87 | addToLog('Created Helia instance') 88 | 89 | return heliaInstance 90 | } 91 | 92 | window.discoveredPeers = new Map() 93 | 94 | const updateConnectedPeers = () => { 95 | const peers = window.helia.libp2p.getPeers() 96 | connectedPeerCountEl.innerHTML = peers.length 97 | connectedPeersListEl.innerHTML = '' 98 | for (const peer of peers) { 99 | const peerEl = document.createElement('li') 100 | peerEl.innerText = peer.toString() 101 | connectedPeersListEl.appendChild(peerEl) 102 | } 103 | } 104 | 105 | const updateDiscoveredPeers = () => { 106 | discoveredPeerCountEl.innerHTML = window.discoveredPeers.size 107 | } 108 | -------------------------------------------------------------------------------- /examples/helia-script-tag/test/index.spec.js: -------------------------------------------------------------------------------- 1 | import { setup } from 'test-ipfs-example/browser' 2 | 3 | // Setup 4 | const test = setup() 5 | 6 | test.describe('using script tag:', () => { 7 | // DOM 8 | const status = '#statusValue' 9 | const node = '#nodeId' 10 | const stopHelia = '.e2e-stopHelia' 11 | 12 | test.beforeEach(async ({ servers, page }) => { 13 | await page.goto(servers[0].url) 14 | }) 15 | 16 | test('should properly initialized a IPFS node and print the status', async ({ page }) => { 17 | // wait for page to init 18 | await page.waitForSelector(`${status}:has-text("Not Started")`) 19 | 20 | // wait for helia to start 21 | await page.waitForSelector(`${status}:has-text("Online")`) 22 | await page.waitForSelector(`${node}:has-text("12D3")`) 23 | 24 | // wait for helia to stop 25 | await page.click(stopHelia) 26 | await page.waitForSelector(`${status}:has-text("Offline")`) 27 | }) 28 | }) 29 | -------------------------------------------------------------------------------- /examples/helia-script-tag/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | 3 | export default defineConfig({ 4 | build: { 5 | target: 'es2020', 6 | minify: false, 7 | // disable @rollup/plugin-commonjs https://github.com/vitejs/vite/issues/9703#issuecomment-1216662109 8 | // should be removable with vite 4 https://vitejs.dev/blog/announcing-vite3.html#esbuild-deps-optimization-at-build-time-experimental 9 | commonjsOptions: { 10 | include: [] 11 | } 12 | }, 13 | define: { 14 | 'process.env.NODE_DEBUG': 'false', 15 | global: 'globalThis' 16 | }, 17 | optimizeDeps: { 18 | // enable esbuild dep optimization during build https://github.com/vitejs/vite/issues/9703#issuecomment-1216662109 19 | // should be removable with vite 4 https://vitejs.dev/blog/announcing-vite3.html#esbuild-deps-optimization-at-build-time-experimental 20 | disabled: false, 21 | 22 | // target: es2020 added as workaround to make big ints work 23 | // - should be removable with vite 4 24 | // https://github.com/vitejs/vite/issues/9062#issuecomment-1182818044 25 | esbuildOptions: { 26 | target: 'es2020' 27 | } 28 | } 29 | }) 30 | -------------------------------------------------------------------------------- /examples/helia-ts-node/.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | # ⚠️ IMPORTANT ⚠️ 2 | 3 | # Please do not create a Pull Request for this repository 4 | 5 | The contents of this repository are automatically synced from the parent [Helia Examples Project](https://github.com/ipfs-examples/helia-examples) so any changes made to the standalone repository will be lost after the next sync. 6 | 7 | Please open a PR against [IPFS Examples](https://github.com/ipfs-examples/helia-examples) instead. 8 | 9 | ## Contributing 10 | 11 | Contributions are what make the open source community such an amazing place to be learn, inspire, and create. Any contributions you make are **greatly appreciated**. 12 | 13 | 1. Fork the [Helia Examples Project](https://github.com/ipfs-examples/helia-examples) 14 | 2. Create your Feature Branch (`git checkout -b feature/amazing-example`) 15 | 3. Commit your Changes (`git commit -a -m 'feat: add some amazing example'`) 16 | 4. Push to the Branch (`git push origin feature/amazing-example`) 17 | 5. Open a Pull Request 18 | -------------------------------------------------------------------------------- /examples/helia-ts-node/.github/workflows/sync.yml: -------------------------------------------------------------------------------- 1 | name: pull 2 | 3 | on: 4 | workflow_dispatch 5 | 6 | jobs: 7 | sync: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v2 11 | - name: Pull from another repository 12 | uses: ipfs-examples/actions-pull-directory-from-repo@main 13 | with: 14 | source-repo: ipfs-examples/helia-examples 15 | source-folder-path: examples/${{ github.event.repository.name }} 16 | source-branch: main 17 | target-branch: main 18 | git-username: github-actions 19 | git-email: github-actions@github.com 20 | -------------------------------------------------------------------------------- /examples/helia-ts-node/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "helia-ts-node", 3 | "version": "1.0.0", 4 | "private": true, 5 | "type": "module", 6 | "description": "Running Helia with ts-node", 7 | "scripts": { 8 | "start": "cross-env NODE_OPTIONS=\"--loader=ts-node/esm\" node src/index.ts", 9 | "test": "cross-env NODE_OPTIONS=\"--loader=ts-node/esm\" test-node-example test/*" 10 | }, 11 | "dependencies": { 12 | "helia": "^5.0.0" 13 | }, 14 | "devDependencies": { 15 | "cross-env": "^7.0.3", 16 | "test-ipfs-example": "^1.2.0", 17 | "ts-node": "^10.9.2", 18 | "typescript": "^5.3.3" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /examples/helia-ts-node/src/index.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | 3 | import { createHelia } from 'helia' 4 | 5 | const node = await createHelia() 6 | 7 | console.info('Helia is running') 8 | console.info('PeerId:', node.libp2p.peerId.toString()) 9 | -------------------------------------------------------------------------------- /examples/helia-ts-node/test/index.spec.js: -------------------------------------------------------------------------------- 1 | import path from 'path' 2 | import { fileURLToPath } from 'url' 3 | import { waitForOutput } from 'test-ipfs-example/node' 4 | 5 | const __dirname = path.dirname(fileURLToPath(import.meta.url)) 6 | 7 | // ts-node is currently broken on node 20 - https://github.com/TypeStrong/ts-node/issues/1997 8 | // await waitForOutput('Helia is running', 'ts-node', ['--esm', path.resolve(__dirname, '../src/index.ts')]) 9 | await waitForOutput('Helia is running', 'node', ['--loader', 'ts-node/esm', path.resolve(__dirname, '../src/index.ts')]) 10 | -------------------------------------------------------------------------------- /examples/helia-ts-node/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "ES2022", 4 | "target": "ES2021", 5 | "moduleResolution": "node", 6 | 7 | // only necessary for the example linting check, your project may not need 8 | // these settings 9 | "strictNullChecks": true, 10 | "skipLibCheck": true 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /examples/helia-typescript/.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | # ⚠️ IMPORTANT ⚠️ 2 | 3 | # Please do not create a Pull Request for this repository 4 | 5 | The contents of this repository are automatically synced from the parent [Helia Examples Project](https://github.com/ipfs-examples/helia-examples) so any changes made to the standalone repository will be lost after the next sync. 6 | 7 | Please open a PR against [IPFS Examples](https://github.com/ipfs-examples/helia-examples) instead. 8 | 9 | ## Contributing 10 | 11 | Contributions are what make the open source community such an amazing place to be learn, inspire, and create. Any contributions you make are **greatly appreciated**. 12 | 13 | 1. Fork the [Helia Examples Project](https://github.com/ipfs-examples/helia-examples) 14 | 2. Create your Feature Branch (`git checkout -b feature/amazing-example`) 15 | 3. Commit your Changes (`git commit -a -m 'feat: add some amazing example'`) 16 | 4. Push to the Branch (`git push origin feature/amazing-example`) 17 | 5. Open a Pull Request 18 | -------------------------------------------------------------------------------- /examples/helia-typescript/.github/workflows/sync.yml: -------------------------------------------------------------------------------- 1 | name: pull 2 | 3 | on: 4 | workflow_dispatch 5 | 6 | jobs: 7 | sync: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v2 11 | - name: Pull from another repository 12 | uses: ipfs-examples/actions-pull-directory-from-repo@main 13 | with: 14 | source-repo: ipfs-examples/helia-examples 15 | source-folder-path: examples/${{ github.event.repository.name }} 16 | source-branch: main 17 | target-branch: main 18 | git-username: github-actions 19 | git-email: github-actions@github.com 20 | -------------------------------------------------------------------------------- /examples/helia-typescript/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | build 3 | dist 4 | .docs 5 | .coverage 6 | node_modules 7 | package-lock.json 8 | yarn.lock 9 | -------------------------------------------------------------------------------- /examples/helia-typescript/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "helia-typescript", 3 | "version": "1.0.0", 4 | "private": true, 5 | "type": "module", 6 | "description": "Building Helia with TypeScript", 7 | "license": "MIT", 8 | "main": "index.js", 9 | "scripts": { 10 | "build": "tsc", 11 | "start": "node dist/src/index.js", 12 | "test": "npm run build && test-node-example test/*" 13 | }, 14 | "dependencies": { 15 | "helia": "^5.0.0" 16 | }, 17 | "devDependencies": { 18 | "test-ipfs-example": "^1.0.0", 19 | "typescript": "^5.1.6" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /examples/helia-typescript/src/index.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | 3 | import { createHelia } from 'helia' 4 | 5 | void createHelia() 6 | .then(helia => { 7 | console.info('Helia is running') 8 | console.info('PeerId:', helia.libp2p.peerId.toString()) 9 | }) 10 | -------------------------------------------------------------------------------- /examples/helia-typescript/test/index.spec.js: -------------------------------------------------------------------------------- 1 | import path from 'path' 2 | import { fileURLToPath } from 'url' 3 | import { waitForOutput } from 'test-ipfs-example/node' 4 | 5 | const __dirname = path.dirname(fileURLToPath(import.meta.url)) 6 | 7 | await waitForOutput('Helia is running', 'node', [path.resolve(__dirname, '../dist/index.js')]) 8 | -------------------------------------------------------------------------------- /examples/helia-typescript/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "ES2015", 4 | "target": "ES2015", 5 | 6 | "moduleResolution": "node", 7 | "skipLibCheck": true, 8 | "outDir": "./dist", 9 | 10 | // only necessary for the example linting check, your project may not need 11 | // these settings 12 | "strictNullChecks": true 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /examples/helia-vite/.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | # ⚠️ IMPORTANT ⚠️ 2 | 3 | # Please do not create a Pull Request for this repository 4 | 5 | The contents of this repository are automatically synced from the parent [Helia Examples Project](https://github.com/ipfs-examples/helia-examples) so any changes made to the standalone repository will be lost after the next sync. 6 | 7 | Please open a PR against [IPFS Examples](https://github.com/ipfs-examples/helia-examples) instead. 8 | 9 | ## Contributing 10 | 11 | Contributions are what make the open source community such an amazing place to be learn, inspire, and create. Any contributions you make are **greatly appreciated**. 12 | 13 | 1. Fork the [Helia Examples Project](https://github.com/ipfs-examples/helia-examples) 14 | 2. Create your Feature Branch (`git checkout -b feature/amazing-example`) 15 | 3. Commit your Changes (`git commit -a -m 'feat: add some amazing example'`) 16 | 4. Push to the Branch (`git push origin feature/amazing-example`) 17 | 5. Open a Pull Request 18 | -------------------------------------------------------------------------------- /examples/helia-vite/.github/workflows/sync.yml: -------------------------------------------------------------------------------- 1 | name: pull 2 | 3 | on: 4 | workflow_dispatch 5 | 6 | jobs: 7 | sync: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v2 11 | - name: Pull from another repository 12 | uses: ipfs-examples/actions-pull-directory-from-repo@main 13 | with: 14 | source-repo: ipfs-examples/helia-examples 15 | source-folder-path: examples/${{ github.event.repository.name }} 16 | source-branch: main 17 | target-branch: main 18 | git-username: github-actions 19 | git-email: github-actions@github.com 20 | -------------------------------------------------------------------------------- /examples/helia-vite/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | /test-results/ 26 | -------------------------------------------------------------------------------- /examples/helia-vite/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Helia + Vite + React 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /examples/helia-vite/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "helia-vite", 3 | "version": "0.0.0", 4 | "description": "Using Helia with vite", 5 | "private": true, 6 | "type": "module", 7 | "scripts": { 8 | "clean": "rimraf ./dist", 9 | "dev": "vite", 10 | "start": "vite", 11 | "build": "vite build", 12 | "preview": "vite preview", 13 | "test": "npm run build && test-browser-example test" 14 | }, 15 | "dependencies": { 16 | "@helia/unixfs": "^4.0.0", 17 | "helia": "^5.0.0", 18 | "prop-types": "^15.8.1", 19 | "react": "^19.0.0", 20 | "react-dom": "^19.0.0", 21 | "rimraf": "^6.0.1" 22 | }, 23 | "devDependencies": { 24 | "@playwright/test": "^1.31.2", 25 | "@types/react": "^19.0.1", 26 | "@types/react-dom": "^19.0.1", 27 | "@vitejs/plugin-react": "^4.0.0", 28 | "test-ipfs-example": "^1.0.0", 29 | "vite": "^6.0.9" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /examples/helia-vite/public/hello-helia.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ipfs-examples/helia-examples/45a9729403e56fe07af54364a31cade86d447e74/examples/helia-vite/public/hello-helia.gif -------------------------------------------------------------------------------- /examples/helia-vite/public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/helia-vite/src/App.css: -------------------------------------------------------------------------------- 1 | #root { 2 | max-width: 1280px; 3 | margin: 0 auto; 4 | padding: 2rem; 5 | text-align: center; 6 | } 7 | 8 | .logo { 9 | height: 6em; 10 | padding: 1.5em; 11 | will-change: filter; 12 | transition: filter 300ms; 13 | } 14 | .logo:hover { 15 | filter: drop-shadow(0 0 2em #646cffaa); 16 | } 17 | .logo.react:hover { 18 | filter: drop-shadow(0 0 2em #61dafbaa); 19 | } 20 | 21 | @keyframes logo-spin { 22 | from { 23 | transform: rotate(0deg); 24 | } 25 | to { 26 | transform: rotate(360deg); 27 | } 28 | } 29 | 30 | @media (prefers-reduced-motion: no-preference) { 31 | a:nth-of-type(2) .logo { 32 | animation: logo-spin infinite 20s linear; 33 | } 34 | } 35 | 36 | .card { 37 | padding: 2em; 38 | } 39 | 40 | .read-the-docs { 41 | color: #888; 42 | } 43 | -------------------------------------------------------------------------------- /examples/helia-vite/src/App.jsx: -------------------------------------------------------------------------------- 1 | import { React, useState } from 'react' 2 | import './App.css' 3 | import { useCommitText } from '@/hooks/useCommitText' 4 | import { useHelia } from '@/hooks/useHelia' 5 | 6 | function App () { 7 | const [text, setText] = useState('') 8 | const { error, starting } = useHelia() 9 | const { 10 | cidString, 11 | commitText, 12 | fetchCommittedText, 13 | committedText 14 | } = useCommitText() 15 | 16 | return ( 17 |
18 |
Helia Status
29 | setText(event.target.value)} 33 | type="text" /> 34 | 38 |
textCid: {cidString}
41 | { cidString && (<> 42 | 46 |
Committed Text: {committedText}
49 | ) 50 | } 51 | 52 |
53 | ) 54 | } 55 | 56 | export default App 57 | -------------------------------------------------------------------------------- /examples/helia-vite/src/hooks/useCommitText.jsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | 3 | import { useState, useCallback } from 'react' 4 | import { useHelia } from '@/hooks/useHelia' 5 | 6 | const encoder = new TextEncoder() 7 | const decoder = new TextDecoder() 8 | 9 | export const useCommitText = () => { 10 | const { helia, fs, error, starting } = useHelia() 11 | const [cid, setCid] = useState(null) 12 | const [cidString, setCidString] = useState('') 13 | const [committedText, setCommittedText] = useState('') 14 | 15 | const commitText = useCallback(async (text) => { 16 | if (!error && !starting) { 17 | try { 18 | const cid = await fs.addBytes( 19 | encoder.encode(text), 20 | helia.blockstore 21 | ) 22 | setCid(cid) 23 | setCidString(cid.toString()) 24 | console.log('Added file:', cid.toString()) 25 | } catch (e) { 26 | console.error(e) 27 | } 28 | } else { 29 | console.log('please wait for helia to start') 30 | } 31 | }, [error, starting, helia, fs]) 32 | 33 | const fetchCommittedText = useCallback(async () => { 34 | let text = '' 35 | if (!error && !starting) { 36 | try { 37 | for await (const chunk of fs.cat(cid)) { 38 | text += decoder.decode(chunk, { 39 | stream: true 40 | }) 41 | } 42 | setCommittedText(text) 43 | } catch (e) { 44 | console.error(e) 45 | } 46 | } else { 47 | console.log('please wait for helia to start') 48 | } 49 | }, [error, starting, cid, helia, fs]) 50 | // If one forgets to add helia in the dependency array in commitText, additions to the blockstore will not be picked up by react, leading to operations on fs to hang indefinitely in the generator state. As such it would be good practice to ensure to include helia inside the dependency array of all hooks to tell react that the useCallback needs the most up to date helia state 51 | 52 | return { cidString, committedText, commitText, fetchCommittedText } 53 | } 54 | -------------------------------------------------------------------------------- /examples/helia-vite/src/hooks/useHelia.jsx: -------------------------------------------------------------------------------- 1 | import { useContext } from 'react' 2 | import { HeliaContext } from '@/provider/HeliaProvider' 3 | 4 | export const useHelia = () => { 5 | const { helia, fs, error, starting } = useContext(HeliaContext) 6 | return { helia, fs, error, starting } 7 | } 8 | -------------------------------------------------------------------------------- /examples/helia-vite/src/index.css: -------------------------------------------------------------------------------- 1 | :root { 2 | font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; 3 | line-height: 1.5; 4 | font-weight: 400; 5 | 6 | color-scheme: light dark; 7 | color: rgba(255, 255, 255, 0.87); 8 | background-color: #242424; 9 | 10 | font-synthesis: none; 11 | text-rendering: optimizeLegibility; 12 | -webkit-font-smoothing: antialiased; 13 | -moz-osx-font-smoothing: grayscale; 14 | -webkit-text-size-adjust: 100%; 15 | } 16 | 17 | a { 18 | font-weight: 500; 19 | color: #646cff; 20 | text-decoration: inherit; 21 | } 22 | a:hover { 23 | color: #535bf2; 24 | } 25 | 26 | body { 27 | margin: 0; 28 | display: flex; 29 | min-width: 320px; 30 | min-height: 100vh; 31 | } 32 | 33 | h1 { 34 | font-size: 3.2em; 35 | line-height: 1.1; 36 | } 37 | 38 | button { 39 | border-radius: 8px; 40 | border: 1px solid transparent; 41 | padding: 0.6em 1.2em; 42 | font-size: 1em; 43 | font-weight: 500; 44 | font-family: inherit; 45 | background-color: #1a1a1a; 46 | cursor: pointer; 47 | transition: border-color 0.25s; 48 | } 49 | button:hover { 50 | border-color: #646cff; 51 | } 52 | button:focus, 53 | button:focus-visible { 54 | outline: 4px auto -webkit-focus-ring-color; 55 | } 56 | 57 | @media (prefers-color-scheme: light) { 58 | :root { 59 | color: #213547; 60 | background-color: #ffffff; 61 | } 62 | a:hover { 63 | color: #747bff; 64 | } 65 | button { 66 | background-color: #f9f9f9; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /examples/helia-vite/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom/client' 3 | import App from './App' 4 | import './index.css' 5 | import { HeliaProvider } from '@/provider/HeliaProvider' 6 | 7 | ReactDOM.createRoot(document.getElementById('root')).render( 8 | 9 | 10 | 11 | 12 | 13 | ) 14 | -------------------------------------------------------------------------------- /examples/helia-vite/src/provider/HeliaProvider.jsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | 3 | import { unixfs } from '@helia/unixfs' 4 | import { createHelia } from 'helia' 5 | import PropTypes from 'prop-types' 6 | import { 7 | React, 8 | useEffect, 9 | useState, 10 | useCallback, 11 | createContext 12 | } from 'react' 13 | 14 | export const HeliaContext = createContext({ 15 | helia: null, 16 | fs: null, 17 | error: false, 18 | starting: true 19 | }) 20 | 21 | export const HeliaProvider = ({ children }) => { 22 | const [helia, setHelia] = useState(null) 23 | const [fs, setFs] = useState(null) 24 | const [starting, setStarting] = useState(true) 25 | const [error, setError] = useState(null) 26 | 27 | const startHelia = useCallback(async () => { 28 | if (helia) { 29 | console.info('helia already started') 30 | } else if (window.helia) { 31 | console.info('found a windowed instance of helia, populating ...') 32 | setHelia(window.helia) 33 | setFs(unixfs(helia)) 34 | setStarting(false) 35 | } else { 36 | try { 37 | console.info('Starting Helia') 38 | const helia = await createHelia() 39 | setHelia(helia) 40 | setFs(unixfs(helia)) 41 | setStarting(false) 42 | } catch (e) { 43 | console.error(e) 44 | setError(true) 45 | } 46 | } 47 | }, []) 48 | 49 | useEffect(() => { 50 | startHelia() 51 | }, []) 52 | 53 | return ( 54 | {children} 62 | ) 63 | } 64 | 65 | HeliaProvider.propTypes = { 66 | children: PropTypes.any 67 | } 68 | -------------------------------------------------------------------------------- /examples/helia-vite/test/index.spec.js: -------------------------------------------------------------------------------- 1 | import { setup, expect } from 'test-ipfs-example/browser' 2 | 3 | // Setup 4 | const test = setup() 5 | 6 | test.describe('Use Helia With react and vite', () => { 7 | // DOM 8 | const heliaStatus = '#heliaStatus' 9 | const textInput = '#textInput' 10 | const commitTextButton = '#commitTextButton' 11 | const cidOutput = '#cidOutput' 12 | const fetchCommittedTextButton = '#fetchCommittedTextButton' 13 | const committedTextOutput = '#committedTextOutput' 14 | 15 | test.beforeEach(async ({ servers, page }) => { 16 | await page.goto(servers[0].url) 17 | }) 18 | 19 | test('should properly initialize a Helia node and add/get a file', async ({ page }) => { 20 | // wait for helia node to be online 21 | const text = 'Hello Helia' 22 | const status = await page.locator(heliaStatus) 23 | await expect(status).toHaveCSS( 24 | 'border-color', 25 | 'rgb(0, 128, 0)', // green 26 | { timeout: 7000 } 27 | ) 28 | 29 | // commit text to the blockstore 30 | await page.fill(textInput, text) 31 | await page.click(commitTextButton) 32 | 33 | await page.waitForSelector(`${cidOutput}:has-text("bafkreig7i5kbqdnoooievvfextf27eoherluxe2pi3j26hu6zpiauydydy")`) 34 | const cidOutputContent = await page.textContent(cidOutput) 35 | 36 | expect(cidOutputContent).toContain('bafkreig7i5kbqdnoooievvfextf27eoherluxe2pi3j26hu6zpiauydydy') 37 | 38 | // retrieve text from blockstore 39 | await page.click(fetchCommittedTextButton) 40 | await page.waitForSelector(`${committedTextOutput}:has-text("${text}")`) 41 | const committedTextOutputLocator = await page.locator(committedTextOutput) 42 | await expect(committedTextOutputLocator).toHaveText(`Committed Text: ${text}`, { timeout: 2000 }) 43 | }) 44 | }) 45 | -------------------------------------------------------------------------------- /examples/helia-vite/vite.config.js: -------------------------------------------------------------------------------- 1 | import path from 'path' 2 | import react from '@vitejs/plugin-react' 3 | import { defineConfig } from 'vite' 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | resolve: { 8 | alias: [{ find: '@', replacement: path.resolve(__dirname, 'src') }] 9 | } 10 | }) 11 | -------------------------------------------------------------------------------- /examples/helia-vue/.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | # ⚠️ IMPORTANT ⚠️ 2 | 3 | # Please do not create a Pull Request for this repository 4 | 5 | The contents of this repository are automatically synced from the parent [Helia Examples Project](https://github.com/ipfs-examples/helia-examples) so any changes made to the standalone repository will be lost after the next sync. 6 | 7 | Please open a PR against [IPFS Examples](https://github.com/ipfs-examples/helia-examples) instead. 8 | 9 | ## Contributing 10 | 11 | Contributions are what make the open source community such an amazing place to be learn, inspire, and create. Any contributions you make are **greatly appreciated**. 12 | 13 | 1. Fork the [Helia Examples Project](https://github.com/ipfs-examples/helia-examples) 14 | 2. Create your Feature Branch (`git checkout -b feature/amazing-example`) 15 | 3. Commit your Changes (`git commit -a -m 'feat: add some amazing example'`) 16 | 4. Push to the Branch (`git push origin feature/amazing-example`) 17 | 5. Open a Pull Request 18 | -------------------------------------------------------------------------------- /examples/helia-vue/.github/workflows/sync.yml: -------------------------------------------------------------------------------- 1 | name: pull 2 | 3 | on: 4 | workflow_dispatch 5 | 6 | jobs: 7 | sync: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v2 11 | - name: Pull from another repository 12 | uses: ipfs-examples/actions-pull-directory-from-repo@main 13 | with: 14 | source-repo: ipfs-examples/helia-examples 15 | source-folder-path: examples/${{ github.event.repository.name }} 16 | source-branch: main 17 | target-branch: main 18 | git-username: github-actions 19 | git-email: github-actions@github.com 20 | -------------------------------------------------------------------------------- /examples/helia-vue/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | .DS_Store 12 | dist 13 | dist-ssr 14 | coverage 15 | *.local 16 | 17 | /cypress/videos/ 18 | /cypress/screenshots/ 19 | 20 | # Editor directories and files 21 | .vscode/* 22 | !.vscode/extensions.json 23 | .idea 24 | *.suo 25 | *.ntvs* 26 | *.njsproj 27 | *.sln 28 | *.sw? 29 | 30 | test-results/ 31 | playwright-report/ 32 | -------------------------------------------------------------------------------- /examples/helia-vue/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite App 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /examples/helia-vue/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "helia-vue", 3 | "version": "0.0.0", 4 | "description": "Using Helia with vue", 5 | "private": true, 6 | "type": "module", 7 | "scripts": { 8 | "clean": "rimraf ./dist", 9 | "dev": "vite", 10 | "start": "vite", 11 | "build": "vite build", 12 | "preview": "vite preview", 13 | "test": "npm run build && test-browser-example test" 14 | }, 15 | "dependencies": { 16 | "@helia/unixfs": "^4.0.0", 17 | "helia": "^5.0.0", 18 | "it-all": "^3.0.1", 19 | "vue": "^3.2.47" 20 | }, 21 | "devDependencies": { 22 | "@vitejs/plugin-vue": "^5.0.3", 23 | "rimraf": "^6.0.1", 24 | "test-ipfs-example": "^1.0.0", 25 | "vite": "^6.0.9" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /examples/helia-vue/public/demo-vue.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ipfs-examples/helia-examples/45a9729403e56fe07af54364a31cade86d447e74/examples/helia-vue/public/demo-vue.gif -------------------------------------------------------------------------------- /examples/helia-vue/src/App.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 32 | 33 | 62 | -------------------------------------------------------------------------------- /examples/helia-vue/src/HeliaApi/useCommitText.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | 3 | import { inject, ref } from 'vue' 4 | 5 | const encoder = new TextEncoder() 6 | const decoder = new TextDecoder() 7 | 8 | export const useCommitText = () => { 9 | const cid = ref() 10 | const commitedText = ref() 11 | const { loading, error, helia, fs } = inject('HeliaProvider') 12 | 13 | const commitText = async (text) => { 14 | console.log('text', text) 15 | if (error.value.length === 0 && !loading.value) { 16 | try { 17 | const res = await fs.value.addBytes( 18 | encoder.encode(text), 19 | helia.value.blockstore 20 | ) 21 | cid.value = res 22 | } catch (e) { 23 | console.error(e) 24 | } 25 | } else { 26 | console.log('please wait for helia to start') 27 | } 28 | } 29 | 30 | const fetchCommitedText = async () => { 31 | let text = '' 32 | console.log(cid) 33 | if (error.value.length === 0 && !loading.value && cid.value) { 34 | try { 35 | for await (const chunk of fs.value.cat(cid.value)) { 36 | text += decoder.decode(chunk, { 37 | stream: true 38 | }) 39 | } 40 | commitedText.value = text 41 | } catch (e) { 42 | console.error(e) 43 | } 44 | } else { 45 | console.log('please wait for helia to start') 46 | } 47 | } 48 | 49 | return { cid, commitText, commitedText, fetchCommitedText } 50 | } 51 | -------------------------------------------------------------------------------- /examples/helia-vue/src/HeliaApi/useUnixFS.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | 3 | import { inject } from 'vue' 4 | 5 | const encoder = new TextEncoder() 6 | const decoder = new TextDecoder() 7 | 8 | export const useUnixFS = () => { 9 | const { loading, error, fs } = inject('HeliaProvider') 10 | 11 | const getStat = async (dirCid, path) => { 12 | if (error.value.length === 0 && !loading.value) { 13 | try { 14 | const res = await fs.value.stat(dirCid, { 15 | path 16 | }) 17 | const data = { cid: res.cid, blocks: res.blocks } 18 | return { status: 'success', data } 19 | } catch (e) { 20 | console.error(e) 21 | } 22 | } else { 23 | console.log('please wait for helia to start') 24 | } 25 | } 26 | 27 | const addDirectory = async (path) => { 28 | if (error.value.length === 0 && !loading.value) { 29 | try { 30 | const emptyDirCid = await fs.value.addDirectory(path) 31 | const res = await fs.value.mkdir(emptyDirCid, path) 32 | return { status: 'success', data: res } 33 | } catch (e) { 34 | console.error(e) 35 | } 36 | } else { 37 | console.log('please wait for helia to start') 38 | } 39 | } 40 | 41 | const getDirectory = async (dirCid, path) => { 42 | const output = [] 43 | if (error.value.length === 0 && !loading.value) { 44 | try { 45 | if (typeof path !== 'undefined') { 46 | for await (const entry of fs.value.ls(dirCid, { 47 | path 48 | })) { 49 | output.push(entry) 50 | } 51 | } else { 52 | for await (const entry of fs.value.ls(dirCid)) { 53 | output.push(entry) 54 | } 55 | } 56 | return { status: 'success', data: output } 57 | } catch (e) { 58 | console.error(e) 59 | } 60 | } else { 61 | console.log('please wait for helia to start') 62 | } 63 | } 64 | 65 | const addFile = async (name, dirCid, content) => { 66 | if (error.value.length === 0 && !loading.value) { 67 | try { 68 | const res = await fs.value.addFile({ 69 | content: encoder.encode(content) 70 | }) 71 | const updatedCid = await fs.value.cp(res, dirCid, name) 72 | return { status: 'success', data: { dirCid: updatedCid, fileCid: res } } 73 | } catch (e) { 74 | console.error(e) 75 | } 76 | } else { 77 | console.log('please wait for helia to start') 78 | } 79 | } 80 | 81 | const getFile = async (cid) => { 82 | if (error.value.length === 0 && !loading.value) { 83 | let txt = '' 84 | try { 85 | for await (const buf of fs.value.cat(cid)) { 86 | txt += decoder.decode(buf, { 87 | stream: true 88 | }) 89 | } 90 | return { status: 'success', data: txt } 91 | } catch (e) { 92 | console.error(e) 93 | } 94 | } else { 95 | console.log('please wait for helia to start') 96 | } 97 | } 98 | return { 99 | getStat, 100 | addDirectory, 101 | getDirectory, 102 | addFile, 103 | getFile 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /examples/helia-vue/src/assets/base.css: -------------------------------------------------------------------------------- 1 | /* color palette from */ 2 | :root { 3 | --vt-c-white: #ffffff; 4 | --vt-c-white-soft: #f8f8f8; 5 | --vt-c-white-mute: #f2f2f2; 6 | 7 | --vt-c-black: #181818; 8 | --vt-c-black-soft: #222222; 9 | --vt-c-black-mute: #282828; 10 | 11 | --vt-c-indigo: #2c3e50; 12 | 13 | --vt-c-divider-light-1: rgba(60, 60, 60, 0.29); 14 | --vt-c-divider-light-2: rgba(60, 60, 60, 0.12); 15 | --vt-c-divider-dark-1: rgba(84, 84, 84, 0.65); 16 | --vt-c-divider-dark-2: rgba(84, 84, 84, 0.48); 17 | 18 | --vt-c-text-light-1: var(--vt-c-indigo); 19 | --vt-c-text-light-2: rgba(60, 60, 60, 0.66); 20 | --vt-c-text-dark-1: var(--vt-c-white); 21 | --vt-c-text-dark-2: rgba(235, 235, 235, 0.64); 22 | } 23 | 24 | /* semantic color variables for this project */ 25 | :root { 26 | --color-background: var(--vt-c-white); 27 | --color-background-soft: var(--vt-c-white-soft); 28 | --color-background-mute: var(--vt-c-white-mute); 29 | 30 | --color-border: var(--vt-c-divider-light-2); 31 | --color-border-hover: var(--vt-c-divider-light-1); 32 | 33 | --color-heading: var(--vt-c-text-light-1); 34 | --color-text: var(--vt-c-text-light-1); 35 | 36 | --section-gap: 160px; 37 | } 38 | 39 | @media (prefers-color-scheme: dark) { 40 | :root { 41 | --color-background: var(--vt-c-black); 42 | --color-background-soft: var(--vt-c-black-soft); 43 | --color-background-mute: var(--vt-c-black-mute); 44 | 45 | --color-border: var(--vt-c-divider-dark-2); 46 | --color-border-hover: var(--vt-c-divider-dark-1); 47 | 48 | --color-heading: var(--vt-c-text-dark-1); 49 | --color-text: var(--vt-c-text-dark-2); 50 | } 51 | } 52 | 53 | *, 54 | *::before, 55 | *::after { 56 | box-sizing: border-box; 57 | margin: 0; 58 | position: relative; 59 | font-weight: normal; 60 | } 61 | 62 | body { 63 | min-height: 100vh; 64 | color: var(--color-text); 65 | background: var(--color-background); 66 | transition: color 0.5s, background-color 0.5s; 67 | line-height: 1.6; 68 | font-family: Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, 69 | Cantarell, 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; 70 | font-size: 15px; 71 | text-rendering: optimizeLegibility; 72 | -webkit-font-smoothing: antialiased; 73 | -moz-osx-font-smoothing: grayscale; 74 | } 75 | -------------------------------------------------------------------------------- /examples/helia-vue/src/assets/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /examples/helia-vue/src/assets/main.css: -------------------------------------------------------------------------------- 1 | @import './base.css'; 2 | 3 | #app { 4 | max-width: 1280px; 5 | margin: 0 auto; 6 | padding: 2rem; 7 | 8 | font-weight: normal; 9 | } 10 | 11 | a, 12 | .green { 13 | text-decoration: none; 14 | color: hsla(160, 100%, 37%, 1); 15 | transition: 0.4s; 16 | } 17 | 18 | @media (hover: hover) { 19 | a:hover { 20 | background-color: hsla(160, 100%, 37%, 0.2); 21 | } 22 | } 23 | 24 | -------------------------------------------------------------------------------- /examples/helia-vue/src/components/TextCommiter.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 27 | 28 | -------------------------------------------------------------------------------- /examples/helia-vue/src/components/UnixFSManager.vue: -------------------------------------------------------------------------------- 1 | 73 | 104 | -------------------------------------------------------------------------------- /examples/helia-vue/src/main.js: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue' 2 | import { HeliaProviderPlugin } from './plugins/HeliaProviderPlugin' 3 | import App from '@/App.vue' 4 | 5 | import './assets/main.css' 6 | 7 | const app = createApp(App) 8 | app.use(HeliaProviderPlugin) 9 | app.mount('#app') 10 | -------------------------------------------------------------------------------- /examples/helia-vue/src/plugins/HeliaProviderPlugin.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | 3 | import { unixfs } from '@helia/unixfs' 4 | import { createHelia } from 'helia' 5 | import { ref } from 'vue' 6 | 7 | export const HeliaProviderPlugin = { 8 | install: async (app) => { 9 | const loading = ref(true) 10 | const error = ref('') 11 | const helia = ref() 12 | const fs = ref() 13 | app.provide('HeliaProvider', { 14 | loading, 15 | error, 16 | helia, 17 | fs 18 | }) 19 | try { 20 | const instance = await createHelia() 21 | loading.value = false 22 | helia.value = instance 23 | fs.value = unixfs(instance) 24 | } catch (e) { 25 | console.error(e) 26 | error.value = e.toString() 27 | loading.value = false 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /examples/helia-vue/test/index.spec.js: -------------------------------------------------------------------------------- 1 | import { setup, expect } from 'test-ipfs-example/browser' 2 | 3 | // Setup 4 | const test = setup() 5 | 6 | test.describe('Use Helia with Vue', () => { 7 | test.beforeEach(async ({ servers, page }) => { 8 | await page.goto(servers[0].url) 9 | }) 10 | 11 | test('should properly initialize Helia, and add/get a file', async ({ page }) => { 12 | const exampleText = 'Hello Helia' 13 | const exampleDirName = 'newdir' 14 | const exampleFileName = 'test.txt' 15 | 16 | const status = page.locator('#heliaStatus') 17 | await expect(status).toHaveCSS( 18 | 'background-color', 19 | 'rgb(0, 128, 0)', 20 | { timeout: 5000 } 21 | ) 22 | 23 | await page.fill('#commitText', exampleText) 24 | await page.click('#commitTextButton') 25 | const cidOutput = page.locator('#commitTextCidOutput') 26 | await expect(cidOutput).toHaveText( 27 | 'cid: bafkreig7i5kbqdnoooievvfextf27eoherluxe2pi3j26hu6zpiauydydy', 28 | { timeout: 2000 } 29 | ) 30 | 31 | await page.click('#fetchCommitedTextButton') 32 | const textOutput = page.locator('#fetchedCommitedTextOutput') 33 | await expect(textOutput).toHaveText( 34 | `added text: ${exampleText}`, 35 | { timeout: 2000 } 36 | ) 37 | 38 | await page.fill('#newDirInput', exampleDirName) 39 | await page.click('#newDirButton') 40 | const dirOutput = page.locator('#newDirOutput') 41 | await expect(dirOutput).toHaveText( 42 | 'directory Cid: bafybeif5hfzip34o7ocwfg4ge537g7o3nbh3auc3s54l3gsaantucqyyra', 43 | { timeout: 2000 } 44 | ) 45 | 46 | await page.click('#statDirButton') 47 | const statDir = page.locator('#statDirOutput') 48 | await expect(statDir).toContainText( 49 | 'bafybeiczsscdsbs7ffqz55asqdf3smv6klcw3gofszvwlyarci47bgf354', 50 | { timeout: 2000 } 51 | ) 52 | 53 | await page.click('#getDirButton') 54 | const lsDir = await page.locator('#dirListOutput') 55 | await expect(lsDir).toContainText( 56 | 'bafybeif5hfzip34o7ocwfg4ge537g7o3nbh3auc3s54l3gsaantucqyyra/newdir', 57 | { timeout: 2000 } 58 | ) 59 | 60 | await page.fill('#fileNameInput', exampleFileName) 61 | await page.fill('#fileContentInput', exampleText) 62 | await page.click('#newFileButton') 63 | const fileCidOutput = page.locator('#fileCidOutput') 64 | await expect(fileCidOutput).toContainText( 65 | 'bafkreig7i5kbqdnoooievvfextf27eoherluxe2pi3j26hu6zpiauydydy', 66 | { timeout: 2000 } 67 | ) 68 | const updatedDirOutput = page.locator('#updatedDirOutput') 69 | await expect(updatedDirOutput).toContainText( 70 | 'bafybeie6lhtnjvea4j7imyx5lp2c7kfgxjkndc4jjpmcetnkq75lapmgwm', 71 | { timeout: 2000 } 72 | ) 73 | 74 | await page.click('#dirContentsButton') 75 | const dirContentsOutput = page.locator('#dirContentsOutput') 76 | await expect(dirContentsOutput).toContainText( 77 | 'bafkreig7i5kbqdnoooievvfextf27eoherluxe2pi3j26hu6zpiauydydy', 78 | { timeout: 2000 } 79 | ) 80 | await page.click('#fileContentsButton') 81 | const fileContentsOutput = page.locator('#fileContentsOutput') 82 | await expect(fileContentsOutput).toContainText( 83 | exampleText, 84 | { timeout: 2000 } 85 | ) 86 | }) 87 | }) 88 | -------------------------------------------------------------------------------- /examples/helia-vue/vite.config.js: -------------------------------------------------------------------------------- 1 | import { fileURLToPath, URL } from 'node:url' 2 | import vue from '@vitejs/plugin-vue' 3 | import { defineConfig } from 'vite' 4 | 5 | // https://vitejs.dev/config/ 6 | export default defineConfig({ 7 | plugins: [vue()], 8 | resolve: { 9 | alias: { 10 | '@': fileURLToPath(new URL('./src', import.meta.url)) 11 | } 12 | } 13 | }) 14 | -------------------------------------------------------------------------------- /examples/helia-webpack/.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | # ⚠️ IMPORTANT ⚠️ 2 | 3 | # Please do not create a Pull Request for this repository 4 | 5 | The contents of this repository are automatically synced from the parent [Helia Examples Project](https://github.com/ipfs-examples/helia-examples) so any changes made to the standalone repository will be lost after the next sync. 6 | 7 | Please open a PR against [IPFS Examples](https://github.com/ipfs-examples/helia-examples) instead. 8 | 9 | ## Contributing 10 | 11 | Contributions are what make the open source community such an amazing place to be learn, inspire, and create. Any contributions you make are **greatly appreciated**. 12 | 13 | 1. Fork the [Helia Examples Project](https://github.com/ipfs-examples/helia-examples) 14 | 2. Create your Feature Branch (`git checkout -b feature/amazing-example`) 15 | 3. Commit your Changes (`git commit -a -m 'feat: add some amazing example'`) 16 | 4. Push to the Branch (`git push origin feature/amazing-example`) 17 | 5. Open a Pull Request 18 | -------------------------------------------------------------------------------- /examples/helia-webpack/.github/workflows/sync.yml: -------------------------------------------------------------------------------- 1 | name: pull 2 | 3 | on: 4 | workflow_dispatch 5 | 6 | jobs: 7 | sync: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v2 11 | - name: Pull from another repository 12 | uses: ipfs-examples/actions-pull-directory-from-repo@main 13 | with: 14 | source-repo: ipfs-examples/helia-examples 15 | source-folder-path: examples/${{ github.event.repository.name }} 16 | source-branch: main 17 | target-branch: main 18 | git-username: github-actions 19 | git-email: github-actions@github.com 20 | -------------------------------------------------------------------------------- /examples/helia-webpack/img/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ipfs-examples/helia-examples/45a9729403e56fe07af54364a31cade86d447e74/examples/helia-webpack/img/1.png -------------------------------------------------------------------------------- /examples/helia-webpack/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "helia-webpack", 3 | "version": "1.0.0", 4 | "private": true, 5 | "type": "module", 6 | "description": "Bundle Helia with Webpack", 7 | "keywords": [], 8 | "license": "MIT", 9 | "scripts": { 10 | "clean": "rimraf ./dist", 11 | "build": "webpack --env production", 12 | "serve": "webpack serve --mode=development", 13 | "start": "npm run serve", 14 | "test": "npm run build && test-browser-example test" 15 | }, 16 | "browserslist": [ 17 | "last 1 Chrome version" 18 | ], 19 | "dependencies": { 20 | "@helia/unixfs": "^4.0.0", 21 | "helia": "^5.0.0", 22 | "react": "^19.0.0", 23 | "react-dom": "^19.0.0" 24 | }, 25 | "devDependencies": { 26 | "@babel/core": "^7.13.10", 27 | "@babel/preset-env": "^7.13.12", 28 | "@babel/preset-react": "^7.12.13", 29 | "@playwright/test": "^1.12.3", 30 | "babel-loader": "^9.1.2", 31 | "copy-webpack-plugin": "^12.0.2", 32 | "css-loader": "^7.1.2", 33 | "html-webpack-plugin": "^5.3.2", 34 | "node-polyfill-webpack-plugin": "^4.0.0", 35 | "playwright": "^1.12.3", 36 | "rimraf": "^6.0.1", 37 | "style-loader": "^4.0.0", 38 | "test-ipfs-example": "^1.0.0", 39 | "webpack": "^5.45.1", 40 | "webpack-cli": "^6.0.1", 41 | "webpack-dev-server": "^5.0.3", 42 | "webpack-merge": "^6.0.1" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /examples/helia-webpack/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <%= htmlWebpackPlugin.options.title %> 9 | 10 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | -------------------------------------------------------------------------------- /examples/helia-webpack/src/app.css: -------------------------------------------------------------------------------- 1 | ::placeholder { 2 | color: rgb(0 0 0 / 30%); 3 | } 4 | 5 | form { 6 | margin: 1.25rem 0; 7 | } 8 | 9 | .window { 10 | display: flex; 11 | flex-direction: column; 12 | background: #222; 13 | color: #fff; 14 | height: 400px; 15 | } 16 | 17 | .window .header { 18 | flex-basis: 3rem; 19 | background: #c6c6c6; 20 | position: relative; 21 | } 22 | 23 | .window .header:after { 24 | content: ". . ."; 25 | position: absolute; 26 | left: 12px; 27 | right: 0; 28 | top: -3px; 29 | font-family: "Times New Roman", Times, serif; 30 | font-size: 96px; 31 | color: #fff; 32 | line-height: 0; 33 | letter-spacing: -12px; 34 | } 35 | 36 | .terminal { 37 | margin: 20px; 38 | font-family: monospace; 39 | font-size: 16px; 40 | overflow: auto; 41 | flex: 1; 42 | } 43 | -------------------------------------------------------------------------------- /examples/helia-webpack/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { createRoot } from 'react-dom/client' 3 | import './app.css' 4 | import App from './app.js' 5 | 6 | const container = document.getElementById('root') 7 | const root = createRoot(container) 8 | root.render() 9 | -------------------------------------------------------------------------------- /examples/helia-webpack/src/ipfs-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /examples/helia-webpack/test/index.spec.js: -------------------------------------------------------------------------------- 1 | import { setup, expect } from 'test-ipfs-example/browser' 2 | 3 | // Setup 4 | const test = setup() 5 | 6 | test.describe('bundle Helia with Webpack:', () => { 7 | // DOM 8 | const nameInput = '#file-name' 9 | const contentInput = '#file-content' 10 | const submitBtn = '#add-submit' 11 | const output = '#output' 12 | 13 | test.beforeEach(async ({ servers, page }) => { 14 | await page.goto(servers[0].url) 15 | }) 16 | 17 | test('should properly initialized a Helia node and add/get a file', async ({ page }) => { 18 | const fileName = 'test.txt' 19 | const stringToUse = 'Hello world!' 20 | 21 | await page.fill(nameInput, fileName) 22 | await page.fill(contentInput, stringToUse) 23 | await page.click(submitBtn) 24 | 25 | await page.waitForSelector(`${output}:has-text("/bafkreigaknpexyvxt76zgkitavbwx6ejgfheup5oybpm77f3pxzrvwpfdi")`) 26 | 27 | const outputContent = await page.textContent(output) 28 | 29 | expect(outputContent).toContain('bafkreigaknpexyvxt76zgkitavbwx6ejgfheup5oybpm77f3pxzrvwpfdi') 30 | expect(outputContent).toContain('https://ipfs.io/ipfs/bafkreigaknpexyvxt76zgkitavbwx6ejgfheup5oybpm77f3pxzrvwpfdi') 31 | expect(outputContent).toContain(fileName) 32 | expect(outputContent).toContain(stringToUse) 33 | }) 34 | }) 35 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "helia-examples", 3 | "version": "1.0.0", 4 | "description": "", 5 | "type": "module", 6 | "scripts": { 7 | "reset": "aegir run clean && aegir clean **/node_modules **/package-lock.json", 8 | "test": "aegir run test", 9 | "clean": "aegir run clean", 10 | "build": "aegir run build", 11 | "lint": "aegir exec aegir -- lint --files '**/*.{js,ts,jsx}' '!**/node_modules/**' '!**/dist/**'", 12 | "lint:fix": "aegir exec aegir -- lint --files '**/*.{js,ts,jsx}' '!**/node_modules/**' '!**/dist/**' --fix", 13 | "dep-check": "aegir run dep-check" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "git+https://github.com/ipfs-examples/helia-examples.git" 18 | }, 19 | "author": "", 20 | "license": "ISC", 21 | "bugs": { 22 | "url": "https://github.com/ipfs-examples/helia-examples/issues" 23 | }, 24 | "homepage": "https://github.com/ipfs-examples/helia-examples#readme", 25 | "devDependencies": { 26 | "aegir": "^45.1.4" 27 | }, 28 | "workspaces": [ 29 | "examples/*" 30 | ], 31 | "private": true, 32 | "eslintConfig": { 33 | "extends": [ 34 | "ipfs", 35 | "plugin:react/recommended" 36 | ], 37 | "parserOptions": { 38 | "sourceType": "module", 39 | "ecmaVersion": 2023 40 | }, 41 | "settings": { 42 | "react": { 43 | "version": "detect" 44 | } 45 | } 46 | }, 47 | "@parcel/resolver-default": { 48 | "packageExports": true 49 | } 50 | } 51 | --------------------------------------------------------------------------------