diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..d401a774 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,11 @@ +version: 2 +updates: +- package-ecosystem: npm + directory: "/" + schedule: + interval: daily + time: "10:00" + open-pull-requests-limit: 20 + commit-message: + prefix: "deps" + prefix-development: "deps(dev)" diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 00000000..f8966ba9 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,29 @@ +## Title + + + +## Description + + + +## Notes & open questions + + + +## Change checklist + +- [ ] I have performed a self-review of my own code +- [ ] I have made corresponding changes to the documentation if necessary (this includes comments as well) +- [ ] I have added tests that prove my fix is effective or that my feature works diff --git a/.github/workflows/js-test-and-release.yml b/.github/workflows/js-test-and-release.yml new file mode 100644 index 00000000..1d7ff79c --- /dev/null +++ b/.github/workflows/js-test-and-release.yml @@ -0,0 +1,27 @@ +name: test & maybe release + +on: + push: + branches: + - main + pull_request: + workflow_dispatch: + +permissions: + contents: write + id-token: write + packages: write + pull-requests: write + +concurrency: + group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.event_name == 'push' && github.sha || github.ref }} + cancel-in-progress: true + +jobs: + js-test-and-release: + uses: pl-strflt/uci/.github/workflows/js-test-and-release.yml@v0.0 + secrets: + DOCKER_TOKEN: ${{ secrets.DOCKER_TOKEN }} + DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + UCI_GITHUB_TOKEN: ${{ secrets.UCI_GITHUB_TOKEN }} diff --git a/.github/workflows/semantic-pull-request.yml b/.github/workflows/semantic-pull-request.yml new file mode 100644 index 00000000..bd00f090 --- /dev/null +++ b/.github/workflows/semantic-pull-request.yml @@ -0,0 +1,12 @@ +name: Semantic PR + +on: + pull_request_target: + types: + - opened + - edited + - synchronize + +jobs: + main: + uses: pl-strflt/.github/.github/workflows/reusable-semantic-pull-request.yml@v0.3 diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml new file mode 100644 index 00000000..16d65d72 --- /dev/null +++ b/.github/workflows/stale.yml @@ -0,0 +1,13 @@ +name: Close and mark stale issue + +on: + schedule: + - cron: '0 0 * * *' + +permissions: + issues: write + pull-requests: write + +jobs: + stale: + uses: pl-strflt/.github/.github/workflows/reusable-stale-issue.yml@v0.3 diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..7ad9e674 --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +node_modules +build +dist +.docs +.coverage +node_modules +package-lock.json +yarn.lock +.vscode diff --git a/CODEOWNERS b/CODEOWNERS new file mode 100644 index 00000000..5d8e69df --- /dev/null +++ b/CODEOWNERS @@ -0,0 +1 @@ +* @ipfs/helia-dev diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..20ce483c --- /dev/null +++ b/LICENSE @@ -0,0 +1,4 @@ +This project is dual licensed under MIT and Apache-2.0. + +MIT: https://www.opensource.org/licenses/mit +Apache-2.0: https://www.apache.org/licenses/license-2.0 diff --git a/LICENSE-APACHE b/LICENSE-APACHE new file mode 100644 index 00000000..14478a3b --- /dev/null +++ b/LICENSE-APACHE @@ -0,0 +1,5 @@ +Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. diff --git a/LICENSE-MIT b/LICENSE-MIT new file mode 100644 index 00000000..72dc60d8 --- /dev/null +++ b/LICENSE-MIT @@ -0,0 +1,19 @@ +The MIT License (MIT) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 00000000..662255c0 --- /dev/null +++ b/README.md @@ -0,0 +1,46 @@ +

+ + Helia logo + +

