Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat!: replace ipld-dag-pb with @ipld/dag-pb #330

Merged
merged 21 commits into from
Oct 5, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
engine-strict=true
16 changes: 16 additions & 0 deletions custom-jest-env.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
const Environment = require('jest-environment-jsdom')

module.exports = class CustomTestEnvironment extends Environment {
async setup () {
await super.setup()
if (typeof this.global.TextEncoder === 'undefined') {
/**
* Fix TextDecoder and TextEncoder not being defined in Jest
* @see https://github.com/jsdom/jsdom/issues/2524#issuecomment-736672511
*/
const { TextEncoder, TextDecoder } = require('util')
this.global.TextEncoder = TextEncoder
this.global.TextDecoder = TextDecoder
}
}
}
1,546 changes: 728 additions & 818 deletions package-lock.json

Large diffs are not rendered by default.

11 changes: 6 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -111,12 +111,13 @@
"start": "cross-env NODE_ENV=production babel src -d dist --copy-files --ignore '**/*.test.js' --watch",
"storybook": "start-storybook -p 9009 --static-dir public",
"storybook:build": "build-storybook -c .storybook --static-dir public --output-dir build",
"test": "react-scripts test --env=jsdom"
"test": "react-scripts test --env=./custom-jest-env.js --transformIgnorePatterns '/node_modules/(?!(multiformats/cid)/)'"
},
"dependencies": {
"@babel/cli": "^7.13.14",
"@ipld/car": "^4.1.0",
"@ipld/dag-json": "^8.0.9",
"@ipld/dag-cbor": "^7.0.2",
"@ipld/dag-json": "^8.0.10",
"@loadable/component": "^5.14.1",
"@tableflip/react-inspector": "^2.3.0",
"blockcodec-to-ipld-format": "^2.0.0",
Expand All @@ -125,13 +126,12 @@
"filesize": "^6.1.0",
"ipfs-unixfs": "^4.0.1",
"ipld": "0.29.0",
"ipld-dag-cbor": "0.18.0",
"ipld-ethereum": "6.0.0",
"ipld-git": "0.6.4",
"ipld-raw": "7.0.0",
"milliseconds": "^1.0.3",
"multibase": "4.0.2",
"multiformats": "^9.6.4",
"multiformats": "^9.7.0",
"multihashes": "^4.0.3",
"react-joyride": "^2.3.0",
"stream-to-it": "^0.2.4"
Expand All @@ -143,6 +143,7 @@
"@babel/preset-react": "^7.13.13",
"@commitlint/cli": "^16.2.3",
"@commitlint/config-conventional": "^16.2.1",
"@ipld/dag-pb": "^2.1.17",
"@semantic-release/changelog": "^6.0.1",
"@semantic-release/commit-analyzer": "^9.0.2",
"@semantic-release/git": "^10.0.1",
Expand All @@ -155,6 +156,7 @@
"@storybook/addon-links": "^5.3.21",
"@storybook/addons": "^5.3.21",
"@storybook/react": "^5.3.21",
"@types/jest": "^28.1.4",
"babel-eslint": "10.0.3",
"cross-env": "^6.0.0",
"eslint": "^6.5.1",
Expand All @@ -173,7 +175,6 @@
"i18next-localstorage-backend": "3.1.3",
"intl-messageformat": "^9.12.0",
"ipfs-css": "^1.3.0",
"ipld-dag-pb": "0.22.2",
"react": "^16.14.0",
"react-dom": "^16.14.0",
"react-helmet": "^5.2.1",
Expand Down
4 changes: 2 additions & 2 deletions src/bundles/explore.js
Original file line number Diff line number Diff line change
Expand Up @@ -182,8 +182,8 @@ function painfullyCompatibleBlockService (ipfs) {
async function getIpld () {
const ipldDeps = await Promise.all([
import(/* webpackChunkName: "ipld" */ 'ipld'),
import(/* webpackChunkName: "ipld" */ 'ipld-dag-cbor'),
import(/* webpackChunkName: "ipld" */ 'ipld-dag-pb'),
import(/* webpackChunkName: "ipld" */ '@ipld/dag-cbor'),
import(/* webpackChunkName: "ipld" */ '@ipld/dag-pb'),
import(/* webpackChunkName: "ipld" */ 'ipld-git'),
import(/* webpackChunkName: "ipld" */ 'ipld-raw'),
import(/* webpackChunkName: "ipld" */ 'ipld-ethereum')
Expand Down
43 changes: 38 additions & 5 deletions src/lib/cid.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,51 @@
import CID from 'cids'
import { CID } from 'multiformats'
import * as dagCbor from '@ipld/dag-cbor'
import * as dagPb from '@ipld/dag-pb'
SgtPooki marked this conversation as resolved.
Show resolved Hide resolved
import * as dagJson from '@ipld/dag-json'

export function toCidOrNull (value) {
/**
* @template {string} Prefix
* @param {any} value
* @param {import('multiformats/bases/interface').MultibaseDecoder<Prefix>} [base]
* @returns
*/
export function toCidOrNull (value, base) {
if (!value) return null
try {
return new CID(value)
return CID.asCID(value) || CID.parse(value, base)
} catch (err) {
console.log('Failed to parse CID', value, err)
return null
}
}

/**
* This function is deprecated, use `getCodeOrNull` instead.
*
* `cid.codec` is deprecated, use integer "code" property instead
*
* @param {any} value
* @returns {string}
* @deprecated
*/
export function getCodecOrNull (value) {
const cid = toCidOrNull(value)
return cid ? cid.codec : null

if (cid == null) return null
switch (cid.code) {
case dagCbor.code:
return dagCbor.name
case dagJson.code:
return dagJson.name
case dagPb.code:
return dagPb.name
default:
return null
}
}

export function getCodeOrNull (value) {
const cid = toCidOrNull(value)
return cid ? cid.code : null
}

export function toCidStrOrNull (value) {
Expand Down
163 changes: 163 additions & 0 deletions src/lib/cid.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
/* global describe it expect */
// @ts-check
import { toCidOrNull, getCodecOrNull, getCodeOrNull, toCidStrOrNull } from './cid'
import * as multiformats from 'multiformats'
import * as dagCbor from '@ipld/dag-cbor'
import * as dagPb from '@ipld/dag-pb'
import crypto from 'crypto'

const { CID, hasher, bytes } = multiformats

export const sha256 = hasher.from({
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sha256 is not exported from multiformats correctly even though it says I can import it from multiformats/hashes/sha2

image

Copy link
Member

@rvagg rvagg Jul 12, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

import { sha256 } from 'multiformats/hashes/sha2'

if the docs are wrong, a PR would be appreciated, but the export is there and we actively use it in many places

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That import doesn't even work :(

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what runtime or bundler is this using? perhaps something needs to be updated here to support proper export maps?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

react@16.14 is probably the culprit here, I don't think they'd nailed down their export maps functionality quite right back then but I don't know why this import specifically would fail tbh

but I bet upgrading react here would be a major pain too! so go with whatever works I suppose.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm assuming this has something to do with migrating js-ipfs stuff to esm, but the GUI packages all still being cjs

name: 'sha2-256',
code: 0x12,
encode: (input) => bytes.coerce(crypto.createHash('sha256').update(input).digest())
})
/**
*
* @template {number} Code
* @param {any} value
* @param {import('multiformats/block').BlockEncoder<Code, any>} codec
* @param {import('multiformats/hashes/interface').MultihashHasher<Code>} hasher
* @returns
*/
const createCID = async (value, codec, hasher) => {
try {
const digest = await hasher.digest(codec.encode(value))
return CID.create(1, codec.code, digest)
} catch (err) {
console.log('Failed to create CID', value, err)
return null
}
}

const cidStr = 'bafyreiddymapg5zcpma3iu4wingqvois6jirucn5776wdsyg5f3f65v75a'

describe('cid.js', () => {
describe('toCidOrNull', () => {
it('Returns CID instance for valid CID string', () => {
// arrange
// act
const cid = toCidOrNull(cidStr)
// assert
expect(cid).toBeInstanceOf(CID)
expect(cid?.code).toBe(dagCbor.code)
})

it('Returns null for invalid CID string', () => {
// arrange
const cidStr = 'abc123'
// act
const cid = toCidOrNull(cidStr)
// assert
expect(cid).not.toBeInstanceOf(CID)
expect(cid).toBe(null)
})
})

describe('getCodecOrNull', () => {
it('CID with `dag-cbor` codec returns `dag-cbor`', async () => {
// arrange
const cid = await createCID({ foo: 'abc' }, dagCbor, sha256)
expect(cid).toBeInstanceOf(CID)
// act
const codec = getCodecOrNull(cid)
// assert
expect(codec).toBe('dag-cbor')
})

it('CID with `dag-pb` codec returns `dag-pb`', async () => {
// arrange
const cid = await createCID({
Data: new Uint8Array(Buffer.from('hello world')),
Links: []
}, dagPb, sha256)
expect(cid).toBeInstanceOf(CID)
// act
const codec = getCodecOrNull(cid)
// assert
expect(codec).toBe('dag-pb')
})

it('CID with any other codec returns null', async () => {
// arrange
/**
* @type {import('multiformats/block').BlockEncoder<99999, any>}
*/
const fakeCodec = {
name: 'fake',
code: 99999,
encode: () => new Uint8Array(Buffer.from('NOTHING'))
}

const cid = await createCID({ foo: 'abc' }, fakeCodec, sha256)
expect(cid).toBeInstanceOf(CID)
// act
const codec = getCodecOrNull(cid)
// assert
expect(codec).toBe(null)
})
})

describe('getCodeOrNull', () => {
it(`CID with 'dag-cbor' codec returns '${dagCbor.code}'`, async () => {
// arrange
const cid = await createCID({ foo: 'abc' }, dagCbor, sha256)
expect(cid).toBeInstanceOf(CID)
// act
const codec = getCodeOrNull(cid)
// assert
expect(codec).toBe(dagCbor.code)
})

it(`CID with 'dag-pb' codec returns '${dagPb.code}'`, async () => {
// arrange
const cid = await createCID({
Data: new Uint8Array(Buffer.from('hello world')),
Links: []
}, dagPb, sha256)
expect(cid).toBeInstanceOf(CID)
// act
const codec = getCodeOrNull(cid)
// assert
expect(codec).toBe(dagPb.code)
})

it('CID with any other codec returns the correct code', async () => {
// arrange
/**
* @type {import('multiformats/block').BlockEncoder<99999, any>}
*/
const fakeCodec = {
name: 'fake',
code: 99999,
encode: () => new Uint8Array(Buffer.from('NOTHING'))
}

const cid = await createCID({ foo: 'abc' }, fakeCodec, sha256)
expect(cid).toBeInstanceOf(CID)
// act
const codec = getCodeOrNull(cid)
// assert
expect(codec).toBe(fakeCodec.code)
})
})

describe('toCidStrOrNull', () => {
it('Returns correct string for valid CID', () => {
// arrange
// act
const tempCidStr = toCidStrOrNull(toCidOrNull(cidStr))
// assert
expect(tempCidStr).toBe(cidStr)
})

it('Returns null for invalid CID string', () => {
// arrange
// act
const tempCidStr = toCidStrOrNull('abc123')
// assert
expect(tempCidStr).toBe(null)
})
})
})
Loading