Skip to content

Commit

Permalink
feat!: use Helia's blockBroker interface (#406)
Browse files Browse the repository at this point in the history
BREAKING CHANGE: update of many ipfs/ipld/libp2p/helia deps that may break consumers. API of ipld-explorer-components has not changed.
  • Loading branch information
SgtPooki committed Nov 29, 2023
1 parent 5805bd8 commit c964a76
Show file tree
Hide file tree
Showing 19 changed files with 10,299 additions and 7,689 deletions.
5 changes: 3 additions & 2 deletions dev/devPage.jsx
Expand Up @@ -91,9 +91,10 @@ const PageRenderer = connect(
(props) => {
const Page = props?.route
const { embed } = props.queryObject
const { doInitHelia } = props
useEffect(() => {
props.doInitHelia()
}, [props])
doInitHelia()
}, [doInitHelia])

return (
<>
Expand Down
17,617 changes: 10,151 additions & 7,466 deletions package-lock.json

Large diffs are not rendered by default.

37 changes: 18 additions & 19 deletions package.json
Expand Up @@ -118,29 +118,28 @@
"storybook:build": "storybook build"
},
"dependencies": {
"@chainsafe/libp2p-gossipsub": "^8.0.0",
"@chainsafe/libp2p-noise": "^12.0.0",
"@chainsafe/libp2p-yamux": "^4.0.2",
"@ipld/car": "^5.1.1",
"@ipld/dag-cbor": "^9.0.1",
"@ipld/dag-json": "^10.0.1",
"@ipld/dag-pb": "^4.0.3",
"@libp2p/delegated-content-routing": "^4.0.3",
"@libp2p/delegated-peer-routing": "^4.0.6",
"@chainsafe/libp2p-gossipsub": "^10.1.1",
"@chainsafe/libp2p-noise": "^13.0.5",
"@chainsafe/libp2p-yamux": "^5.0.3",
"@ipld/car": "^5.2.4",
"@ipld/dag-cbor": "^9.0.6",
"@ipld/dag-json": "^10.1.5",
"@ipld/dag-pb": "^4.0.6",
"@libp2p/delegated-content-routing": "^4.0.11",
"@libp2p/delegated-peer-routing": "^4.0.14",
"@libp2p/ipni-content-routing": "^1.0.1",
"@loadable/component": "^5.14.1",
"@tableflip/react-inspector": "^2.3.0",
"cytoscape": "^3.18.1",
"cytoscape-dagre": "^2.3.2",
"filesize": "^6.1.0",
"helia": "^1.1.2",
"ipfs-unixfs": "^4.0.1",
"helia": "^2.1.0",
"ipfs-unixfs": "^11.1.0",
"ipld-git": "^0.6.6",
"ipld-raw": "7.0.0",
"kubo-rpc-client": "^3.0.1",
"libp2p": "^0.45.1",
"libp2p": "^0.46.21",
"milliseconds": "^1.0.3",
"multiformats": "^11.0.2",
"multiformats": "^12.1.3",
"react-joyride": "^2.3.0",
"stream-to-it": "^0.2.4",
"web-encoding": "^1.1.5"
Expand All @@ -157,9 +156,9 @@
"@esbuild-plugins/esm-externals": "^0.1.2",
"@esbuild-plugins/node-globals-polyfill": "^0.2.3",
"@esbuild-plugins/node-modules-polyfill": "^0.2.2",
"@helia/interface": "^1.0.0",
"@libp2p/bootstrap": "^8.0.0",
"@libp2p/websockets": "^6.0.1",
"@helia/interface": "^2.1.0",
"@libp2p/bootstrap": "^9.0.12",
"@libp2p/websockets": "^7.0.13",
"@rollup/plugin-commonjs": "^25.0.0",
"@rollup/plugin-json": "^6.0.0",
"@rollup/plugin-node-resolve": "^15.0.2",
Expand Down Expand Up @@ -197,7 +196,7 @@
"dag-jose": "^4.0.0",
"datastore-core": "^9.1.1",
"eslint": "^8.41.0",
"eslint-config-ipfs": "^4.0.3",
"eslint-config-ipfs": "^6.0.0",
"eslint-config-react-app": "^7.0.1",
"eslint-config-standard": "^17.0.0",
"eslint-config-standard-react": "^13.0.0",
Expand All @@ -217,7 +216,7 @@
"interface-datastore": "^8.2.0",
"intl-messageformat": "^10.3.5",
"ipfs-core": "^0.18.0",
"ipfs-css": "^1.3.0",
"ipfs-css": "^1.4.0",
"jsdom": "^22.1.0",
"npm-run-all": "^4.1.5",
"prop-types": "^15.8.1",
Expand Down
2 changes: 1 addition & 1 deletion src/bundles/explore.js
Expand Up @@ -40,7 +40,7 @@ const makeBundle = () => {
localPath,
nodes,
pathBoundaries
} = await resolveIpldPath(store.selectHelia(), store.selectKuboClient(), cid, rest)
} = await resolveIpldPath(store.selectHelia(), cid, rest)