+ +# helia-verified-fetch + +[![ipfs.tech](https://img.shields.io/badge/project-IPFS-blue.svg?style=flat-square)](https://ipfs.tech) +[![Discuss](https://img.shields.io/discourse/https/discuss.ipfs.tech/posts.svg?style=flat-square)](https://discuss.ipfs.tech) +[![codecov](https://img.shields.io/codecov/c/github/ipfs/helia-verified-fetch.svg?style=flat-square)](https://codecov.io/gh/ipfs/helia-verified-fetch) +[![CI](https://img.shields.io/github/actions/workflow/status/ipfs/helia-verified-fetch/js-test-and-release.yml?branch=main\&style=flat-square)](https://github.com/ipfs/helia-verified-fetch/actions/workflows/js-test-and-release.yml?query=branch%3Amain) + +> A fetch-like API for obtaining verified & trustless IPFS content on the web + +## About + +This repo contains a server implementation of the IPFS [Delegated Routing V1 HTTP API](https://specs.ipfs.tech/routing/http-routing-v1/) along with a client that can be used to interact with any compliant server implementation. + +# Packages + +- [`/packages/interop`](./packages/interop) Interop tests for Helia +- [`/packages/verified-fetch`](./packages/verified-fetch) A fetch-like API for obtaining verified & trustless IPFS content on the web. + +# API Docs + +- + +# License + +Licensed under either of + +- Apache 2.0, ([LICENSE-APACHE](LICENSE-APACHE) / ) +- MIT ([LICENSE-MIT](LICENSE-MIT) / ) + +# Contribute + +Contributions welcome! Please check out [the issues](https://github.com/ipfs/helia-verified-fetch/issues). + +Also see our [contributing document](https://github.com/ipfs/community/blob/master/CONTRIBUTING_JS.md) for more information on how we work, and about contributing in general. + +Please be aware that all interactions related to this repo are subject to the IPFS [Code of Conduct](https://github.com/ipfs/community/blob/master/code-of-conduct.md). + +Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. + +[![](https://cdn.rawgit.com/jbenet/contribute-ipfs-gif/master/img/contribute.gif)](https://github.com/ipfs/community/blob/master/CONTRIBUTING.md) diff --git a/hello.txt b/hello.txt deleted file mode 100644 index e69de29b..00000000 diff --git a/package.json b/package.json new file mode 100644 index 00000000..9b06f99a --- /dev/null +++ b/package.json @@ -0,0 +1,132 @@ +{ + "name": "helia-verified-fetch", + "version": "1.0.0", + "description": "A fetch-like API for obtaining verified & trustless IPFS content on the web", + "license": "Apache-2.0 OR MIT", + "homepage": "https://github.com/ipfs/helia-verified-fetch#readme", + "repository": { + "type": "git", + "url": "git+https://github.com/ipfs/helia-verified-fetch.git" + }, + "bugs": { + "url": "https://github.com/ipfs/helia-verified-fetch/issues" + }, + "keywords": [ + "ipfs" + ], + "private": true, + "release": { + "branches": [ + "main" + ], + "plugins": [ + [ + "@semantic-release/commit-analyzer", + { + "preset": "conventionalcommits", + "releaseRules": [ + { + "breaking": true, + "release": "major" + }, + { + "revert": true, + "release": "patch" + }, + { + "type": "feat", + "release": "minor" + }, + { + "type": "fix", + "release": "patch" + }, + { + "type": "docs", + "release": "patch" + }, + { + "type": "test", + "release": "patch" + }, + { + "type": "deps", + "release": "patch" + }, + { + "scope": "no-release", + "release": false + } + ] + } + ], + [ + "@semantic-release/release-notes-generator", + { + "preset": "conventionalcommits", + "presetConfig": { + "types": [ + { + "type": "feat", + "section": "Features" + }, + { + "type": "fix", + "section": "Bug Fixes" + }, + { + "type": "chore", + "section": "Trivial Changes" + }, + { + "type": "docs", + "section": "Documentation" + }, + { + "type": "deps", + "section": "Dependencies" + }, + { + "type": "test", + "section": "Tests" + } + ] + } + } + ], + "@semantic-release/changelog", + "@semantic-release/npm", + "@semantic-release/github", + "@semantic-release/git" + ] + }, + "scripts": { + "reset": "aegir run clean && aegir clean **/node_modules **/package-lock.json", + "test": "aegir run test", + "test:node": "aegir run test:node", + "test:chrome": "aegir run test:chrome", + "test:chrome-webworker": "aegir run test:chrome-webworker", + "test:firefox": "aegir run test:firefox", + "test:firefox-webworker": "aegir run test:firefox-webworker", + "test:electron-main": "aegir run test:electron-main", + "test:electron-renderer": "aegir run test:electron-renderer", + "clean": "aegir run clean", + "generate": "aegir run generate", + "build": "aegir run build", + "lint": "aegir run lint", + "dep-check": "aegir run dep-check", + "doc-check": "aegir run doc-check", + "release": "run-s build docs:no-publish npm:release docs", + "npm:release": "aegir release", + "docs": "aegir docs", + "docs:no-publish": "aegir docs --publish false" + }, + "devDependencies": { + "aegir": "^42.2.0", + "npm-run-all": "^4.1.5" + }, + "type": "module", + "workspaces": [ + "packages/*" + ] +} diff --git a/packages/interop/.aegir.js b/packages/interop/.aegir.js new file mode 100644 index 00000000..226c2746 --- /dev/null +++ b/packages/interop/.aegir.js @@ -0,0 +1,46 @@ +import getPort from 'aegir/get-port' +import { createServer } from 'ipfsd-ctl' +import * as kuboRpcClient from 'kubo-rpc-client' + +/** @type {import('aegir').PartialOptions} */ +export default { + test: { + files: './dist/src/*.spec.js', + before: async (options) => { + if (options.runner !== 'node') { + const ipfsdPort = await getPort() + const ipfsdServer = await createServer({ + host: '127.0.0.1', + port: ipfsdPort + }, { + ipfsBin: (await import('kubo')).default.path(), + kuboRpcModule: kuboRpcClient, + ipfsOptions: { + config: { + Addresses: { + Swarm: [ + "/ip4/0.0.0.0/tcp/0", + "/ip4/0.0.0.0/tcp/0/ws" + ] + } + } + } + }).start() + + return { + env: { + IPFSD_SERVER: `http://127.0.0.1:${ipfsdPort}` + }, + ipfsdServer + } + } + + return {} + }, + after: async (options, beforeResult) => { + if (options.runner !== 'node') { + await beforeResult.ipfsdServer.stop() + } + } + } +} diff --git a/packages/interop/CHANGELOG.md b/packages/interop/CHANGELOG.md new file mode 100644 index 00000000..7856af91 --- /dev/null +++ b/packages/interop/CHANGELOG.md @@ -0,0 +1,215 @@ +# Changelog + +## [5.0.0](https://github.com/ipfs/helia/compare/interop-v4.0.0...interop-v5.0.0) (2024-01-31) + + +### ⚠ BREAKING CHANGES + +* to support paths in `@helia/ipns`, the return type of `ipns.resolve` is now `{ path: string, cid: CID }` instead of just `CID` + +### Features + +* support paths in @helia/ipns ([#410](https://github.com/ipfs/helia/issues/410)) ([ca8d5eb](https://github.com/ipfs/helia/commit/ca8d5ebdf587574c7fb84517b558226c3479caa9)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @helia/block-brokers bumped from ^2.0.0 to ^2.0.1 + * @helia/http bumped from ^1.0.0 to ^1.0.1 + * @helia/ipns bumped from ^5.0.0 to ^6.0.0 + * helia bumped from ^4.0.0 to ^4.0.1 + +## [4.0.0](https://github.com/ipfs/helia/compare/interop-v3.0.1...interop-v4.0.0) (2024-01-24) + + +### ⚠ BREAKING CHANGES + +* remove gossipsub from default libp2p services ([#401](https://github.com/ipfs/helia/issues/401)) +* `helia.routing` is the default routing used, the `libp2p` routing has been removed as it is redundant +* the `libp2p` property has been removed from the `Helia` interface in `@helia/interface` - it is still present on the return type of `createHelia` from the `helia` module + +### Features + +* add @helia/http to monorepo ([#372](https://github.com/ipfs/helia/issues/372)) ([76220cd](https://github.com/ipfs/helia/commit/76220cd5adf45af7fa61fd0a1321de4722b744d6)) +* export binary from @helia/interop ([#384](https://github.com/ipfs/helia/issues/384)) ([3477b27](https://github.com/ipfs/helia/commit/3477b2748d44a862e8afeae1a7a2668cdd8a7100)) +* use helia router for IPNS put/get ([#387](https://github.com/ipfs/helia/issues/387)) ([ce74026](https://github.com/ipfs/helia/commit/ce740268e83f50e6f144b74969a98d54005cd852)) + + +### Bug Fixes + +* include aegir config in interop and run from install dir ([#389](https://github.com/ipfs/helia/issues/389)) ([a2229bd](https://github.com/ipfs/helia/commit/a2229bd79d5c8b805604bb24bad222462a9ed8cc)) +* remove gossipsub from default libp2p services ([#401](https://github.com/ipfs/helia/issues/401)) ([99c94f4](https://github.com/ipfs/helia/commit/99c94f4b85c4ed826a6195207e3545cbbc87a6d1)) +* update ipns module to v9 and fix double verification of records ([#396](https://github.com/ipfs/helia/issues/396)) ([f2853f8](https://github.com/ipfs/helia/commit/f2853f8bd5bdcee8ab7a685355b0be47f29620e0)) + + +### Dependencies + +* bump kubo from 0.25.0 to 0.26.0 ([#400](https://github.com/ipfs/helia/issues/400)) ([a9c55f0](https://github.com/ipfs/helia/commit/a9c55f0e672e439cbcc6b938963ab150997c6e45)) +* The following workspace dependencies were updated + * dependencies + * @helia/block-brokers bumped from ^1.0.0 to ^2.0.0 + * @helia/car bumped from ^2.0.1 to ^3.0.0 + * @helia/dag-cbor bumped from ^2.0.1 to ^3.0.0 + * @helia/dag-json bumped from ^2.0.1 to ^3.0.0 + * @helia/http bumped from ^0.9.0 to ^1.0.0 + * @helia/interface bumped from ^3.0.1 to ^4.0.0 + * @helia/ipns bumped from ^4.0.0 to ^5.0.0 + * @helia/json bumped from ^2.0.1 to ^3.0.0 + * @helia/mfs bumped from ^2.0.1 to ^3.0.0 + * @helia/routers bumped from ^0.0.0 to ^1.0.0 + * @helia/strings bumped from ^2.0.1 to ^3.0.0 + * @helia/unixfs bumped from ^2.0.1 to ^3.0.0 + * helia bumped from ^3.0.1 to ^4.0.0 + +## [3.0.1](https://github.com/ipfs/helia/compare/interop-v3.0.0...interop-v3.0.1) (2024-01-16) + + +### Bug Fixes + +* update type import path ([#379](https://github.com/ipfs/helia/issues/379)) ([ece384a](https://github.com/ipfs/helia/commit/ece384aab5e1c95857aa4aa07b86656710d8ca35)) + +## [3.0.0](https://github.com/ipfs/helia/compare/interop-v2.0.0...interop-v3.0.0) (2024-01-09) + + +### ⚠ BREAKING CHANGES + +* uses multiformats v13 and helia v3 +* uses multiformats v13 and helia v3 +* uses multiformats v13 and helia v3 +* uses multiformats v13 and helia v3 +* uses multiformats v13 and helia v3 +* uses multiformats v13 and helia v3, renames `dht` routing to `libp2p` +* uses multiformats v13 +* uses multiformats v13 and helia v3 + +### Features + +* update helia to v3 and multiformats to v13 ([9f7dc0a](https://github.com/ipfs/helia/commit/9f7dc0a0581524531501fc062fefb6ba26d99c02)) +* update helia to v3 and multiformats to v13 ([#147](https://github.com/ipfs/helia/issues/147)) ([001247c](https://github.com/ipfs/helia/commit/001247c6fc38ff3d810736371de901e5e1099f26)) +* update helia to v3 and multiformats to v13 ([#167](https://github.com/ipfs/helia/issues/167)) ([a0381b9](https://github.com/ipfs/helia/commit/a0381b95051bbf3edfa4f53e0ae2d5f43c1e4382)) +* update helia to v3 and multiformats to v13 ([#45](https://github.com/ipfs/helia/issues/45)) ([f078447](https://github.com/ipfs/helia/commit/f078447b6eba4c3d404d62bb930757aa1c0efe74)) +* update helia to v3 and multiformats to v13 ([#45](https://github.com/ipfs/helia/issues/45)) ([3c7d9d4](https://github.com/ipfs/helia/commit/3c7d9d4a8e74e1a808c265fbc6ecbdc24f0f3da9)) +* update helia to v3 and multiformats to v13 ([#46](https://github.com/ipfs/helia/issues/46)) ([e3dc586](https://github.com/ipfs/helia/commit/e3dc5867ffc4de0dd3b05b56eb1b0ce98d50dcb1)) +* update helia to v3 and multiformats to v13 ([#52](https://github.com/ipfs/helia/issues/52)) ([6405c34](https://github.com/ipfs/helia/commit/6405c3487879614dc4dd7308b15c946d644e0488)) +* update helia to v3 and multiformats to v13 ([#87](https://github.com/ipfs/helia/issues/87)) ([ae7cbc9](https://github.com/ipfs/helia/commit/ae7cbc9a16a267cb0f6d7cecd381f919430afaea)) + + +### Bug Fixes + +* create @helia/block-brokers package ([#341](https://github.com/ipfs/helia/issues/341)) ([#342](https://github.com/ipfs/helia/issues/342)) ([2979147](https://github.com/ipfs/helia/commit/297914756fa06dc0c28890a2654d1159d16689c2)) + + +### Dependencies + +* The following workspace dependencies were updated + * devDependencies + * @helia/block-brokers bumped from ~0.0.0 to ~1.0.0 + * @helia/car bumped from ^2.0.0 to ^2.0.1 + * @helia/dag-cbor bumped from ^2.0.0 to ^2.0.1 + * @helia/dag-json bumped from ^2.0.0 to ^2.0.1 + * @helia/interface bumped from ^3.0.0 to ^3.0.1 + * @helia/json bumped from ^2.0.0 to ^2.0.1 + * @helia/mfs bumped from ^2.0.0 to ^2.0.1 + * @helia/strings bumped from ^2.0.0 to ^2.0.1 + * @helia/unixfs bumped from ^2.0.0 to ^2.0.1 + * helia bumped from ^3.0.0 to ^3.0.1 + +## [2.0.0](https://github.com/ipfs/helia/compare/interop-v1.1.0...interop-v2.0.0) (2024-01-07) + + +### ⚠ BREAKING CHANGES + +* `helia.pin.add` and `helia.pin.rm` now return `AsyncGenerator` +* The libp2p API has changed in a couple of places - please see the [upgrade guide](https://github.com/libp2p/js-libp2p/blob/main/doc/migrations/v0.46-v1.0.0.md) + +### deps + +* updates to libp2p v1 ([#320](https://github.com/ipfs/helia/issues/320)) ([635d7a2](https://github.com/ipfs/helia/commit/635d7a2938111ccc53f8defbd9b8f8f8ea3e8e6a)) + + +### Features + +* iterable pinning ([#231](https://github.com/ipfs/helia/issues/231)) ([c15c774](https://github.com/ipfs/helia/commit/c15c7749294d3d4aea5aef70544d088250336798)) + + +### Dependencies + +* The following workspace dependencies were updated + * devDependencies + * @helia/interface bumped from ^2.1.0 to ^3.0.0 + * helia bumped from ^2.1.0 to ^3.0.0 + +## [1.1.0](https://www.github.com/ipfs/helia/compare/interop-v1.0.3...interop-v1.1.0) (2023-11-06) + + +### Features + +* GatewayBlockBroker prioritizes & tries all gateways ([#281](https://www.github.com/ipfs/helia/issues/281)) ([9bad21b](https://www.github.com/ipfs/helia/commit/9bad21bd59fe6d1ba4a137db5a46bd2ead5238c3)) + + +### Dependencies + +* The following workspace dependencies were updated + * devDependencies + * @helia/interface bumped from ^2.0.0 to ^2.1.0 + * helia bumped from ^2.0.3 to ^2.1.0 + +### [1.0.3](https://www.github.com/ipfs/helia/compare/interop-v1.0.2...interop-v1.0.3) (2023-09-18) + + +### Dependencies + +* The following workspace dependencies were updated + * devDependencies + * helia bumped from ^2.0.2 to ^2.0.3 + +### [1.0.2](https://www.github.com/ipfs/helia/compare/interop-v1.0.1...interop-v1.0.2) (2023-09-14) + + +### Bug Fixes + +* **kubo:** ⬆️ Upgrading go-ipfs to kubo ([#251](https://www.github.com/ipfs/helia/issues/251)) ([963a7a2](https://www.github.com/ipfs/helia/commit/963a7a21774703a105c865a5b6db670f278eec73)) + + +### Dependencies + +* The following workspace dependencies were updated + * devDependencies + * helia bumped from ^2.0.1 to ^2.0.2 + +### [1.0.1](https://www.github.com/ipfs/helia/compare/interop-v1.0.0...interop-v1.0.1) (2023-08-16) + + +### Bug Fixes + +* enable dcutr by default ([#239](https://www.github.com/ipfs/helia/issues/239)) ([7431f09](https://www.github.com/ipfs/helia/commit/7431f09aef332dc142a5f7c2c59c9410e4529a92)) + + +### Dependencies + +* The following workspace dependencies were updated + * devDependencies + * helia bumped from ^2.0.0 to ^2.0.1 + +## [1.0.0](https://www.github.com/ipfs/helia/compare/interop-v0.0.0...interop-v1.0.0) (2023-08-16) + + +### ⚠ BREAKING CHANGES + +* libp2p has been updated to 0.46.x + +### Dependencies + +* **dev:** bump go-ipfs from 0.21.0 to 0.22.0 ([#228](https://www.github.com/ipfs/helia/issues/228)) ([2e8e447](https://www.github.com/ipfs/helia/commit/2e8e447f782745e517e935cd1bb3312db6384a5b)) +* update libp2p to 0.46.x ([#215](https://www.github.com/ipfs/helia/issues/215)) ([65b68f0](https://www.github.com/ipfs/helia/commit/65b68f071d04d2f6f0fcf35938b146706b1a3cd0)) + + + +### Dependencies + +* The following workspace dependencies were updated + * devDependencies + * @helia/interface bumped from ^1.0.0 to ^2.0.0 + * helia bumped from ^1.0.0 to ^2.0.0 diff --git a/packages/interop/LICENSE b/packages/interop/LICENSE new file mode 100644 index 00000000..20ce483c --- /dev/null +++ b/packages/interop/LICENSE @@ -0,0 +1,4 @@ +This project is dual licensed under MIT and Apache-2.0. + +MIT: https://www.opensource.org/licenses/mit +Apache-2.0: https://www.apache.org/licenses/license-2.0 diff --git a/packages/interop/LICENSE-APACHE b/packages/interop/LICENSE-APACHE new file mode 100644 index 00000000..14478a3b --- /dev/null +++ b/packages/interop/LICENSE-APACHE @@ -0,0 +1,5 @@ +Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. diff --git a/packages/interop/LICENSE-MIT b/packages/interop/LICENSE-MIT new file mode 100644 index 00000000..72dc60d8 --- /dev/null +++ b/packages/interop/LICENSE-MIT @@ -0,0 +1,19 @@ +The MIT License (MIT) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/packages/interop/README.md b/packages/interop/README.md new file mode 100644 index 00000000..28ac686a --- /dev/null +++ b/packages/interop/README.md @@ -0,0 +1,58 @@ +

