diff --git a/packages/ipfs-unixfs-exporter/package.json b/packages/ipfs-unixfs-exporter/package.json index b4b9adc5..7381637a 100644 --- a/packages/ipfs-unixfs-exporter/package.json +++ b/packages/ipfs-unixfs-exporter/package.json @@ -136,6 +136,7 @@ }, "dependencies": { "@ipld/dag-cbor": "^9.0.0", + "@ipld/dag-json": "^10.1.7", "@ipld/dag-pb": "^4.0.0", "@multiformats/murmur3": "^2.0.0", "err-code": "^3.0.1", diff --git a/packages/ipfs-unixfs-exporter/src/resolvers/dag-json.ts b/packages/ipfs-unixfs-exporter/src/resolvers/dag-json.ts new file mode 100644 index 00000000..d701501d --- /dev/null +++ b/packages/ipfs-unixfs-exporter/src/resolvers/dag-json.ts @@ -0,0 +1,67 @@ +import * as dagJson from '@ipld/dag-json' +import errCode from 'err-code' +import { CID } from 'multiformats/cid' +import type { Resolver } from '../index.js' + +const resolve: Resolver = async (cid, name, path, toResolve, resolve, depth, blockstore, options) => { + const block = await blockstore.get(cid, options) + const object = dagJson.decode(block) + let subObject = object + let subPath = path + + while (toResolve.length > 0) { + const prop = toResolve[0] + + if (prop in subObject) { + // remove the bit of the path we have resolved + toResolve.shift() + subPath = `${subPath}/${prop}` + + const subObjectCid = CID.asCID(subObject[prop]) + if (subObjectCid != null) { + return { + entry: { + type: 'object', + name, + path, + cid, + node: block, + depth, + size: BigInt(block.length), + content: async function * () { + yield object + } + }, + next: { + cid: subObjectCid, + name: prop, + path: subPath, + toResolve + } + } + } + + subObject = subObject[prop] + } else { + // cannot resolve further + throw errCode(new Error(`No property named ${prop} found in dag-json node ${cid}`), 'ERR_NO_PROP') + } + } + + return { + entry: { + type: 'object', + name, + path, + cid, + node: block, + depth, + size: BigInt(block.length), + content: async function * () { + yield object + } + } + } +} + +export default resolve diff --git a/packages/ipfs-unixfs-exporter/src/resolvers/index.ts b/packages/ipfs-unixfs-exporter/src/resolvers/index.ts index 6fffc8cc..93038d32 100644 --- a/packages/ipfs-unixfs-exporter/src/resolvers/index.ts +++ b/packages/ipfs-unixfs-exporter/src/resolvers/index.ts @@ -1,9 +1,11 @@ import * as dagCbor from '@ipld/dag-cbor' +import * as dagJson from '@ipld/dag-json' import * as dagPb from '@ipld/dag-pb' import errCode from 'err-code' import * as raw from 'multiformats/codecs/raw' import { identity } from 'multiformats/hashes/identity' import dagCborResolver from './dag-cbor.js' +import dagJsonResolver from './dag-json.js' import identifyResolver from './identity.js' import rawResolver from './raw.js' import dagPbResolver from './unixfs-v1/index.js' @@ -13,6 +15,7 @@ const resolvers: Record = { [dagPb.code]: dagPbResolver, [raw.code]: rawResolver, [dagCbor.code]: dagCborResolver, + [dagJson.code]: dagJsonResolver, [identity.code]: identifyResolver } diff --git a/packages/ipfs-unixfs-exporter/test/exporter.spec.ts b/packages/ipfs-unixfs-exporter/test/exporter.spec.ts index af28be2e..f1c5fd95 100644 --- a/packages/ipfs-unixfs-exporter/test/exporter.spec.ts +++ b/packages/ipfs-unixfs-exporter/test/exporter.spec.ts @@ -1,6 +1,7 @@ /* eslint-env mocha */ import * as dagCbor from '@ipld/dag-cbor' +import * as dagJson from '@ipld/dag-json' import * as dagPb from '@ipld/dag-pb' import { expect } from 'aegir/chai' import { MemoryBlockstore } from 'blockstore-core' @@ -1008,6 +1009,39 @@ describe('exporter', () => { return expect(first(exported.content())).to.eventually.deep.equal(node) }) + it('errors when exporting a non-existent key from a json node', async () => { + const node = { + foo: 'bar' + } + + const jsonBlock = dagJson.encode(node) + const cid = CID.createV1(dagJson.code, await sha256.digest(jsonBlock)) + await block.put(cid, jsonBlock) + + try { + await exporter(`${cid}/baz`, block) + } catch (err: any) { + expect(err.code).to.equal('ERR_NO_PROP') + } + }) + + it('exports a json node', async () => { + const node = { + foo: 'bar' + } + + const jsonBlock = dagJson.encode(node) + const cid = CID.createV1(dagJson.code, await sha256.digest(jsonBlock)) + await block.put(cid, jsonBlock) + const exported = await exporter(`${cid}`, block) + + if (exported.type !== 'object') { + throw new Error('Unexpected type') + } + + return expect(first(exported.content())).to.eventually.deep.equal(node) + }) + it('errors when exporting a node with no resolver', async () => { const cid = CID.create(1, 0x78, CID.parse('zdj7WkRPAX9o9nb9zPbXzwG7JEs78uyhwbUs8JSUayB98DWWY').multihash)