return {
path,
Expand Down
34 changes: 14 additions & 20 deletions src/bundles/helia.ts
@@ -1,25 +1,23 @@
/* globals globalThis */
import { type Helia } from '@helia/interface'
import { create as createKuboClient, type IPFSHTTPClient } from 'kubo-rpc-client'

import initHelia from '../lib/init-helia'
import initHelia from '../lib/init-helia.js'
import type { KuboGatewayOptions } from '../types.d.js'

interface HeliaBundleState {
apiOpts: Record<string, string>
kuboGatewayOptions: KuboGatewayOptions
instance: Helia | null
error: Error | null
kuboClient: IPFSHTTPClient | null
}

const defaultState: HeliaBundleState = {
apiOpts: {
kuboGatewayOptions: {
host: '127.0.0.1',
port: '5001',
port: '8080',
protocol: 'http'
},
instance: null,
error: null,
kuboClient: null
error: null
}

function getUserOpts (key: string): Record<string, unknown> {
Expand All @@ -42,8 +40,7 @@ const bundle = {
if (type === 'HELIA_INIT_FINISHED') {
return Object.assign({}, state, {
instance: payload.instance ?? state.instance,
kuboClient: payload.kuboClient ?? state.kuboClient,
apiOpts: payload.apiOpts ?? state.apiOpts,
kuboGatewayOptions: payload.kuboGatewayOptions ?? state.kuboGatewayOptions,
error: null
})
}
Expand All @@ -55,7 +52,6 @@ const bundle = {
return state
},

selectKuboClient: ({ helia }: { helia: HeliaBundleState }): IPFSHTTPClient | null => helia.kuboClient,
selectHelia: ({ helia }: { helia: HeliaBundleState }) => helia.instance,

selectHeliaReady: ({ helia }: { helia: HeliaBundleState }) => helia.instance !== null,
Expand All @@ -69,25 +65,23 @@ const bundle = {
doInitHelia: () => async ({ dispatch, getState }: any) => {

Check warning on line 65 in src/bundles/helia.ts

View workflow job for this annotation

GitHub Actions / js-test-and-release / check

Unexpected any. Specify a different type

Check warning on line 65 in src/bundles/helia.ts

View workflow job for this annotation

GitHub Actions / js-test-and-release / check

Unexpected any. Specify a different type

Check warning on line 65 in src/bundles/helia.ts

View workflow job for this annotation

GitHub Actions / js-test-and-release / test-node (macos-latest, lts/*)

Unexpected any. Specify a different type

Check warning on line 65 in src/bundles/helia.ts

View workflow job for this annotation

GitHub Actions / js-test-and-release / test-webkit (macos-latest, lts/*)

Unexpected any. Specify a different type

Check warning on line 65 in src/bundles/helia.ts

View workflow job for this annotation

GitHub Actions / js-test-and-release / test-webkit-webworker (macos-latest, lts/*)

Unexpected any. Specify a different type
dispatch({ type: 'HELIA_INIT_STARTED' })

const apiOpts = Object.assign(
const kuboGatewayOptions = Object.assign(
{},
getState().helia.apiOpts,
getUserOpts('ipfsApi')
getState().helia.kuboGatewayOptions,
getUserOpts('kuboGateway')
)
// TRY helia!

try {
console.info(
"🎛️ Customise your kubo-rpc-client opts by setting an `ipfsApi` value in localStorage. e.g. localStorage.setItem('ipfsApi', JSON.stringify({port: '1337'}))"
"🎛️ Customise your Kubo gateway opts by setting an `kuboGateway` value in localStorage. e.g. localStorage.setItem('kuboGateway', JSON.stringify({port: '1337'}))"
)
const kuboClient = createKuboClient(apiOpts)
console.time('HELIA_INIT')
const helia = await initHelia(kuboClient)
const helia = await initHelia(kuboGatewayOptions)
console.timeEnd('HELIA_INIT')
return dispatch({
type: 'HELIA_INIT_FINISHED',
payload: {
apiOpts,
kuboClient,
kuboGatewayOptions,
instance: helia,
provider: 'helia'
}
Expand Down
36 changes: 34 additions & 2 deletions src/components/object-info/ObjectInfo.jsx
Expand Up @@ -79,7 +79,39 @@ const DagNodeIcon = ({ type, ...props }) => (
</svg>
)

/**
* replace bigint or other non-JSON-serializable values with appropriate values for the react-inspector
* Note that data for some blocks (e.g. bafyreicnokmhmrnlp2wjhyk2haep4tqxiptwfrp2rrs7rzq7uk766chqvq) currently do not
* look like NormalizedDagNode['data']
*
* @param {import('../../types').NormalizedDagNode['data']} data
*/
const getObjectInspectorData = (data) => {
if (data == null) return data
if (data.blockSizes != null) {
data.blockSizes = data.blockSizes.map(Number)
}
return data
}

/**
* @param {object} props
* @param {import('react-i18next').TFunction} props.t
* @param {boolean} props.tReady
* @param {string} props.className
* @param {string} props.type
* @param {string} props.cid
* @param {string} props.localPath
* @param {bigint} props.size
* @param {import('../../types').NormalizedDagNode['data']} props.data
* @param {object[]} props.links
* @param {string} props.format
* @param {Function} props.onLinkClick
* @param {string} props.gatewayUrl
* @param {string} props.publicGatewayUrl
*/
const ObjectInfo = ({ t, tReady, className, type, cid, localPath, size, data, links, format, onLinkClick, gatewayUrl, publicGatewayUrl, ...props }) => {
if (!tReady) return null
const isUnixFs = format === 'unixfs' && data.type && ['directory', 'file'].some(x => x === data.type)
let nodeStyleType = type

Expand Down Expand Up @@ -130,7 +162,7 @@ const ObjectInfo = ({ t, tReady, className, type, cid, localPath, size, data, li
<div className='dt dt--fixed pt2'>
{/* eslint-disable-next-line jsx-a11y/label-has-associated-control */}
<label className='dtc silver tracked ttu f7' style={{ width: 48 }}>Size</label>
<div className='dtc truncate charcoal monospace'>{humansize(size)}</div>
<div className='dtc truncate charcoal monospace'>{humansize(Number(size))}</div>
</div>
)}
<div className='dt dt--fixed pt2'>
Expand All @@ -151,7 +183,7 @@ const ObjectInfo = ({ t, tReady, className, type, cid, localPath, size, data, li
? null
: (
<div className='pa3 mt2 bg-white f5 nl3 nr3 mh0-l'>
<ObjectInspector showMaxKeys={100} data={data} theme={objectInspectorTheme} expandPaths={toExpandPathsNotation(localPath)} />
<ObjectInspector showMaxKeys={100} data={getObjectInspectorData(data)} theme={objectInspectorTheme} expandPaths={toExpandPathsNotation(localPath)} />
</div>
)}
</div>
Expand Down
10 changes: 5 additions & 5 deletions src/global.d.ts
Expand Up @@ -3,16 +3,16 @@
*/
declare module 'stream-to-it' {
interface toIterable {
source: <T>(stream: ReadableStream<T>) => AsyncIterable<T>
source<T>(stream: ReadableStream<T>): AsyncIterable<T>
}
const toIterable: toIterable
export default toIterable
}

interface OldIpldFormat {
util: {
serialize: (obj: unknown) => Promise<Uint8Array>
deserialize: (bytes: Uint8Array) => Promise<unknown>
serialize(obj: unknown): Promise<Uint8Array>
deserialize(bytes: Uint8Array): Promise<unknown>

codec: number
defaultHashAlg: number
Expand All @@ -21,8 +21,8 @@ interface OldIpldFormat {
}
codec: number
resolver: {
resolve: (bytes: Uint8Array, path: string) => Promise<unknown>
tree: (bytes: Uint8Array) => Promise<unknown>
resolve(bytes: Uint8Array, path: string): Promise<unknown>
tree(bytes: Uint8Array): Promise<unknown>
}
defaultHashAlg: number
}
Expand Down
2 changes: 0 additions & 2 deletions src/lib/codec-importer.ts
Expand Up @@ -13,14 +13,12 @@ export default async function codecImporter<T extends CodecDataTypes = CodecData
case 'dag-cbor':
return import('@ipld/dag-cbor')
case 'dag-pb':
// @ts-expect-error - return types need normalizing
return import('@ipld/dag-pb')
case 'git-raw':
return {
decode: (await import('ipld-git')).default.util.deserialize
}
case 'raw':
// @ts-expect-error - return types need normalizing
return import('multiformats/codecs/raw')
case 'json':
return import('multiformats/codecs/json')
Expand Down
4 changes: 2 additions & 2 deletions src/lib/get-codec-for-cid.ts
Expand Up @@ -8,8 +8,8 @@ import { ensureLeadingSlash } from './helpers'
import type { ResolveType } from '../types'

interface CodecWrapper<DecodedType = any> {
decode: (bytes: Uint8Array) => DecodedType
resolve: (path: string, bytes: Uint8Array) => Promise<ResolveType<DecodedType>>
decode(bytes: Uint8Array): DecodedType
resolve(path: string, bytes: Uint8Array): Promise<ResolveType<DecodedType>>
}

interface DecodeFn<T = any> {
Expand Down

0 comments on commit c964a76

Please sign in to comment.