+ + Helia logo + +

+ +# @helia/interop + +[![ipfs.tech](https://img.shields.io/badge/project-IPFS-blue.svg?style=flat-square)](https://ipfs.tech) +[![Discuss](https://img.shields.io/discourse/https/discuss.ipfs.tech/posts.svg?style=flat-square)](https://discuss.ipfs.tech) +[![codecov](https://img.shields.io/codecov/c/github/ipfs/helia-verified-fetch.svg?style=flat-square)](https://codecov.io/gh/ipfs/helia-verified-fetch) +[![CI](https://img.shields.io/github/actions/workflow/status/ipfs/helia-verified-fetch/js-test-and-release.yml?branch=main\&style=flat-square)](https://github.com/ipfs/helia-verified-fetch/actions/workflows/js-test-and-release.yml?query=branch%3Amain) + +> Interop tests for Helia + +# About + +Runs interop tests between Helia and Kubo. + +## Example - Testing a new Kubo release + +```console +$ npm i @helia/interop +$ KUBO_BINARY=/path/to/kubo helia-interop +``` + +# Install + +```console +$ npm i @helia/interop +``` + +## Browser ` +``` + +# License + +Licensed under either of + +- Apache 2.0, ([LICENSE-APACHE](LICENSE-APACHE) / ) +- MIT ([LICENSE-MIT](LICENSE-MIT) / ) + +# Contribute + +Contributions welcome! Please check out [the issues](https://github.com/ipfs/helia-verified-fetch/issues). + +Also see our [contributing document](https://github.com/ipfs/community/blob/master/CONTRIBUTING_JS.md) for more information on how we work, and about contributing in general. + +Please be aware that all interactions related to this repo are subject to the IPFS [Code of Conduct](https://github.com/ipfs/community/blob/master/code-of-conduct.md). + +Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. + +[![](https://cdn.rawgit.com/jbenet/contribute-ipfs-gif/master/img/contribute.gif)](https://github.com/ipfs/community/blob/master/CONTRIBUTING.md) diff --git a/packages/interop/package.json b/packages/interop/package.json new file mode 100644 index 00000000..b30c5792 --- /dev/null +++ b/packages/interop/package.json @@ -0,0 +1,73 @@ +{ + "name": "@helia/interop", + "version": "5.0.0", + "description": "Interop tests for Helia", + "license": "Apache-2.0 OR MIT", + "homepage": "https://github.com/ipfs/helia-verified-fetch/tree/main/packages/interop#readme", + "repository": { + "type": "git", + "url": "git+https://github.com/ipfs/helia-verified-fetch.git" + }, + "bugs": { + "url": "https://github.com/ipfs/helia-verified-fetch/issues" + }, + "publishConfig": { + "access": "public", + "provenance": true + }, + "keywords": [ + "IPFS" + ], + "bin": { + "helia-interop": "./dist/src/bin.js" + }, + "type": "module", + "types": "./dist/src/index.d.ts", + "files": [ + "src", + "dist", + "!dist/test", + "!**/*.tsbuildinfo" + ], + "exports": { + ".": { + "types": "./dist/src/index.d.ts", + "import": "./dist/src/index.js" + } + }, + "eslintConfig": { + "extends": "ipfs", + "parserOptions": { + "project": true, + "sourceType": "module" + } + }, + "scripts": { + "clean": "aegir clean", + "lint": "aegir lint", + "dep-check": "aegir dep-check", + "doc-check": "aegir doc-check", + "build": "aegir build", + "test": "aegir test", + "test:chrome": "aegir test -t browser --cov", + "test:chrome-webworker": "aegir test -t webworker", + "test:firefox": "aegir test -t browser -- --browser firefox", + "test:firefox-webworker": "aegir test -t webworker -- --browser firefox", + "test:node": "aegir test -t node --cov", + "test:electron-main": "aegir test -t electron-main" + }, + "dependencies": { + "@helia/verified-fetch": "~0.0.0", + "aegir": "^42.2.5", + "ipfsd-ctl": "^13.0.0", + "it-drain": "^3.0.5", + "kubo": "^0.26.0", + "kubo-rpc-client": "^3.0.3", + "magic-bytes.js": "^1.8.0", + "multiformats": "^13.1.0" + }, + "browser": { + "./dist/src/fixtures/create-kubo.js": "./dist/src/fixtures/create-kubo.browser.js", + "kubo": false + } +} diff --git a/packages/interop/src/fixtures/create-kubo.browser.ts b/packages/interop/src/fixtures/create-kubo.browser.ts new file mode 100644 index 00000000..8eacb7da --- /dev/null +++ b/packages/interop/src/fixtures/create-kubo.browser.ts @@ -0,0 +1,30 @@ +import { type Controller, createController } from 'ipfsd-ctl' +import * as kuboRpcClient from 'kubo-rpc-client' + +export async function createKuboNode (): Promise { + return createController({ + kuboRpcModule: kuboRpcClient, + test: true, + endpoint: process.env.IPFSD_SERVER, + ipfsOptions: { + config: { + Addresses: { + Swarm: [ + '/ip4/0.0.0.0/tcp/0', + '/ip4/0.0.0.0/tcp/0/ws' + ], + Gateway: '/ip4/127.0.0.1/tcp/8180' + }, + Gateway: { + NoFetch: true, + ExposeRoutingAPI: true, + HTTPHeaders: { + 'Access-Control-Allow-Origin': ['*'], + 'Access-Control-Allow-Methods': ['GET', 'POST', 'PUT', 'OPTIONS'] + } + } + } + }, + args: ['--enable-pubsub-experiment', '--enable-namesys-pubsub'] + }) +} diff --git a/packages/interop/src/fixtures/create-kubo.ts b/packages/interop/src/fixtures/create-kubo.ts new file mode 100644 index 00000000..a632b905 --- /dev/null +++ b/packages/interop/src/fixtures/create-kubo.ts @@ -0,0 +1,32 @@ +/* eslint-disable @typescript-eslint/ban-ts-comment,@typescript-eslint/prefer-ts-expect-error */ +import { createController, type Controller } from 'ipfsd-ctl' +import { path as kuboPath } from 'kubo' +import * as kuboRpcClient from 'kubo-rpc-client' + +export async function createKuboNode (): Promise { + return createController({ + kuboRpcModule: kuboRpcClient, + ipfsBin: kuboPath(), + test: true, + ipfsOptions: { + config: { + Addresses: { + Swarm: [ + '/ip4/0.0.0.0/tcp/4001', + '/ip4/0.0.0.0/tcp/4002/ws' + ], + Gateway: '/ip4/127.0.0.1/tcp/8180' + }, + Gateway: { + NoFetch: true, + ExposeRoutingAPI: true, + HTTPHeaders: { + 'Access-Control-Allow-Origin': ['*'], + 'Access-Control-Allow-Methods': ['GET', 'POST', 'PUT', 'OPTIONS'] + } + } + } + }, + args: ['--enable-pubsub-experiment', '--enable-namesys-pubsub'] + }) +} diff --git a/packages/interop/src/fixtures/data/QmQJ8fxavY54CUsxMSx9aE9Rdcmvhx8awJK2jzJp4iAqCr-tokens.uniswap.org-2024-01-18.car b/packages/interop/src/fixtures/data/QmQJ8fxavY54CUsxMSx9aE9Rdcmvhx8awJK2jzJp4iAqCr-tokens.uniswap.org-2024-01-18.car new file mode 100644 index 00000000..a1279473 Binary files /dev/null and b/packages/interop/src/fixtures/data/QmQJ8fxavY54CUsxMSx9aE9Rdcmvhx8awJK2jzJp4iAqCr-tokens.uniswap.org-2024-01-18.car differ diff --git a/packages/interop/src/fixtures/data/QmbQDovX7wRe9ek7u6QXe9zgCXkTzoUSsTFJEkrYV1HrVR-xkcd-Barrel-part-1.car b/packages/interop/src/fixtures/data/QmbQDovX7wRe9ek7u6QXe9zgCXkTzoUSsTFJEkrYV1HrVR-xkcd-Barrel-part-1.car new file mode 100644 index 00000000..362d3854 Binary files /dev/null and b/packages/interop/src/fixtures/data/QmbQDovX7wRe9ek7u6QXe9zgCXkTzoUSsTFJEkrYV1HrVR-xkcd-Barrel-part-1.car differ diff --git a/packages/interop/src/fixtures/data/QmbxpRxwKXxnJQjnPqm1kzDJSJ8YgkLxH23mcZURwPHjGv-helia-identify-website.car b/packages/interop/src/fixtures/data/QmbxpRxwKXxnJQjnPqm1kzDJSJ8YgkLxH23mcZURwPHjGv-helia-identify-website.car new file mode 100644 index 00000000..5391b246 Binary files /dev/null and b/packages/interop/src/fixtures/data/QmbxpRxwKXxnJQjnPqm1kzDJSJ8YgkLxH23mcZURwPHjGv-helia-identify-website.car differ diff --git a/packages/interop/src/fixtures/data/QmeiDMLtPUS3RT2xAcUwsNyZz169wPke2q7im9vZpVLSYw-fake-blog.libp2p.io.car b/packages/interop/src/fixtures/data/QmeiDMLtPUS3RT2xAcUwsNyZz169wPke2q7im9vZpVLSYw-fake-blog.libp2p.io.car new file mode 100644 index 00000000..de186e8f Binary files /dev/null and b/packages/interop/src/fixtures/data/QmeiDMLtPUS3RT2xAcUwsNyZz169wPke2q7im9vZpVLSYw-fake-blog.libp2p.io.car differ diff --git a/packages/interop/src/fixtures/data/bafybeidbclfqleg2uojchspzd4bob56dqetqjsj27gy2cq3klkkgxtpn4i-single-layer-hamt-with-multi-block-files.car b/packages/interop/src/fixtures/data/bafybeidbclfqleg2uojchspzd4bob56dqetqjsj27gy2cq3klkkgxtpn4i-single-layer-hamt-with-multi-block-files.car new file mode 100644 index 00000000..bc2ae755 Binary files /dev/null and b/packages/interop/src/fixtures/data/bafybeidbclfqleg2uojchspzd4bob56dqetqjsj27gy2cq3klkkgxtpn4i-single-layer-hamt-with-multi-block-files.car differ diff --git a/packages/interop/src/fixtures/load-fixture-data.ts b/packages/interop/src/fixtures/load-fixture-data.ts new file mode 100644 index 00000000..12c14c4b --- /dev/null +++ b/packages/interop/src/fixtures/load-fixture-data.ts @@ -0,0 +1,9 @@ +import loadFixture from 'aegir/fixtures' +import drain from 'it-drain' +import type { Controller } from 'ipfsd-ctl' + +export async function loadFixtureDataCar (controller: Controller, path: string): Promise { + const fixtureData = `src/fixtures/data/${path}` + const buf = loadFixture(fixtureData) + await drain(controller.api.dag.import([buf])) +} diff --git a/packages/interop/src/index.ts b/packages/interop/src/index.ts new file mode 100644 index 00000000..6d6d1a43 --- /dev/null +++ b/packages/interop/src/index.ts @@ -0,0 +1,14 @@ +/** + * @packageDocumentation + * + * Runs interop tests between Helia and Kubo. + * + * @example Testing a new Kubo release + * + * ```console + * $ npm i @helia/interop + * $ KUBO_BINARY=/path/to/kubo helia-interop + * ``` + */ + +export {} diff --git a/packages/interop/src/json.spec.ts b/packages/interop/src/json.spec.ts new file mode 100644 index 00000000..4536faab --- /dev/null +++ b/packages/interop/src/json.spec.ts @@ -0,0 +1,47 @@ +/* eslint-env mocha */ +import { createVerifiedFetch } from '@helia/verified-fetch' +import { expect } from 'aegir/chai' +import { CID } from 'multiformats/cid' +import { createKuboNode } from './fixtures/create-kubo.js' +import { loadFixtureDataCar } from './fixtures/load-fixture-data.js' +import type { Controller } from 'ipfsd-ctl' + +describe('@helia/verified-fetch - json', () => { + describe('unixfs - multiblock', () => { + let controller: Controller<'go'> + let verifiedFetch: Awaited> + + before(async () => { + controller = await createKuboNode() + await controller.start() + // As of 2024-01-18, https://cloudflare-ipfs.com/ipns/tokens.uniswap.org resolves to: + // root: QmQJ8fxavY54CUsxMSx9aE9Rdcmvhx8awJK2jzJp4iAqCr + // child1: QmNik5N4ryNwzzXYq5hCYKGcRjAf9QtigxtiJh9o8aXXbG // partial JSON + // child2: QmWNBJX6fZyNTLWNYBHxAHpBctCP43R2zeqV2G8uavqFZn // partial JSON + await loadFixtureDataCar(controller, 'QmQJ8fxavY54CUsxMSx9aE9Rdcmvhx8awJK2jzJp4iAqCr-tokens.uniswap.org-2024-01-18.car') + verifiedFetch = await createVerifiedFetch({ + gateways: [`http://${controller.api.gatewayHost}:${controller.api.gatewayPort}`], + routers: [`http://${controller.api.gatewayHost}:${controller.api.gatewayPort}`] + }) + }) + + after(async () => { + await controller.stop() + await verifiedFetch.stop() + }) + + it('handles UnixFS-chunked JSON file', async () => { + const resp = await verifiedFetch(CID.parse('QmQJ8fxavY54CUsxMSx9aE9Rdcmvhx8awJK2jzJp4iAqCr')) + expect(resp).to.be.ok() + const jsonObj = await resp.json() + expect(jsonObj).to.be.ok() + expect(jsonObj).to.have.property('name').equal('Uniswap Labs Default') + expect(jsonObj).to.have.property('timestamp').equal('2023-12-13T18:25:25.830Z') + expect(jsonObj).to.have.property('version').to.deep.equal({ major: 11, minor: 11, patch: 0 }) + expect(jsonObj).to.have.property('tags') + expect(jsonObj).to.have.property('logoURI').equal('ipfs://QmNa8mQkrNKp1WEEeGjFezDmDeodkWRevGFN8JCV7b4Xir') + expect(jsonObj).to.have.property('keywords').to.deep.equal(['uniswap', 'default']) + expect(jsonObj.tokens).to.be.an('array').of.length(767) + }) + }) +}) diff --git a/packages/interop/src/unixfs-dir.spec.ts b/packages/interop/src/unixfs-dir.spec.ts new file mode 100644 index 00000000..2f2db818 --- /dev/null +++ b/packages/interop/src/unixfs-dir.spec.ts @@ -0,0 +1,94 @@ +/* eslint-env mocha */ +import { createVerifiedFetch } from '@helia/verified-fetch' +import { expect } from 'aegir/chai' +import { filetypemime } from 'magic-bytes.js' +import { createKuboNode } from './fixtures/create-kubo.js' +import { loadFixtureDataCar } from './fixtures/load-fixture-data.js' +import type { VerifiedFetch } from '@helia/verified-fetch' +import type { Controller } from 'ipfsd-ctl' + +describe('@helia/verified-fetch - unixfs directory', () => { + let controller: Controller + let verifiedFetch: VerifiedFetch + + before(async () => { + controller = await createKuboNode() + await controller.start() + + verifiedFetch = await createVerifiedFetch({ + gateways: [`http://${controller.api.gatewayHost}:${controller.api.gatewayPort}`], + routers: [`http://${controller.api.gatewayHost}:${controller.api.gatewayPort}`] + }) + }) + + after(async () => { + await controller.stop() + await verifiedFetch.stop() + }) + + describe('XKCD Barrel Part 1', () => { + before(async () => { + // This is the content of https://explore.ipld.io/#/explore/QmdmQXB2mzChmMeKY47C43LxUdg1NDJ5MWcKMKxDu7RgQm/1%20-%20Barrel%20-%20Part%201 + await loadFixtureDataCar(controller, 'QmbQDovX7wRe9ek7u6QXe9zgCXkTzoUSsTFJEkrYV1HrVR-xkcd-Barrel-part-1.car') + }) + + it('fails to load when passed the root', async () => { + // The spec says we should generate HTML with directory listings, but we don't do that yet, so expect a failure + const resp = await verifiedFetch('ipfs://QmbQDovX7wRe9ek7u6QXe9zgCXkTzoUSsTFJEkrYV1HrVR') + expect(resp).to.be.ok() + expect(resp.status).to.equal(501) // TODO: we should do a directory listing instead + }) + + it('can return a string for unixfs pathed data', async () => { + const resp = await verifiedFetch('ipfs://QmbQDovX7wRe9ek7u6QXe9zgCXkTzoUSsTFJEkrYV1HrVR/1 - Barrel - Part 1 - alt.txt') + expect(resp).to.be.ok() + const text = await resp.text() + expect(text).to.equal('Don\'t we all.') + }) + + it('can return an image for unixfs pathed data', async () => { + const resp = await verifiedFetch('ipfs://QmbQDovX7wRe9ek7u6QXe9zgCXkTzoUSsTFJEkrYV1HrVR/1 - Barrel - Part 1.png') + expect(resp).to.be.ok() + const imgData = await resp.blob() + expect(imgData).to.be.ok() + expect(imgData.size).to.equal(24848) + }) + }) + + describe('content type parser', () => { + before(async () => { + await verifiedFetch.stop() + verifiedFetch = await createVerifiedFetch({ + gateways: [`http://${controller.api.gatewayHost}:${controller.api.gatewayPort}`], + routers: [`http://${controller.api.gatewayHost}:${controller.api.gatewayPort}`] + }, { + contentTypeParser: (bytes) => { + return filetypemime(bytes)?.[0] + } + }) + }) + + it('can return an image content-type for unixfs pathed data', async () => { + const resp = await verifiedFetch('ipfs://QmbQDovX7wRe9ek7u6QXe9zgCXkTzoUSsTFJEkrYV1HrVR/1 - Barrel - Part 1.png') + // tediously this is actually a jpeg file with a .png extension + expect(resp.headers.get('content-type')).to.equal('image/jpeg') + }) + }) + + describe('HAMT-sharded directory', () => { + before(async () => { + // from https://github.com/ipfs/gateway-conformance/blob/193833b91f2e9b17daf45c84afaeeae61d9d7c7e/fixtures/trustless_gateway_car/single-layer-hamt-with-multi-block-files.car + await loadFixtureDataCar(controller, 'bafybeidbclfqleg2uojchspzd4bob56dqetqjsj27gy2cq3klkkgxtpn4i-single-layer-hamt-with-multi-block-files.car') + }) + + it('loads path /ipfs/bafybeidbclfqleg2uojchspzd4bob56dqetqjsj27gy2cq3klkkgxtpn4i/685.txt', async () => { + const resp = await verifiedFetch('ipfs://bafybeidbclfqleg2uojchspzd4bob56dqetqjsj27gy2cq3klkkgxtpn4i/685.txt') + expect(resp).to.be.ok() + const text = await resp.text() + // npx kubo@0.25.0 cat '/ipfs/bafybeidbclfqleg2uojchspzd4bob56dqetqjsj27gy2cq3klkkgxtpn4i/685.txt' + expect(text).to.equal(`Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc non imperdiet nunc. Proin ac quam ut nibh eleifend aliquet. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Sed ligula dolor, imperdiet sagittis arcu et, semper tincidunt urna. Donec et tempor augue, quis sollicitudin metus. Curabitur semper ullamcorper aliquet. Mauris hendrerit sodales lectus eget fermentum. Proin sollicitudin vestibulum commodo. Vivamus nec lectus eu augue aliquet dignissim nec condimentum justo. In hac habitasse platea dictumst. Mauris vel sem neque. + +Vivamus finibus, enim at lacinia semper, arcu erat gravida lacus, sit amet gravida magna orci sit amet est. Sed non leo lacus. Nullam viverra ipsum a tincidunt dapibus. Nulla pulvinar ligula sit amet ante ultrices tempus. Proin purus urna, semper sed lobortis quis, gravida vitae ipsum. Aliquam mi urna, pulvinar eu bibendum quis, convallis ac dolor. In gravida justo sed risus ullamcorper, vitae luctus massa hendrerit. Pellentesque habitant amet.`) + }) + }) +}) diff --git a/packages/interop/src/websites.spec.ts b/packages/interop/src/websites.spec.ts new file mode 100644 index 00000000..287e6638 --- /dev/null +++ b/packages/interop/src/websites.spec.ts @@ -0,0 +1,85 @@ +/* eslint-env mocha */ +import { createVerifiedFetch } from '@helia/verified-fetch' +import { expect } from 'aegir/chai' +import { createKuboNode } from './fixtures/create-kubo.js' +import { loadFixtureDataCar } from './fixtures/load-fixture-data.js' +import type { Controller } from 'ipfsd-ctl' + +describe('@helia/verified-fetch - websites', () => { + describe('helia-identify.on.fleek.co', () => { + let controller: Controller<'go'> + let verifiedFetch: Awaited> + + before(async () => { + controller = await createKuboNode() + await controller.start() + // 2024-01-22 CID for _dnslink.helia-identify.on.fleek.co + await loadFixtureDataCar(controller, 'QmbxpRxwKXxnJQjnPqm1kzDJSJ8YgkLxH23mcZURwPHjGv-helia-identify-website.car') + verifiedFetch = await createVerifiedFetch({ + gateways: [`http://${controller.api.gatewayHost}:${controller.api.gatewayPort}`], + routers: [`http://${controller.api.gatewayHost}:${controller.api.gatewayPort}`] + }) + }) + + after(async () => { + await controller.stop() + await verifiedFetch.stop() + }) + + it('loads index.html when passed helia-identify.on.fleek.co root CID', async () => { + const resp = await verifiedFetch('ipfs://QmbxpRxwKXxnJQjnPqm1kzDJSJ8YgkLxH23mcZURwPHjGv') + expect(resp).to.be.ok() + const html = await resp.text() + expect(html).to.be.ok() + expect(html).to.include('Run Identify on a remote node with Helia') + }) + + it('loads helia-identify.on.fleek.co index.html directly ', async () => { + const resp = await verifiedFetch('ipfs://QmbxpRxwKXxnJQjnPqm1kzDJSJ8YgkLxH23mcZURwPHjGv/index.html') + expect(resp).to.be.ok() + const html = await resp.text() + expect(html).to.be.ok() + expect(html).to.include('Run Identify on a remote node with Helia') + }) + }) + + /** + * + * Created on 2024-01-23. /ipns/blog.libp2p.io/index.html resolved to QmVZNGy6SPvUbvQCXXaGDdp8kvfJm9MMozjU12dyzH6hKf + * + * ```shell + * mkdir fake-blog.libp2p.io + * npx kubo@0.25.0 cat '/ipfs/QmVZNGy6SPvUbvQCXXaGDdp8kvfJm9MMozjU12dyzH6hKf' > fake-blog.libp2p.io/index.html + * npx kubo@0.25.0 add -r fake-blog.libp2p.io + * npx kubo@0.25.0 dag export QmeiDMLtPUS3RT2xAcUwsNyZz169wPke2q7im9vZpVLSYw > QmeiDMLtPUS3RT2xAcUwsNyZz169wPke2q7im9vZpVLSYw-fake-blog.libp2p.io.car + * ``` + */ + describe('fake blog.libp2p.io', () => { + let controller: Controller<'go'> + let verifiedFetch: Awaited> + + before(async () => { + controller = await createKuboNode() + await controller.start() + await loadFixtureDataCar(controller, 'QmeiDMLtPUS3RT2xAcUwsNyZz169wPke2q7im9vZpVLSYw-fake-blog.libp2p.io.car') + verifiedFetch = await createVerifiedFetch({ + gateways: [`http://${controller.api.gatewayHost}:${controller.api.gatewayPort}`], + routers: [`http://${controller.api.gatewayHost}:${controller.api.gatewayPort}`] + }) + }) + + after(async () => { + await controller.stop() + await verifiedFetch.stop() + }) + + it('loads index.html when passed fake-blog.libp2p.io root CID', async () => { + const resp = await verifiedFetch('ipfs://QmeiDMLtPUS3RT2xAcUwsNyZz169wPke2q7im9vZpVLSYw') + expect(resp).to.be.ok() + const html = await resp.text() + expect(html).to.be.ok() + expect(html).to.include('Home | libp2p Blog & News') + expect(html).to.include('') + }) + }) +}) diff --git a/packages/interop/tsconfig.json b/packages/interop/tsconfig.json new file mode 100644 index 00000000..8fdfec9a --- /dev/null +++ b/packages/interop/tsconfig.json @@ -0,0 +1,15 @@ +{ + "extends": "aegir/src/config/tsconfig.aegir.json", + "compilerOptions": { + "outDir": "dist" + }, + "include": [ + "src", + "test" + ], + "references": [ + { + "path": "../verified-fetch" + } + ] +} diff --git a/packages/verified-fetch/README.md b/packages/verified-fetch/README.md index 966f04de..eb99d3e2 100644 --- a/packages/verified-fetch/README.md +++ b/packages/verified-fetch/README.md @@ -4,10 +4,12 @@

