@helia/http
Version:
A lightweight implementation of IPFS over HTTP in JavaScript
163 lines (142 loc) • 5.04 kB
text/typescript
/**
* @packageDocumentation
*
* Exports a `createHeliaHTTP` function that returns an object that implements a lightweight version of the {@link Helia} API that functions only over HTTP.
*
* Pass it to other modules like {@link https://www.npmjs.com/package/@helia/unixfs | @helia/unixfs} to fetch files from the distributed web.
*
* @example
*
* ```typescript
* import { createHeliaHTTP } from '@helia/http'
* import { unixfs } from '@helia/unixfs'
* import { CID } from 'multiformats/cid'
*
* const helia = await createHeliaHTTP()
*
* const fs = unixfs(helia)
* fs.cat(CID.parse('bafyFoo'))
* ```
*/
import { trustlessGateway } from '@helia/block-brokers'
import { Helia as HeliaClass } from '@helia/core'
import { MemoryBlockstore } from 'blockstore-core'
import { MemoryDatastore } from 'datastore-core'
import { createLibp2p } from './utils/libp2p.js'
import type { DefaultLibp2pServices } from './utils/libp2p-defaults.js'
import type { DAGWalker, Helia } from '@helia/interface'
import type { BlockBroker } from '@helia/interface/blocks'
import type { ComponentLogger, Libp2p } from '@libp2p/interface'
import type { Blockstore } from 'interface-blockstore'
import type { Datastore } from 'interface-datastore'
import type { Libp2pOptions } from 'libp2p'
import type { MultihashHasher } from 'multiformats/hashes/interface'
// re-export interface types so people don't have to depend on @helia/interface
// if they don't want to
export * from '@helia/interface'
export * from '@helia/interface/blocks'
export * from '@helia/interface/pins'
/**
* Options used to create a Helia node.
*/
export interface HeliaHTTPInit<T extends Libp2p = Libp2p> {
/**
* A libp2p node is required to perform network operations. Either a
* preconfigured node or options to configure a node can be passed
* here.
*
* If node options are passed, they will be merged with the default
* config for the current platform. In this case all passed config
* keys will replace those from the default config.
*/
libp2p?: T | Libp2pOptions
/**
* The blockstore is where blocks are stored
*/
blockstore?: Blockstore
/**
* The datastore is where data is stored
*/
datastore?: Datastore
/**
* By default sha256, sha512 and identity hashes are supported for
* bitswap operations. To bitswap blocks with CIDs using other hashes
* pass appropriate MultihashHashers here.
*/
hashers?: MultihashHasher[]
/**
* In order to pin CIDs that correspond to a DAG, it's necessary to know
* how to traverse that DAG. DAGWalkers take a block and yield any CIDs
* encoded within that block.
*/
dagWalkers?: DAGWalker[]
/**
* A list of strategies used to fetch blocks when they are not present in
* the local blockstore
*/
blockBrokers?: Array<(components: any) => BlockBroker>
/**
* Pass `false` to not start the Helia node
*/
start?: boolean
/**
* Garbage collection requires preventing blockstore writes during searches
* for unpinned blocks as DAGs are typically pinned after they've been
* imported - without locking this could lead to the deletion of blocks while
* they are being added to the blockstore.
*
* By default this lock is held on the main process (e.g. node cluster's
* primary process, the renderer thread in browsers) and other processes will
* contact the main process for access (worker processes in node cluster,
* webworkers in the browser).
*
* If Helia is being run wholly in a non-primary process, with no other process
* expected to access the blockstore (e.g. being run in the background in a
* webworker), pass true here to hold the gc lock in this process.
*/
holdGcLock?: boolean
/**
* An optional logging component to pass to libp2p. If not specified the
* default implementation from libp2p will be used.
*/
logger?: ComponentLogger
}
/**
* Create and return a Helia node
*/
export async function createHeliaHTTP (init: HeliaHTTPInit = {}): Promise<Helia> {
const datastore = init.datastore ?? new MemoryDatastore()
const blockstore = init.blockstore ?? new MemoryBlockstore()
let libp2p: Libp2p<DefaultLibp2pServices>
if (isLibp2p(init.libp2p)) {
libp2p = init.libp2p as any
} else {
libp2p = await createLibp2p<DefaultLibp2pServices>({
...init,
libp2p: init.libp2p,
datastore
})
}
const helia = new HeliaClass({
...init,
libp2p,
datastore,
blockstore,
blockBrokers: init.blockBrokers ?? [
trustlessGateway()
]
})
if (init.start !== false) {
await helia.start()
}
return helia
}
function isLibp2p (obj: any): obj is Libp2p {
if (obj == null) {
return false
}
// a non-exhaustive list of methods found on the libp2p object
const funcs = ['dial', 'dialProtocol', 'hangUp', 'handle', 'unhandle', 'getMultiaddrs', 'getProtocols']
// if these are all functions it's probably a libp2p object
return funcs.every(m => typeof obj[m] === 'function')
}