diff --git a/README.md b/README.md
index 29532713..aa002d9c 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,7 @@
-# helia-verified-fetch
+# @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)
@@ -13,18 +13,531 @@
> A fetch-like API for obtaining verified & trustless IPFS content on the web
-## About
+# 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.
+
+
+`@helia/verified-fetch` provides a [fetch](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API)-like API for retrieving content from the [IPFS](https://ipfs.tech/) network.
+
+All content is retrieved in a [trustless manner](https://www.techopedia.com/definition/trustless), and the integrity of all bytes are verified by comparing hashes of the data. By default, CIDs are retrieved over HTTP from [trustless gateways](https://specs.ipfs.tech/http-gateways/trustless-gateway/).
+
+This is a marked improvement over `fetch` which offers no such protections and is vulnerable to all sorts of attacks like [Content Spoofing](https://owasp.org/www-community/attacks/Content_Spoofing), [DNS Hijacking](https://en.wikipedia.org/wiki/DNS_hijacking), etc.
+
+A `verifiedFetch` function is exported to get up and running quickly, and a `createVerifiedFetch` function is also available that allows customizing the underlying [Helia](https://helia.io/) node for complete control over how content is retrieved.
+
+Browser-cache-friendly [Response](https://developer.mozilla.org/en-US/docs/Web/API/Response) objects are returned which should be instantly familiar to web developers.
+
+You may use any supported resource argument to fetch content:
+
+- [CID](https://multiformats.github.io/js-multiformats/classes/cid.CID.html) instance
+- IPFS URL
+- IPNS URL
+
+## Example - Getting started
+
+```typescript
+import { verifiedFetch } from '@helia/verified-fetch'
+
+const resp = await verifiedFetch('ipfs://bafy...')
+
+const json = await resp.json()
+```
+
+## Example - Using a CID instance to fetch JSON
+
+```typescript
+import { verifiedFetch } from '@helia/verified-fetch'
+import { CID } from 'multiformats/cid'
+
+const cid = CID.parse('bafyFoo') // some json file
+const response = await verifiedFetch(cid)
+const json = await response.json()
+```
+
+## Example - Using IPFS protocol to fetch an image
+
+```typescript
+import { verifiedFetch } from '@helia/verified-fetch'
+
+const response = await verifiedFetch('ipfs://bafyFoo') // CID for some image file
+const blob = await response.blob()
+const image = document.createElement('img')
+image.src = URL.createObjectURL(blob)
+document.body.appendChild(image)
+```
+
+## Example - Using IPNS protocol to stream a big file
+
+```typescript
+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()
+```
+
+## Configuration
+
+### Custom HTTP gateways and routers
+
+Out of the box `@helia/verified-fetch` uses a default set of [trustless gateways](https://specs.ipfs.tech/http-gateways/trustless-gateway/) for fetching blocks and [HTTP delegated routers](https://specs.ipfs.tech/routing/http-routing-v1/) for performing routing tasks - looking up peers, resolving/publishing [IPNS](https://docs.ipfs.tech/concepts/ipns/) names, etc.
+
+It's possible to override these by passing `gateways` and `routers` keys to the `createVerifiedFetch` function:
+
+## Example - Configuring gateways and routers
+
+```typescript
+import { createVerifiedFetch } from '@helia/verified-fetch'
+
+const fetch = await createVerifiedFetch({
+ gateways: ['https://trustless-gateway.link'],
+ routers: ['http://delegated-ipfs.dev']
+})
+
+const resp = await fetch('ipfs://bafy...')
+
+const json = await resp.json()
+```
+
+### Usage with customized Helia
+
+For full control of how `@helia/verified-fetch` fetches content from the distributed web you can pass a preconfigured Helia node to `createVerifiedFetch`.
+
+The [helia](https://www.npmjs.com/package/helia) module is configured with a libp2p node that is suited for decentralized applications, alternatively [@helia/http](https://www.npmjs.com/package/@helia/http) is available which uses HTTP gateways for all network operations.
+
+You can see variations of Helia and js-libp2p configuration options at .
+
+```typescript
+import { trustlessGateway } from '@helia/block-brokers'
+import { createHeliaHTTP } from '@helia/http'
+import { delegatedHTTPRouting } from '@helia/routers'
+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))
+ })
+)
+
+const resp = await fetch('ipfs://bafy...')
+
+const json = await resp.json()
+```
+
+### Custom content-type parsing
+
+By default, if the response can be parsed as JSON, `@helia/verified-fetch` sets the `Content-Type` header as `application/json`, otherwise it sets it as `application/octet-stream` - this is because the `.json()`, `.text()`, `.blob()`, and `.arrayBuffer()` methods will usually work as expected without a detailed content type.
+
+If you require an accurate content-type you can provide a `contentTypeParser` function as an option to `createVerifiedFetch` to handle parsing the content type.
+
+The function you provide will be called with the first chunk of bytes from the file and should return a string or a promise of a string.
+
+## Example - Customizing content-type parsing
+
+```typescript
+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']
+}, {
+ 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')
+ ]
+})
+```
+
+### IPLD codec handling
+
+IPFS supports several data formats (typically referred to as codecs) which are included in the CID. `@helia/verified-fetch` attempts to abstract away some of the details for easier consumption.
+
+#### DAG-PB
+
+[DAG-PB](https://ipld.io/docs/codecs/known/dag-pb/) is the codec we are most likely to encounter, it is what [UnixFS](https://github.com/ipfs/specs/blob/main/UNIXFS.md) uses under the hood.
+
+##### Using the DAG-PB codec as a Blob
+
+```typescript
+import { verifiedFetch } from '@helia/verified-fetch'
+
+const res = await verifiedFetch('ipfs://Qmfoo')
+const blob = await res.blob()
+
+console.info(blob) // Blob { size: x, type: 'application/octet-stream' }
+```
+
+##### Using the DAG-PB codec as an ArrayBuffer
+
+```typescript
+import { verifiedFetch } from '@helia/verified-fetch'
+
+const res = await verifiedFetch('ipfs://Qmfoo')
+const buf = await res.arrayBuffer()
+
+console.info(buf) // ArrayBuffer { [Uint8Contents]: < ... >, byteLength: x }
+```
+
+##### Using the DAG-PB codec as a stream
+
+```typescript
+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()
+
+ if (next?.done === true) {
+ break
+ }
+
+ if (next?.value != null) {
+ console.info(next.value) // Uint8Array(x) [ ... ]
+ }
+}
+```
+
+##### Content-Type
+
+When fetching `DAG-PB` data, the content type will be set to `application/octet-stream` unless a custom content-type parser is configured.
+
+#### JSON
+
+The JSON codec is a very simple codec, a block parseable with this codec is a JSON string encoded into a `Uint8Array`.
+
+##### Using the JSON codec
+
+```typescript
+import * as json from 'multiformats/codecs/json'
+
+const block = new TextEncoder().encode('{ "hello": "world" }')
+const obj = json.decode(block)
+
+console.info(obj) // { hello: 'world' }
+```
+
+##### Content-Type
+
+When the `JSON` codec is encountered, the `Content-Type` header of the response will be set to `application/json`.
+
+### DAG-JSON
+
+[DAG-JSON](https://ipld.io/docs/codecs/known/dag-json/) expands on the `JSON` codec, adding the ability to contain [CID](https://docs.ipfs.tech/concepts/content-addressing/)s which act as links to other blocks, and byte arrays.
+
+`CID`s and byte arrays are represented using special object structures with a single `"/"` property.
+
+Using `DAG-JSON` has two important caveats:
+
+1. Your `JSON` structure cannot contain an object with only a `"/"` property, as it will be interpreted as a special type.
+2. Since `JSON` has no technical limit on number sizes, `DAG-JSON` also allows numbers larger than `Number.MAX_SAFE_INTEGER`. JavaScript requires use of `BigInt`s to represent numbers larger than this, and `JSON.parse` does not support them, so precision will be lost.
+
+Otherwise this codec follows the same rules as the `JSON` codec.
+
+##### Using the DAG-JSON codec
+
+```typescript
+import * as dagJson from '@ipld/dag-json'
+
+const block = new TextEncoder().encode(`{
+ "hello": "world",
+ "cid": {
+ "/": "baeaaac3imvwgy3zao5xxe3de"
+ },
+ "buf": {
+ "/": {
+ "bytes": "AAECAwQ"
+ }
+ }
+}`)
+
+const obj = dagJson.decode(block)
+
+console.info(obj)
+// {
+// hello: 'world',
+// cid: CID(baeaaac3imvwgy3zao5xxe3de),
+// buf: Uint8Array(5) [ 0, 1, 2, 3, 4 ]
+// }
+```
+
+##### Content-Type
+
+When the `DAG-JSON` codec is encountered in the requested CID, the `Content-Type` header of the response will be set to `application/json`.
+
+`DAG-JSON` data can be parsed from the response by using the `.json()` function, which will return `CID`s/byte arrays as plain `{ "/": ... }` objects:
+
+```typescript
+import { verifiedFetch } from '@helia/verified-fetch'
+import * as dagJson from '@ipld/dag-json'
+
+const res = await verifiedFetch('ipfs://bafyDAGJSON')
+
+// either:
+const obj = await res.json()
+console.info(obj.cid) // { "/": "baeaaac3imvwgy3zao5xxe3de" }
+console.info(obj.buf) // { "/": { "bytes": "AAECAwQ" } }
+```
+
+Alternatively, it can be decoded using the `@ipld/dag-json` module and the `.arrayBuffer()` method, in which case you will get `CID` objects and `Uint8Array`s:
+
+```typescript
+import { verifiedFetch } from '@helia/verified-fetch'
+import * as dagJson from '@ipld/dag-json'
+
+const res = await verifiedFetch('ipfs://bafyDAGJSON')
+
+// or:
+const obj = dagJson.decode(await res.arrayBuffer())
+console.info(obj.cid) // CID(baeaaac3imvwgy3zao5xxe3de)
+console.info(obj.buf) // Uint8Array(5) [ 0, 1, 2, 3, 4 ]
+```
+
+#### DAG-CBOR
+
+[DAG-CBOR](https://ipld.io/docs/codecs/known/dag-cbor/) uses the [Concise Binary Object Representation](https://cbor.io/) format for serialization instead of JSON.
+
+This supports more datatypes in a safer way than JSON and is smaller on the wire to boot so is usually preferable to JSON or DAG-JSON.
+
+##### Content-Type
+
+Not all data types supported by `DAG-CBOR` can be successfully turned into JSON and back into the same binary form.
+
+When a decoded block can be round-tripped to JSON, the `Content-Type` will be set to `application/json`. In this case the `.json()` method on the `Response` object can be used to obtain an object representation of the response.
+
+When it cannot, the `Content-Type` will be `application/octet-stream` - in this case the `@ipld/dag-json` module must be used to deserialize the return value from `.arrayBuffer()`.
+
+##### Detecting JSON-safe DAG-CBOR
+
+If the `Content-Type` header of the response is `application/json`, the `.json()` method may be used to access the response body in object form, otherwise the `.arrayBuffer()` method must be used to decode the raw bytes using the `@ipld/dag-cbor` module.
+
+```typescript
+import { verifiedFetch } from '@helia/verified-fetch'
+import * as dagCbor from '@ipld/dag-cbor'
+
+const res = await verifiedFetch('ipfs://bafyDagCborCID')
+let obj
+
+if (res.headers.get('Content-Type') === 'application/json') {
+ // DAG-CBOR data can be safely decoded as JSON
+ obj = await res.json()
+} else {
+ // response contains non-JSON friendly data types
+ obj = dagCbor.decode(await res.arrayBuffer())
+}
+
+console.info(obj) // ...
+```
+
+## The `Accept` header
+
+The `Accept` header can be passed to override certain response processing, or to ensure that the final `Content-Type` of the response is the one that is expected.
+
+If the final `Content-Type` does not match the `Accept` header, or if the content cannot be represented in the format dictated by the `Accept` header, or you have configured a custom content type parser, and that parser returns a value that isn't in the accept header, a [406: Not Acceptable](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/406) response will be returned:
+
+```typescript
+import { verifiedFetch } from '@helia/verified-fetch'
+
+const res = await verifiedFetch('ipfs://bafyJPEGImageCID', {
+ headers: {
+ accept: 'image/png'
+ }
+})
+
+console.info(res.status) // 406 - the image was a JPEG but we specified PNG as the accept header
+```
+
+It can also be used to skip processing the data from some formats such as `DAG-CBOR` if you wish to handle decoding it yourself:
+
+```typescript
+import { verifiedFetch } from '@helia/verified-fetch'
+
+const res = await verifiedFetch('ipfs://bafyDAGCBORCID', {
+ headers: {
+ accept: 'application/octet-stream'
+ }
+})
+
+console.info(res.headers.get('accept')) // application/octet-stream
+const buf = await res.arrayBuffer() // raw bytes, not processed as JSON
+```
+
+## Comparison to fetch
+
+This module attempts to act as similarly to the `fetch()` API as possible.
+
+[The `fetch()` API](https://developer.mozilla.org/en-US/docs/Web/API/fetch) takes two parameters:
+
+1. A [resource](https://developer.mozilla.org/en-US/docs/Web/API/fetch#resource)
+2. An [options object](https://developer.mozilla.org/en-US/docs/Web/API/fetch#options)
+
+### Resource argument
+
+This library supports the following methods of fetching web3 content from IPFS:
+
+1. IPFS protocol: `ipfs://` & `ipfs://`
+2. IPNS protocol: `ipns://` & `ipns://` & `ipns://`
+3. CID instances: An actual CID instance `CID.parse('bafy...')`
+
+As well as support for pathing & params for items 1 & 2 above according to [IPFS - Path Gateway Specification](https://specs.ipfs.tech/http-gateways/path-gateway) & [IPFS - Trustless Gateway Specification](https://specs.ipfs.tech/http-gateways/trustless-gateway/). Further refinement of those specifications specifically for web-based scenarios can be found in the [Web Pathing Specification IPIP](https://github.com/ipfs/specs/pull/453).
+
+If you pass a CID instance, it assumes you want the content for that specific CID only, and does not support pathing or params for that CID.
+
+### Options argument
+
+This library does not plan to support the exact Fetch API options object, as some of the arguments don't make sense. Instead, it will only support options necessary to meet [IPFS specs](https://specs.ipfs.tech/) related to specifying the resultant shape of desired content.
+
+Some of those header specifications are:
+
+1.
+2.
+3.
+
+Where possible, options and Helia internals will be automatically configured to the appropriate codec & content type based on the `verified-fetch` configuration and `options` argument passed.
+
+Known Fetch API options that will be supported:
+
+1. `signal` - An AbortSignal that a user can use to abort the request.
+2. `redirect` - A string that specifies the redirect type. One of `follow`, `error`, or `manual`. Defaults to `follow`. Best effort to adhere to the [Fetch API redirect](https://developer.mozilla.org/en-US/docs/Web/API/fetch#redirect) parameter.
+3. `headers` - An object of headers to be sent with the request. Best effort to adhere to the [Fetch API headers](https://developer.mozilla.org/en-US/docs/Web/API/fetch#headers) parameter.
+ - `accept` - A string that specifies the accept header. Relevant values:
+ - [`vnd.ipld.raw`](https://www.iana.org/assignments/media-types/application/vnd.ipld.raw). (default)
+ - [`vnd.ipld.car`](https://www.iana.org/assignments/media-types/application/vnd.ipld.car)
+ - [`vnd.ipfs.ipns-record`](https://www.iana.org/assignments/media-types/application/vnd.ipfs.ipns-record)
+4. `method` - A string that specifies the HTTP method to use for the request. Defaults to `GET`. Best effort to adhere to the [Fetch API method](https://developer.mozilla.org/en-US/docs/Web/API/fetch#method) parameter.
+5. `body` - An object that specifies the body of the request. Best effort to adhere to the [Fetch API body](https://developer.mozilla.org/en-US/docs/Web/API/fetch#body) parameter.
+6. `cache` - Will basically act as `force-cache` for the request. Best effort to adhere to the [Fetch API cache](https://developer.mozilla.org/en-US/docs/Web/API/fetch#cache) parameter.
+
+Non-Fetch API options that will be supported:
+
+1. `onProgress` - Similar to Helia `onProgress` options, this will be a function that will be called with a progress event. Supported progress events are:
+ - `helia:verified-fetch:error` - An error occurred during the request.
+ - `helia:verified-fetch:request:start` - The request has been sent
+ - `helia:verified-fetch:request:complete` - The request has been sent
+ - `helia:verified-fetch:request:error` - An error occurred during the request.
+ - `helia:verified-fetch:request:abort` - The request was aborted prior to completion.
+ - `helia:verified-fetch:response:start` - The initial HTTP Response headers have been set, and response stream is started.
+ - `helia:verified-fetch:response:complete` - The response stream has completed.
+ - `helia:verified-fetch:response:error` - An error occurred while building the response.
+
+Some in-flight specs (IPIPs) that will affect the options object this library supports in the future can be seen at , a subset are:
+
+1. [IPIP-0412: Signaling Block Order in CARs on HTTP Gateways](https://specs.ipfs.tech/ipips/ipip-0412/)
+2. [IPIP-0402: Partial CAR Support on Trustless Gateways](https://specs.ipfs.tech/ipips/ipip-0402/)
+3. [IPIP-0386: Subdomain Gateway Interop with \_redirects](https://specs.ipfs.tech/ipips/ipip-0386/)
+4. [IPIP-0328: JSON and CBOR Response Formats on HTTP Gateways](https://specs.ipfs.tech/ipips/ipip-0328/)
+5. [IPIP-0288: TAR Response Format on HTTP Gateways](https://specs.ipfs.tech/ipips/ipip-0288/)
+
+### Response types
+
+This library's purpose is to return reasonably representable content from IPFS. In other words, fetching content is intended for leaf-node content -- such as images/videos/audio & other assets, or other IPLD content (with link) -- that can be represented by . The content type you receive back will depend upon the CID you request as well as the `Accept` header value you provide.
+
+All content we retrieve from the IPFS network is obtained via an AsyncIterable, and will be set as the [body of the HTTP Response](https://developer.mozilla.org/en-US/docs/Web/API/Response/Response#body) via a [`ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/Streams_API/Using_readable_streams#consuming_a_fetch_as_a_stream) or other efficient method that avoids loading the entire response into memory or getting the entire response from the network before returning a response to the user.
+
+If your content doesn't have a mime-type or an [IPFS spec](https://specs.ipfs.tech), this library will not support it, but you can use the [`helia`](https://github.com/ipfs/helia) library directly for those use cases. See [Unsupported response types](#unsupported-response-types) for more information.
+
+#### Handling response types
+
+For handling responses we want to follow conventions/abstractions from Fetch API where possible:
+
+- For JSON, assuming you abstract any differences between dag-json/dag-cbor/json/and json-file-on-unixfs, you would call `.json()` to get a JSON object.
+- For images (or other web-relevant asset) you want to add to the DOM, use `.blob()` or `.arrayBuffer()` to get the raw bytes.
+- For plain text in utf-8, you would call `.text()`
+- For streaming response data, use something like `response.body.getReader()` to get a [`ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/Streams_API/Using_readable_streams#consuming_a_fetch_as_a_stream).
+
+#### Unsupported response types
+
+- Returning IPLD nodes or DAGs as JS objects is not supported, as there is no currently well-defined structure for representing this data in an [HTTP Response](https://developer.mozilla.org/en-US/docs/Web/API/Response). Instead, users should request `aplication/vnd.ipld.car` or use the [`helia`](https://github.com/ipfs/helia) library directly for this use case.
+- Others? Open an issue or PR!
+
+### Response headers
+
+This library will set the [HTTP Response](https://developer.mozilla.org/en-US/docs/Web/API/Response) headers to the appropriate values for the content type according to the appropriate [IPFS Specifications](https://specs.ipfs.tech/).
+
+Some known header specifications:
+
+-
+-
+-
+
+### Possible Scenarios that could cause confusion
+
+#### Attempting to fetch the CID for content that does not make sense
+
+If you request `bafybeiaysi4s6lnjev27ln5icwm6tueaw2vdykrtjkwiphwekaywqhcjze`, which points to the root of the en.wikipedia.org mirror, a response object does not make sense.
+
+### Errors
+
+Known Errors that can be thrown:
+
+1. `TypeError` - If the resource argument is not a string, CID, or CID string.
+2. `TypeError` - If the options argument is passed and not an object.
+3. `TypeError` - If the options argument is passed and is malformed.
+4. `AbortError` - If the content request is aborted due to user aborting provided AbortSignal.
+
+# Install
+
+```console
+$ npm i @helia/verified-fetch
+```
+
+## Browser `
+```
# API Docs
--
+-
# License
diff --git a/packages/verified-fetch/README.md b/packages/verified-fetch/README.md
deleted file mode 100644
index 50631d5d..00000000
--- a/packages/verified-fetch/README.md
+++ /dev/null
@@ -1,559 +0,0 @@
-
-
-
-
-
-
-# @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
-
-
-
-`@helia/verified-fetch` provides a [fetch](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API)-like API for retrieving content from the [IPFS](https://ipfs.tech/) network.
-
-All content is retrieved in a [trustless manner](https://www.techopedia.com/definition/trustless), and the integrity of all bytes are verified by comparing hashes of the data. By default, CIDs are retrieved over HTTP from [trustless gateways](https://specs.ipfs.tech/http-gateways/trustless-gateway/).
-
-This is a marked improvement over `fetch` which offers no such protections and is vulnerable to all sorts of attacks like [Content Spoofing](https://owasp.org/www-community/attacks/Content_Spoofing), [DNS Hijacking](https://en.wikipedia.org/wiki/DNS_hijacking), etc.
-
-A `verifiedFetch` function is exported to get up and running quickly, and a `createVerifiedFetch` function is also available that allows customizing the underlying [Helia](https://helia.io/) node for complete control over how content is retrieved.
-
-Browser-cache-friendly [Response](https://developer.mozilla.org/en-US/docs/Web/API/Response) objects are returned which should be instantly familiar to web developers.
-
-You may use any supported resource argument to fetch content:
-
-- [CID](https://multiformats.github.io/js-multiformats/classes/cid.CID.html) instance
-- IPFS URL
-- IPNS URL
-
-## Example - Getting started
-
-```typescript
-import { verifiedFetch } from '@helia/verified-fetch'
-
-const resp = await verifiedFetch('ipfs://bafy...')
-
-const json = await resp.json()
-```
-
-## Example - Using a CID instance to fetch JSON
-
-```typescript
-import { verifiedFetch } from '@helia/verified-fetch'
-import { CID } from 'multiformats/cid'
-
-const cid = CID.parse('bafyFoo') // some json file
-const response = await verifiedFetch(cid)
-const json = await response.json()
-```
-
-## Example - Using IPFS protocol to fetch an image
-
-```typescript
-import { verifiedFetch } from '@helia/verified-fetch'
-
-const response = await verifiedFetch('ipfs://bafyFoo') // CID for some image file
-const blob = await response.blob()
-const image = document.createElement('img')
-image.src = URL.createObjectURL(blob)
-document.body.appendChild(image)
-```
-
-## Example - Using IPNS protocol to stream a big file
-
-```typescript
-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()
-```
-
-## Configuration
-
-### Custom HTTP gateways and routers
-
-Out of the box `@helia/verified-fetch` uses a default set of [trustless gateways](https://specs.ipfs.tech/http-gateways/trustless-gateway/) for fetching blocks and [HTTP delegated routers](https://specs.ipfs.tech/routing/http-routing-v1/) for performing routing tasks - looking up peers, resolving/publishing [IPNS](https://docs.ipfs.tech/concepts/ipns/) names, etc.
-
-It's possible to override these by passing `gateways` and `routers` keys to the `createVerifiedFetch` function:
-
-## Example - Configuring gateways and routers
-
-```typescript
-import { createVerifiedFetch } from '@helia/verified-fetch'
-
-const fetch = await createVerifiedFetch({
- gateways: ['https://trustless-gateway.link'],
- routers: ['http://delegated-ipfs.dev']
-})
-
-const resp = await fetch('ipfs://bafy...')
-
-const json = await resp.json()
-```
-
-### Usage with customized Helia
-
-For full control of how `@helia/verified-fetch` fetches content from the distributed web you can pass a preconfigured Helia node to `createVerifiedFetch`.
-
-The [helia](https://www.npmjs.com/package/helia) module is configured with a libp2p node that is suited for decentralized applications, alternatively [@helia/http](https://www.npmjs.com/package/@helia/http) is available which uses HTTP gateways for all network operations.
-
-You can see variations of Helia and js-libp2p configuration options at .
-
-```typescript
-import { trustlessGateway } from '@helia/block-brokers'
-import { createHeliaHTTP } from '@helia/http'
-import { delegatedHTTPRouting } from '@helia/routers'
-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))
- })
-)
-
-const resp = await fetch('ipfs://bafy...')
-
-const json = await resp.json()
-```
-
-### Custom content-type parsing
-
-By default, if the response can be parsed as JSON, `@helia/verified-fetch` sets the `Content-Type` header as `application/json`, otherwise it sets it as `application/octet-stream` - this is because the `.json()`, `.text()`, `.blob()`, and `.arrayBuffer()` methods will usually work as expected without a detailed content type.
-
-If you require an accurate content-type you can provide a `contentTypeParser` function as an option to `createVerifiedFetch` to handle parsing the content type.
-
-The function you provide will be called with the first chunk of bytes from the file and should return a string or a promise of a string.
-
-## Example - Customizing content-type parsing
-
-```typescript
-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']
-}, {
- 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')
- ]
-})
-```
-
-### IPLD codec handling
-
-IPFS supports several data formats (typically referred to as codecs) which are included in the CID. `@helia/verified-fetch` attempts to abstract away some of the details for easier consumption.
-
-#### DAG-PB
-
-[DAG-PB](https://ipld.io/docs/codecs/known/dag-pb/) is the codec we are most likely to encounter, it is what [UnixFS](https://github.com/ipfs/specs/blob/main/UNIXFS.md) uses under the hood.
-
-##### Using the DAG-PB codec as a Blob
-
-```typescript
-import { verifiedFetch } from '@helia/verified-fetch'
-
-const res = await verifiedFetch('ipfs://Qmfoo')
-const blob = await res.blob()
-
-console.info(blob) // Blob { size: x, type: 'application/octet-stream' }
-```
-
-##### Using the DAG-PB codec as an ArrayBuffer
-
-```typescript
-import { verifiedFetch } from '@helia/verified-fetch'
-
-const res = await verifiedFetch('ipfs://Qmfoo')
-const buf = await res.arrayBuffer()
-
-console.info(buf) // ArrayBuffer { [Uint8Contents]: < ... >, byteLength: x }
-```
-
-##### Using the DAG-PB codec as a stream
-
-```typescript
-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()
-
- if (next?.done === true) {
- break
- }
-
- if (next?.value != null) {
- console.info(next.value) // Uint8Array(x) [ ... ]
- }
-}
-```
-
-##### Content-Type
-
-When fetching `DAG-PB` data, the content type will be set to `application/octet-stream` unless a custom content-type parser is configured.
-
-#### JSON
-
-The JSON codec is a very simple codec, a block parseable with this codec is a JSON string encoded into a `Uint8Array`.
-
-##### Using the JSON codec
-
-```typescript
-import * as json from 'multiformats/codecs/json'
-
-const block = new TextEncoder().encode('{ "hello": "world" }')
-const obj = json.decode(block)
-
-console.info(obj) // { hello: 'world' }
-```
-
-##### Content-Type
-
-When the `JSON` codec is encountered, the `Content-Type` header of the response will be set to `application/json`.
-
-### DAG-JSON
-
-[DAG-JSON](https://ipld.io/docs/codecs/known/dag-json/) expands on the `JSON` codec, adding the ability to contain [CID](https://docs.ipfs.tech/concepts/content-addressing/)s which act as links to other blocks, and byte arrays.
-
-`CID`s and byte arrays are represented using special object structures with a single `"/"` property.
-
-Using `DAG-JSON` has two important caveats:
-
-1. Your `JSON` structure cannot contain an object with only a `"/"` property, as it will be interpreted as a special type.
-2. Since `JSON` has no technical limit on number sizes, `DAG-JSON` also allows numbers larger than `Number.MAX_SAFE_INTEGER`. JavaScript requires use of `BigInt`s to represent numbers larger than this, and `JSON.parse` does not support them, so precision will be lost.
-
-Otherwise this codec follows the same rules as the `JSON` codec.
-
-##### Using the DAG-JSON codec
-
-```typescript
-import * as dagJson from '@ipld/dag-json'
-
-const block = new TextEncoder().encode(`{
- "hello": "world",
- "cid": {
- "/": "baeaaac3imvwgy3zao5xxe3de"
- },
- "buf": {
- "/": {
- "bytes": "AAECAwQ"
- }
- }
-}`)
-
-const obj = dagJson.decode(block)
-
-console.info(obj)
-// {
-// hello: 'world',
-// cid: CID(baeaaac3imvwgy3zao5xxe3de),
-// buf: Uint8Array(5) [ 0, 1, 2, 3, 4 ]
-// }
-```
-
-##### Content-Type
-
-When the `DAG-JSON` codec is encountered in the requested CID, the `Content-Type` header of the response will be set to `application/json`.
-
-`DAG-JSON` data can be parsed from the response by using the `.json()` function, which will return `CID`s/byte arrays as plain `{ "/": ... }` objects:
-
-```typescript
-import { verifiedFetch } from '@helia/verified-fetch'
-import * as dagJson from '@ipld/dag-json'
-
-const res = await verifiedFetch('ipfs://bafyDAGJSON')
-
-// either:
-const obj = await res.json()
-console.info(obj.cid) // { "/": "baeaaac3imvwgy3zao5xxe3de" }
-console.info(obj.buf) // { "/": { "bytes": "AAECAwQ" } }
-```
-
-Alternatively, it can be decoded using the `@ipld/dag-json` module and the `.arrayBuffer()` method, in which case you will get `CID` objects and `Uint8Array`s:
-
-```typescript
-import { verifiedFetch } from '@helia/verified-fetch'
-import * as dagJson from '@ipld/dag-json'
-
-const res = await verifiedFetch('ipfs://bafyDAGJSON')
-
-// or:
-const obj = dagJson.decode(await res.arrayBuffer())
-console.info(obj.cid) // CID(baeaaac3imvwgy3zao5xxe3de)
-console.info(obj.buf) // Uint8Array(5) [ 0, 1, 2, 3, 4 ]
-```
-
-#### DAG-CBOR
-
-[DAG-CBOR](https://ipld.io/docs/codecs/known/dag-cbor/) uses the [Concise Binary Object Representation](https://cbor.io/) format for serialization instead of JSON.
-
-This supports more datatypes in a safer way than JSON and is smaller on the wire to boot so is usually preferable to JSON or DAG-JSON.
-
-##### Content-Type
-
-Not all data types supported by `DAG-CBOR` can be successfully turned into JSON and back into the same binary form.
-
-When a decoded block can be round-tripped to JSON, the `Content-Type` will be set to `application/json`. In this case the `.json()` method on the `Response` object can be used to obtain an object representation of the response.
-
-When it cannot, the `Content-Type` will be `application/octet-stream` - in this case the `@ipld/dag-json` module must be used to deserialize the return value from `.arrayBuffer()`.
-
-##### Detecting JSON-safe DAG-CBOR
-
-If the `Content-Type` header of the response is `application/json`, the `.json()` method may be used to access the response body in object form, otherwise the `.arrayBuffer()` method must be used to decode the raw bytes using the `@ipld/dag-cbor` module.
-
-```typescript
-import { verifiedFetch } from '@helia/verified-fetch'
-import * as dagCbor from '@ipld/dag-cbor'
-
-const res = await verifiedFetch('ipfs://bafyDagCborCID')
-let obj
-
-if (res.headers.get('Content-Type') === 'application/json') {
- // DAG-CBOR data can be safely decoded as JSON
- obj = await res.json()
-} else {
- // response contains non-JSON friendly data types
- obj = dagCbor.decode(await res.arrayBuffer())
-}
-
-console.info(obj) // ...
-```
-
-## The `Accept` header
-
-The `Accept` header can be passed to override certain response processing, or to ensure that the final `Content-Type` of the response is the one that is expected.
-
-If the final `Content-Type` does not match the `Accept` header, or if the content cannot be represented in the format dictated by the `Accept` header, or you have configured a custom content type parser, and that parser returns a value that isn't in the accept header, a [406: Not Acceptable](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/406) response will be returned:
-
-```typescript
-import { verifiedFetch } from '@helia/verified-fetch'
-
-const res = await verifiedFetch('ipfs://bafyJPEGImageCID', {
- headers: {
- accept: 'image/png'
- }
-})
-
-console.info(res.status) // 406 - the image was a JPEG but we specified PNG as the accept header
-```
-
-It can also be used to skip processing the data from some formats such as `DAG-CBOR` if you wish to handle decoding it yourself:
-
-```typescript
-import { verifiedFetch } from '@helia/verified-fetch'
-
-const res = await verifiedFetch('ipfs://bafyDAGCBORCID', {
- headers: {
- accept: 'application/octet-stream'
- }
-})
-
-console.info(res.headers.get('accept')) // application/octet-stream
-const buf = await res.arrayBuffer() // raw bytes, not processed as JSON
-```
-
-## Comparison to fetch
-
-This module attempts to act as similarly to the `fetch()` API as possible.
-
-[The `fetch()` API](https://developer.mozilla.org/en-US/docs/Web/API/fetch) takes two parameters:
-
-1. A [resource](https://developer.mozilla.org/en-US/docs/Web/API/fetch#resource)
-2. An [options object](https://developer.mozilla.org/en-US/docs/Web/API/fetch#options)
-
-### Resource argument
-
-This library supports the following methods of fetching web3 content from IPFS:
-
-1. IPFS protocol: `ipfs://` & `ipfs://`
-2. IPNS protocol: `ipns://` & `ipns://` & `ipns://`
-3. CID instances: An actual CID instance `CID.parse('bafy...')`
-
-As well as support for pathing & params for items 1 & 2 above according to [IPFS - Path Gateway Specification](https://specs.ipfs.tech/http-gateways/path-gateway) & [IPFS - Trustless Gateway Specification](https://specs.ipfs.tech/http-gateways/trustless-gateway/). Further refinement of those specifications specifically for web-based scenarios can be found in the [Web Pathing Specification IPIP](https://github.com/ipfs/specs/pull/453).
-
-If you pass a CID instance, it assumes you want the content for that specific CID only, and does not support pathing or params for that CID.
-
-### Options argument
-
-This library does not plan to support the exact Fetch API options object, as some of the arguments don't make sense. Instead, it will only support options necessary to meet [IPFS specs](https://specs.ipfs.tech/) related to specifying the resultant shape of desired content.
-
-Some of those header specifications are:
-
-1.
-2.
-3.
-
-Where possible, options and Helia internals will be automatically configured to the appropriate codec & content type based on the `verified-fetch` configuration and `options` argument passed.
-
-Known Fetch API options that will be supported:
-
-1. `signal` - An AbortSignal that a user can use to abort the request.
-2. `redirect` - A string that specifies the redirect type. One of `follow`, `error`, or `manual`. Defaults to `follow`. Best effort to adhere to the [Fetch API redirect](https://developer.mozilla.org/en-US/docs/Web/API/fetch#redirect) parameter.
-3. `headers` - An object of headers to be sent with the request. Best effort to adhere to the [Fetch API headers](https://developer.mozilla.org/en-US/docs/Web/API/fetch#headers) parameter.
- - `accept` - A string that specifies the accept header. Relevant values:
- - [`vnd.ipld.raw`](https://www.iana.org/assignments/media-types/application/vnd.ipld.raw). (default)
- - [`vnd.ipld.car`](https://www.iana.org/assignments/media-types/application/vnd.ipld.car)
- - [`vnd.ipfs.ipns-record`](https://www.iana.org/assignments/media-types/application/vnd.ipfs.ipns-record)
-4. `method` - A string that specifies the HTTP method to use for the request. Defaults to `GET`. Best effort to adhere to the [Fetch API method](https://developer.mozilla.org/en-US/docs/Web/API/fetch#method) parameter.
-5. `body` - An object that specifies the body of the request. Best effort to adhere to the [Fetch API body](https://developer.mozilla.org/en-US/docs/Web/API/fetch#body) parameter.
-6. `cache` - Will basically act as `force-cache` for the request. Best effort to adhere to the [Fetch API cache](https://developer.mozilla.org/en-US/docs/Web/API/fetch#cache) parameter.
-
-Non-Fetch API options that will be supported:
-
-1. `onProgress` - Similar to Helia `onProgress` options, this will be a function that will be called with a progress event. Supported progress events are:
- - `helia:verified-fetch:error` - An error occurred during the request.
- - `helia:verified-fetch:request:start` - The request has been sent
- - `helia:verified-fetch:request:complete` - The request has been sent
- - `helia:verified-fetch:request:error` - An error occurred during the request.
- - `helia:verified-fetch:request:abort` - The request was aborted prior to completion.
- - `helia:verified-fetch:response:start` - The initial HTTP Response headers have been set, and response stream is started.
- - `helia:verified-fetch:response:complete` - The response stream has completed.
- - `helia:verified-fetch:response:error` - An error occurred while building the response.
-
-Some in-flight specs (IPIPs) that will affect the options object this library supports in the future can be seen at , a subset are:
-
-1. [IPIP-0412: Signaling Block Order in CARs on HTTP Gateways](https://specs.ipfs.tech/ipips/ipip-0412/)
-2. [IPIP-0402: Partial CAR Support on Trustless Gateways](https://specs.ipfs.tech/ipips/ipip-0402/)
-3. [IPIP-0386: Subdomain Gateway Interop with \_redirects](https://specs.ipfs.tech/ipips/ipip-0386/)
-4. [IPIP-0328: JSON and CBOR Response Formats on HTTP Gateways](https://specs.ipfs.tech/ipips/ipip-0328/)
-5. [IPIP-0288: TAR Response Format on HTTP Gateways](https://specs.ipfs.tech/ipips/ipip-0288/)
-
-### Response types
-
-This library's purpose is to return reasonably representable content from IPFS. In other words, fetching content is intended for leaf-node content -- such as images/videos/audio & other assets, or other IPLD content (with link) -- that can be represented by . The content type you receive back will depend upon the CID you request as well as the `Accept` header value you provide.
-
-All content we retrieve from the IPFS network is obtained via an AsyncIterable, and will be set as the [body of the HTTP Response](https://developer.mozilla.org/en-US/docs/Web/API/Response/Response#body) via a [`ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/Streams_API/Using_readable_streams#consuming_a_fetch_as_a_stream) or other efficient method that avoids loading the entire response into memory or getting the entire response from the network before returning a response to the user.
-
-If your content doesn't have a mime-type or an [IPFS spec](https://specs.ipfs.tech), this library will not support it, but you can use the [`helia`](https://github.com/ipfs/helia) library directly for those use cases. See [Unsupported response types](#unsupported-response-types) for more information.
-
-#### Handling response types
-
-For handling responses we want to follow conventions/abstractions from Fetch API where possible:
-
-- For JSON, assuming you abstract any differences between dag-json/dag-cbor/json/and json-file-on-unixfs, you would call `.json()` to get a JSON object.
-- For images (or other web-relevant asset) you want to add to the DOM, use `.blob()` or `.arrayBuffer()` to get the raw bytes.
-- For plain text in utf-8, you would call `.text()`
-- For streaming response data, use something like `response.body.getReader()` to get a [`ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/Streams_API/Using_readable_streams#consuming_a_fetch_as_a_stream).
-
-#### Unsupported response types
-
-- Returning IPLD nodes or DAGs as JS objects is not supported, as there is no currently well-defined structure for representing this data in an [HTTP Response](https://developer.mozilla.org/en-US/docs/Web/API/Response). Instead, users should request `aplication/vnd.ipld.car` or use the [`helia`](https://github.com/ipfs/helia) library directly for this use case.
-- Others? Open an issue or PR!
-
-### Response headers
-
-This library will set the [HTTP Response](https://developer.mozilla.org/en-US/docs/Web/API/Response) headers to the appropriate values for the content type according to the appropriate [IPFS Specifications](https://specs.ipfs.tech/).
-
-Some known header specifications:
-
--
--
--
-
-### Possible Scenarios that could cause confusion
-
-#### Attempting to fetch the CID for content that does not make sense
-
-If you request `bafybeiaysi4s6lnjev27ln5icwm6tueaw2vdykrtjkwiphwekaywqhcjze`, which points to the root of the en.wikipedia.org mirror, a response object does not make sense.
-
-### Errors
-
-Known Errors that can be thrown:
-
-1. `TypeError` - If the resource argument is not a string, CID, or CID string.
-2. `TypeError` - If the options argument is passed and not an object.
-3. `TypeError` - If the options argument is passed and is malformed.
-4. `AbortError` - If the content request is aborted due to user aborting provided AbortSignal.
-
-# Install
-
-```console
-$ npm i @helia/verified-fetch
-```
-
-## Browser `
-```
-
-# 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)