Skip to content

Commit

Permalink
feat: remove buffer dependency (#460)
Browse files Browse the repository at this point in the history
* test: add more coverage for normalise-dag-node.ts

* feat: remove dependency on Buffer

* fix: setExplorePath handles url check properly

* fix: more type safety
  • Loading branch information
SgtPooki authored Nov 1, 2024
1 parent 7fc8cf8 commit f4905d2
Show file tree
Hide file tree
Showing 13 changed files with 334 additions and 91 deletions.
1 change: 1 addition & 0 deletions .aegir.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ export default {

// vite stuff
'rollup-plugin-node-polyfills',
'@vitest/coverage-v8',

// typescript plugins
'typescript-plugin-css-modules'
Expand Down
3 changes: 0 additions & 3 deletions .storybook/preview.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
import { type Preview } from '@storybook/react';
import { Buffer } from 'buffer'

globalThis.Buffer = Buffer

// import CSS files
import 'ipfs-css'
Expand Down
4 changes: 0 additions & 4 deletions dev/devPage.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
/* globals globalThis */
import 'ipfs-css'
import { Buffer } from 'buffer'
import React, { type MouseEvent, useEffect, useState } from 'react'
import { createRoot } from 'react-dom/client'
import { I18nextProvider, useTranslation } from 'react-i18next'
import 'tachyons'
import i18n from '../src/i18n.js'
import { ExplorePage, StartExploringPage, IpldExploreForm, IpldCarExploreForm, ExploreProvider, HeliaProvider, useExplore, useHelia } from '../src/index.js'

globalThis.Buffer = globalThis.Buffer ?? Buffer

const HeaderComponent: React.FC = () => {
const activeColor = 'navy 0-100'
const inActiveColor = 'navy o-50'
Expand Down
97 changes: 97 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@
"test": "run-s test:unit test:storybook:ci test:consumer",
"test:node": "run-s test:unit",
"test:unit": "vitest run --environment=node",
"test:unit:cov": "vitest run --environment=node --coverage",
"storybook": "storybook dev",
"storybook:build": "storybook build",
"test:consumer": "run-s test:consumer:webui test:consumer:ipld.io",
Expand Down Expand Up @@ -232,6 +233,7 @@
"@types/react-helmet": "^6.1.11",
"@types/react-virtualized": "^9.21.30",
"@vitejs/plugin-react": "^4.3.2",
"@vitest/coverage-v8": "^2.1.3",
"aegir": "^44.1.4",
"concurrently": "^9.0.1",
"eslint-config-ipfs": "^7.0.6",
Expand Down
7 changes: 0 additions & 7 deletions src/lib/browser-shims.js

This file was deleted.

2 changes: 1 addition & 1 deletion src/lib/cid.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { CID, type MultibaseDecoder } from 'multiformats/cid'
/**
* Converts a value to a CID or returns null if it cannot be converted.
*/
export function toCidOrNull <T extends string> (value: CID | string | null, base?: MultibaseDecoder<T> | undefined): CID | null {
export function toCidOrNull <T extends string> (value: CID | string | null | unknown, base?: MultibaseDecoder<T> | undefined): CID | null {
if (value == null) return null
try {
return CID.asCID(value) ?? CID.parse(value as string, base)
Expand Down
5 changes: 4 additions & 1 deletion src/lib/extract-info.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ import { type CID } from 'multiformats'
import { decodeCid } from '../components/cid-info/decode-cid.js'
import getCodecNameFromCode from './get-codec-name-from-code'

const toHex = (bytes: Uint8Array): string => Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength).toString('hex').toUpperCase()
const toHex = (bytes: Uint8Array): string => Array.from(new Uint8Array(bytes.buffer, bytes.byteOffset, bytes.byteLength))
.map(byte => byte.toString(16).padStart(2, '0'))
.join('')
.toUpperCase()

export interface ExtractedInfo {
base: string
Expand Down
42 changes: 32 additions & 10 deletions src/lib/normalise-dag-node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import * as dagCbor from '@ipld/dag-cbor'
import * as dagPb from '@ipld/dag-pb'
import { UnixFS } from 'ipfs-unixfs'
import { type CID } from 'multiformats/cid'
import { toCidOrNull, getCodeOrNull, toCidStrOrNull } from './cid.js'
import { isTruthy } from './helpers.js'
import type { NormalizedDagPbNodeFormat, CodecType, NormalizedDagNode, NormalizedDagLink, dagNode } from '../types.js'
Expand All @@ -25,7 +26,7 @@ function isDagPbNode (node: dagNode | PBNode, cid: string): node is PBNode {
* @param {string} cidStr - the cid string passed to `ipfs.dag.get`
* @returns {import('../types').NormalizedDagNode}
*/
export default function normaliseDagNode (node: dagNode | PBNode, cidStr: string): NormalizedDagNode {
export function normaliseDagNode (node: dagNode | PBNode, cidStr: string): NormalizedDagNode {
const code = getCodeOrNull(cidStr)
if (isDagPbNode(node, cidStr)) {
return normaliseDagPb(node, cidStr, dagPb.code)
Expand All @@ -34,6 +35,7 @@ export default function normaliseDagNode (node: dagNode | PBNode, cidStr: string
// @ts-expect-error - todo: resolve node type error
return normaliseDagCbor(node, cidStr, code ?? dagCbor.code)
}
export default normaliseDagNode

/**
* Normalize links and add type info. Add unixfs info where available
Expand Down Expand Up @@ -120,18 +122,41 @@ export function normaliseDagCbor (data: NormalizedDagNode['data'], cid: string,
}
}

type PlainObjectOrArray = Record<string, unknown> | unknown[] | string

function isPlainObjectOrArray (obj: unknown): obj is PlainObjectOrArray {
return (
obj !== null &&
typeof obj === 'object' &&
!(obj instanceof ArrayBuffer) &&
!ArrayBuffer.isView(obj) &&
!(typeof obj === 'string')
)
}

type DagCborNodeObject = Record<string, unknown> & { '/': string | CID | null }

/**
* This should be called after `isPlainObjectOrArray` to avoid type errors.
*/
function isDagCborNodeObject (obj: PlainObjectOrArray): obj is DagCborNodeObject {
return Object.keys(obj).length === 1 && (obj as Record<string, unknown>)['/'] != null
}

/**
* A valid IPLD link in a dag-cbor node is an object with single "/" property.
*/
export function findAndReplaceDagCborLinks (obj: unknown, sourceCid: string, path: string = ''): NormalizedDagLink[] {
if (obj == null || typeof obj !== 'object' || Buffer.isBuffer(obj) || typeof obj === 'string') {
if (!isPlainObjectOrArray(obj)) {
return []
}

// FIXME: remove as any cast
const cid = toCidOrNull(obj as any)
if (cid != null) {
return [{ path, source: sourceCid, target: cid.toString(), size: BigInt(0), index: 0 }]
const cid = toCidOrNull(obj)
if (typeof obj === 'string' || cid != null) {
if (cid != null) {
return [{ path, source: sourceCid, target: cid.toString(), size: BigInt(0), index: 0 }]
}
return []
}

if (Array.isArray(obj)) {
Expand All @@ -146,22 +171,19 @@ export function findAndReplaceDagCborLinks (obj: unknown, sourceCid: string, pat
const keys = Object.keys(obj)

// Support older `{ "/": Buffer } style links until all the IPLD formats are updated.
if (keys.length === 1 && keys[0] === '/') {
// @ts-expect-error - todo: resolve this type error
if (isDagCborNodeObject(obj)) {
const targetCid = toCidOrNull(obj['/'])

if (targetCid == null) return []

const target = targetCid.toString()
// @ts-expect-error - todo: resolve this type error
obj['/'] = target

return [{ path, source: sourceCid, target, size: BigInt(0), index: 0 }]
}

if (keys.length > 0) {
return keys
// @ts-expect-error - todo: resolve this type error
.map(key => findAndReplaceDagCborLinks(obj[key], sourceCid, isTruthy(path) ? `${path}/${key}` : `${key}`))
.reduce((a, b) => a.concat(b))
.filter(a => Boolean(a))
Expand Down
Loading

0 comments on commit f4905d2

Please sign in to comment.