+# @helia/verified-fetch + [![ipfs.tech](https://img.shields.io/badge/project-IPFS-blue.svg?style=flat-square)](https://ipfs.tech) [![Discuss](https://img.shields.io/discourse/https/discuss.ipfs.tech/posts.svg?style=flat-square)](https://discuss.ipfs.tech) -[![codecov](https://img.shields.io/codecov/c/github/ipfs/helia.svg?style=flat-square)](https://codecov.io/gh/ipfs/helia) -[![CI](https://img.shields.io/github/actions/workflow/status/ipfs/helia/main.yml?branch=main\&style=flat-square)](https://github.com/ipfs/helia/actions/workflows/main.yml?query=branch%3Amain) +[![codecov](https://img.shields.io/codecov/c/github/ipfs/helia-verified-fetch.svg?style=flat-square)](https://codecov.io/gh/ipfs/helia-verified-fetch) +[![CI](https://img.shields.io/github/actions/workflow/status/ipfs/helia-verified-fetch/js-test-and-release.yml?branch=main\&style=flat-square)](https://github.com/ipfs/helia-verified-fetch/actions/workflows/js-test-and-release.yml?query=branch%3Amain) > A fetch-like API for obtaining verified & trustless IPFS content on the web. @@ -83,7 +85,7 @@ document.body.appendChild(image) import { verifiedFetch } from '@helia/verified-fetch' const response = await verifiedFetch('ipns://mydomain.com/path/to/very-long-file.log') -const bigFileStreamReader = await response.body.getReader() +const bigFileStreamReader = await response.body?.getReader() ``` ## Configuration @@ -100,8 +102,8 @@ It's possible to override these by passing `gateways` and `routers` keys to the import { createVerifiedFetch } from '@helia/verified-fetch' const fetch = await createVerifiedFetch({ - gateways: ['https://trustless-gateway.link'], - routers: ['http://delegated-ipfs.dev'] + gateways: ['https://trustless-gateway.link'], + routers: ['http://delegated-ipfs.dev'] }) const resp = await fetch('ipfs://bafy...') @@ -125,13 +127,13 @@ import { createVerifiedFetch } from '@helia/verified-fetch' const fetch = await createVerifiedFetch( await createHeliaHTTP({ - blockBrokers: [ - trustlessGateway({ - gateways: ['https://mygateway.example.net', 'https://trustless-gateway.link'] - }) - ], - routers: ['http://delegated-ipfs.dev'].map((routerUrl) => delegatedHTTPRouting(routerUrl)) - }) + blockBrokers: [ + trustlessGateway({ + gateways: ['https://mygateway.example.net', 'https://trustless-gateway.link'] + }) + ], + routers: ['http://delegated-ipfs.dev'].map((routerUrl) => delegatedHTTPRouting(routerUrl)) + }) ) const resp = await fetch('ipfs://bafy...') @@ -154,14 +156,36 @@ import { createVerifiedFetch } from '@helia/verified-fetch' import { fileTypeFromBuffer } from '@sgtpooki/file-type' const fetch = await createVerifiedFetch({ - gateways: ['https://trustless-gateway.link'], - routers: ['http://delegated-ipfs.dev'] + gateways: ['https://trustless-gateway.link'], + routers: ['http://delegated-ipfs.dev'] }, { - contentTypeParser: async (bytes) => { - // call to some magic-byte recognition library like magic-bytes, file-type, or your own custom byte recognition - const result = await fileTypeFromBuffer(bytes) - return result?.mime - } + contentTypeParser: async (bytes) => { + // call to some magic-byte recognition library like magic-bytes, file-type, or your own custom byte recognition + const result = await fileTypeFromBuffer(bytes) + return result?.mime + } +}) +``` + +### Custom DNS resolvers + +If you don't want to leak DNS queries to the default resolvers, you can provide your own list of DNS resolvers to `createVerifiedFetch`. + +Note that you do not need to provide both a DNS-over-HTTPS and a DNS-over-JSON resolver, and you should prefer `dnsJsonOverHttps` resolvers for usage in the browser for a smaller bundle size. See for more information. + +## Example - Customizing DNS resolvers + +```typescript +import { createVerifiedFetch } from '@helia/verified-fetch' +import { dnsJsonOverHttps, dnsOverHttps } from '@helia/ipns/dns-resolvers' + +const fetch = await createVerifiedFetch({ + gateways: ['https://trustless-gateway.link'], + routers: ['http://delegated-ipfs.dev'], + dnsResolvers: [ + dnsJsonOverHttps('https://my-dns-resolver.example.com/dns-json'), + dnsOverHttps('https://my-dns-resolver.example.com/dns-query') + ] }) ``` @@ -203,6 +227,10 @@ import { verifiedFetch } from '@helia/verified-fetch' const res = await verifiedFetch('ipfs://Qmfoo') const reader = res.body?.getReader() +if (reader == null) { + throw new Error('Could not create reader from response body') +} + while (true) { const next = await reader.read() @@ -306,7 +334,7 @@ import * as dagJson from '@ipld/dag-json' const res = await verifiedFetch('ipfs://bafyDAGJSON') // or: -const obj = dagJson.decode(await res.arrayBuffer()) +const obj = dagJson.decode(await res.arrayBuffer()) console.info(obj.cid) // CID(baeaaac3imvwgy3zao5xxe3de) console.info(obj.buf) // Uint8Array(5) [ 0, 1, 2, 3, 4 ] ``` @@ -509,7 +537,7 @@ Loading this module through a script tag will make it's exports available as `He # API Docs -- +- # License @@ -520,7 +548,7 @@ Licensed under either of # Contribute -Contributions welcome! Please check out [the issues](https://github.com/ipfs/helia/issues). +Contributions welcome! Please check out [the issues](https://github.com/ipfs/helia-verified-fetch/issues). Also see our [contributing document](https://github.com/ipfs/community/blob/master/CONTRIBUTING_JS.md) for more information on how we work, and about contributing in general. diff --git a/packages/verified-fetch/package.json b/packages/verified-fetch/package.json index e71d86cc..1f1061d3 100644 --- a/packages/verified-fetch/package.json +++ b/packages/verified-fetch/package.json @@ -3,13 +3,13 @@ "version": "0.0.0", "description": "A fetch-like API for obtaining verified & trustless IPFS content on the web.", "license": "Apache-2.0 OR MIT", - "homepage": "https://github.com/ipfs/helia/tree/main/packages/verified-fetch#readme", + "homepage": "https://github.com/ipfs/helia-verified-fetch/tree/main/packages/verified-fetch#readme", "repository": { "type": "git", - "url": "git+https://github.com/ipfs/helia.git" + "url": "git+https://github.com/ipfs/helia-verified-fetch.git" }, "bugs": { - "url": "https://github.com/ipfs/helia/issues" + "url": "https://github.com/ipfs/helia-verified-fetch/issues" }, "publishConfig": { "access": "public", @@ -41,95 +41,11 @@ "sourceType": "module" } }, - "release": { - "branches": [ - "main" - ], - "plugins": [ - [ - "@semantic-release/commit-analyzer", - { - "preset": "conventionalcommits", - "releaseRules": [ - { - "breaking": true, - "release": "major" - }, - { - "revert": true, - "release": "patch" - }, - { - "type": "feat", - "release": "minor" - }, - { - "type": "fix", - "release": "patch" - }, - { - "type": "docs", - "release": "patch" - }, - { - "type": "test", - "release": "patch" - }, - { - "type": "deps", - "release": "patch" - }, - { - "scope": "no-release", - "release": false - } - ] - } - ], - [ - "@semantic-release/release-notes-generator", - { - "preset": "conventionalcommits", - "presetConfig": { - "types": [ - { - "type": "feat", - "section": "Features" - }, - { - "type": "fix", - "section": "Bug Fixes" - }, - { - "type": "chore", - "section": "Trivial Changes" - }, - { - "type": "docs", - "section": "Documentation" - }, - { - "type": "deps", - "section": "Dependencies" - }, - { - "type": "test", - "section": "Tests" - } - ] - } - } - ], - "@semantic-release/changelog", - "@semantic-release/npm", - "@semantic-release/github", - "@semantic-release/git" - ] - }, "scripts": { "clean": "aegir clean", "lint": "aegir lint", "dep-check": "aegir dep-check", + "doc-check": "aegir doc-check", "build": "aegir build", "test": "aegir test", "test:chrome": "aegir test -t browser --cov", @@ -141,19 +57,19 @@ "release": "aegir release" }, "dependencies": { - "@helia/block-brokers": "^2.0.1", - "@helia/car": "^3.0.0", - "@helia/http": "^1.0.1", - "@helia/interface": "^4.0.0", - "@helia/ipns": "^6.0.0", - "@helia/routers": "^1.0.0", - "@helia/unixfs": "^3.0.0", + "@helia/block-brokers": "^2.0.2", + "@helia/car": "^3.1.0", + "@helia/http": "^1.0.2", + "@helia/interface": "^4.0.1", + "@helia/ipns": "^6.0.1", + "@helia/routers": "^1.0.1", + "@helia/unixfs": "^3.0.1", "@ipld/dag-cbor": "^9.2.0", "@ipld/dag-json": "^10.2.0", "@ipld/dag-pb": "^4.1.0", - "@libp2p/interface": "^1.1.2", - "@libp2p/kad-dht": "^12.0.7", - "@libp2p/peer-id": "^4.0.5", + "@libp2p/interface": "^1.1.4", + "@libp2p/kad-dht": "^12.0.8", + "@libp2p/peer-id": "^4.0.7", "cborg": "^4.0.9", "hashlru": "^2.3.0", "interface-blockstore": "^5.2.10", @@ -161,28 +77,28 @@ "ipfs-unixfs-exporter": "^13.5.0", "it-map": "^3.0.5", "it-pipe": "^3.0.1", - "it-tar": "^6.0.4", + "it-tar": "^6.0.5", "it-to-browser-readablestream": "^2.0.6", "multiformats": "^13.1.0", "progress-events": "^1.0.0", "uint8arrays": "^5.0.2" }, "devDependencies": { - "@helia/car": "^3.0.0", - "@helia/dag-cbor": "^3.0.0", - "@helia/dag-json": "^3.0.0", - "@helia/json": "^3.0.0", - "@helia/utils": "^0.0.1", - "@ipld/car": "^5.2.6", - "@libp2p/logger": "^4.0.5", - "@libp2p/peer-id-factory": "^4.0.5", + "@helia/car": "^3.1.0", + "@helia/dag-cbor": "^3.0.1", + "@helia/dag-json": "^3.0.1", + "@helia/json": "^3.0.1", + "@helia/utils": "^0.0.2", + "@ipld/car": "^5.3.0", + "@libp2p/logger": "^4.0.7", + "@libp2p/peer-id-factory": "^4.0.7", "@sgtpooki/file-type": "^1.0.1", "@types/sinon": "^17.0.3", - "aegir": "^42.2.2", + "aegir": "^42.2.5", "blockstore-core": "^4.4.0", "browser-readablestream-to-it": "^2.0.5", - "datastore-core": "^9.2.8", - "helia": "^4.0.1", + "datastore-core": "^9.2.9", + "helia": "^4.0.2", "ipfs-unixfs-importer": "^15.2.4", "ipns": "^9.0.0", "it-all": "^3.0.4", diff --git a/packages/verified-fetch/src/index.ts b/packages/verified-fetch/src/index.ts index ef54ce29..564d5cf0 100644 --- a/packages/verified-fetch/src/index.ts +++ b/packages/verified-fetch/src/index.ts @@ -56,7 +56,7 @@ * import { verifiedFetch } from '@helia/verified-fetch' * * const response = await verifiedFetch('ipns://mydomain.com/path/to/very-long-file.log') - * const bigFileStreamReader = await response.body.getReader() + * const bigFileStreamReader = await response.body?.getReader() * ``` * * ## Configuration @@ -73,8 +73,8 @@ * import { createVerifiedFetch } from '@helia/verified-fetch' * * const fetch = await createVerifiedFetch({ - * gateways: ['https://trustless-gateway.link'], - * routers: ['http://delegated-ipfs.dev'] + * gateways: ['https://trustless-gateway.link'], + * routers: ['http://delegated-ipfs.dev'] * }) * * const resp = await fetch('ipfs://bafy...') @@ -98,13 +98,13 @@ * * const fetch = await createVerifiedFetch( * await createHeliaHTTP({ - * blockBrokers: [ - * trustlessGateway({ - * gateways: ['https://mygateway.example.net', 'https://trustless-gateway.link'] - * }) - * ], - * routers: ['http://delegated-ipfs.dev'].map((routerUrl) => delegatedHTTPRouting(routerUrl)) - * }) + * blockBrokers: [ + * trustlessGateway({ + * gateways: ['https://mygateway.example.net', 'https://trustless-gateway.link'] + * }) + * ], + * routers: ['http://delegated-ipfs.dev'].map((routerUrl) => delegatedHTTPRouting(routerUrl)) + * }) * ) * * const resp = await fetch('ipfs://bafy...') @@ -127,14 +127,14 @@ * import { fileTypeFromBuffer } from '@sgtpooki/file-type' * * const fetch = await createVerifiedFetch({ - * gateways: ['https://trustless-gateway.link'], - * routers: ['http://delegated-ipfs.dev'] + * gateways: ['https://trustless-gateway.link'], + * routers: ['http://delegated-ipfs.dev'] * }, { - * contentTypeParser: async (bytes) => { - * // call to some magic-byte recognition library like magic-bytes, file-type, or your own custom byte recognition - * const result = await fileTypeFromBuffer(bytes) - * return result?.mime - * } + * contentTypeParser: async (bytes) => { + * // call to some magic-byte recognition library like magic-bytes, file-type, or your own custom byte recognition + * const result = await fileTypeFromBuffer(bytes) + * return result?.mime + * } * }) * ``` * @@ -198,6 +198,10 @@ * const res = await verifiedFetch('ipfs://Qmfoo') * const reader = res.body?.getReader() * + * if (reader == null) { + * throw new Error('Could not create reader from response body') + * } + * * while (true) { * const next = await reader.read() * @@ -301,7 +305,7 @@ * const res = await verifiedFetch('ipfs://bafyDAGJSON') * * // or: - * const obj = dagJson.decode(await res.arrayBuffer()) + * const obj = dagJson.decode(await res.arrayBuffer()) * console.info(obj.cid) // CID(baeaaac3imvwgy3zao5xxe3de) * console.info(obj.buf) // Uint8Array(5) [ 0, 1, 2, 3, 4 ] * ``` diff --git a/packages/verified-fetch/tsconfig.json b/packages/verified-fetch/tsconfig.json index 6c7cd39b..13a35996 100644 --- a/packages/verified-fetch/tsconfig.json +++ b/packages/verified-fetch/tsconfig.json @@ -6,37 +6,5 @@ "include": [ "src", "test" - ], - "references": [ - { - "path": "../block-brokers" - }, - { - "path": "../dag-cbor" - }, - { - "path": "../dag-json" - }, - { - "path": "../helia" - }, - { - "path": "../http" - }, - { - "path": "../interface" - }, - { - "path": "../ipns" - }, - { - "path": "../json" - }, - { - "path": "../routers" - }, - { - "path": "../unixfs" - } ] } diff --git a/typedoc.json b/typedoc.json new file mode 100644 index 00000000..8a5f92ae --- /dev/null +++ b/typedoc.json @@ -0,0 +1,7 @@ +{ + "$schema": "https://typedoc.org/schema.json", + "name": "Helia Routing V1 HTTP API", + "exclude": [ + "packages/interop" + ] +}