Skip to content
This repository has been archived by the owner on Aug 11, 2021. It is now read-only.

feat: new IPLD Format API #107

Merged
merged 1 commit into from
May 6, 2019
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
35 changes: 14 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,42 +72,34 @@ const file = {
size: 11
}

dagCBOR.util.serialize(file, (err, serialized) => {
if (err) {
throw err
}
const serialized = dagCBOR.util.serialize(file)
console.log(`Encoded as a ${serialized.length} byte Buffer`)

console.log(`Encoded as a ${serialized.length} byte Buffer`)

dagCBOR.util.deserialize(serialized, (err, node) => {
if (err) {
throw err
}

console.log('Decoded as:', node)
require('assert').deepEqual(node, file) // should match
})
})
const node = dagCBOR.util.deserialize(serialized)
console.log('Decoded as:', node)
require('assert').deepEqual(node, file) // should match

// → Encoded as a 22 byte Buffer
// → Decoded as: { name: 'hello.txt', size: 11 }
```

## API

### `dagCBOR.util.serialize(obj, callback)`
### `dagCBOR.util.serialize(obj)`

Encodes an object into IPLD CBOR form, replacing any CIDs found within the object to CBOR tags (with an id of `42`).

- `obj` (any): any object able to be serialized as CBOR
- `callback` (`Function`): function to be called when serialization is complete, arguments are: `error` and the serialized node if no error was encountered.

### `dagCBOR.util.deserialize(serialized, callback)`
Returns the serialized node.

### `dagCBOR.util.deserialize(serialized)`

Decodes an IPLD CBOR encoded representation, restoring any CBOR tags (id `42`) to CIDs.

- `serialized` (`Buffer` or `String`): a binary blob representing an IPLD CBOR encoded object.
- `callback` (`Function`): function to be called when deserialization is complete, arguments are: `error` and the deserialized object if no error was encountered.

Returns the deserialized object.

### `dagCBOR.util.configureDecoder([options])`

Expand All @@ -123,7 +115,7 @@ Possible values in the `options` argument are:

Calling `dagCBOR.util.configureDecoder()` with no arguments will reset to the default decoder `size`, `maxSize` and `tags`.

### `dagCBOR.util.cid(obj[, options,] callback)`
### `dagCBOR.util.cid(obj[, options,])`

Create a [CID](https://github.com/multiformats/js-cid) for the given unserialized object.

Expand All @@ -132,7 +124,8 @@ Create a [CID](https://github.com/multiformats/js-cid) for the given unserialize
* `hashAlg` (`String`): a [registered multicodec](https://github.com/multiformats/multicodec/blob/master/table.csv) hash algorithm.
* `hashLen` (`String`): an optional hash length
* `version` (`Number`): CID version number, defaults to `1`
- `callback` (`Function`): function to be called when the object is serialized and a CID is created from that serialized form, arguments are: `error` and the created CID if no error was encountered.

Returns a Promise with the created CID.

## Contribute

Expand Down
6 changes: 2 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,11 @@
"borc": "^2.1.0",
"cids": "~0.6.0",
"is-circular": "^1.0.2",
"multihashing-async": "~0.6.0",
"traverse": "~0.6.6"
"multicodec": "~0.5.0",
"multihashing-async": "~0.7.0"
},
"devDependencies": {
"aegir": "^18.2.0",
"async": "^2.6.2",
"bs58": "^4.0.1",
"chai": "^4.2.0",
"detect-node": "^2.0.4",
"dirty-chai": "^2.0.1",
Expand Down
2 changes: 2 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@

exports.util = require('./util.js')
exports.resolver = require('./resolver.js')
exports.codec = exports.util.codec
exports.defaultHashAlg = exports.util.defaultHashAlg
165 changes: 50 additions & 115 deletions src/resolver.js
Original file line number Diff line number Diff line change
@@ -1,135 +1,70 @@
'use strict'

const util = require('./util')
const traverse = require('traverse')
const CID = require('cids')

exports = module.exports

exports.multicodec = 'dag-cbor'
exports.defaultHashAlg = 'sha2-256'
const util = require('./util')

/*
* resolve: receives a path and a binary blob and returns the value on path,
* throw if not possible. `binaryBlob` is CBOR encoded data.
/**
* Resolves a path within a CBOR block.
*
* Returns the value or a link and the partial mising path. This way the
* IPLD Resolver can fetch the link and continue to resolve.
*
* @param {Buffer} binaryBlob - Binary representation of a CBOR block
* @param {string} [path='/'] - Path that should be resolved
* @returns {Object} result - Result of the path it it was resolved successfully
* @returns {*} result.value - Value the path resolves to
* @returns {string} result.remainderPath - If the path resolves half-way to a
* link, then the `remainderPath` is the part after the link that can be used
* for further resolving
*/
exports.resolve = (binaryBlob, path, callback) => {
if (typeof path === 'function') {
callback = path
path = undefined
}

util.deserialize(binaryBlob, (err, node) => {
if (err) {
return callback(err)
exports.resolve = (binaryBlob, path) => {
let node = util.deserialize(binaryBlob)

const parts = path.split('/').filter(Boolean)
while (parts.length) {
const key = parts.shift()
if (node[key] === undefined) {
throw new Error(`Object has no property '${key}'`)
}

// root

if (!path || path === '/') {
return callback(null, {
node = node[key]
if (CID.isCID(node)) {
return {
value: node,
remainderPath: ''
})
}

// within scope

const parts = path.split('/')
const val = traverse(node).get(parts)

if (val !== undefined) {
return callback(null, {
value: val,
remainderPath: ''
})
}

// out of scope
let value
const len = parts.length

for (let i = 0; i < len; i++) {
const partialPath = parts.shift()

if (Array.isArray(node) && !Buffer.isBuffer(node)) {
value = node[Number(partialPath)]
} if (node[partialPath]) {
value = node[partialPath]
} else {
// can't traverse more
if (!value) {
return callback(new Error('path not available at root'))
} else {
parts.unshift(partialPath)
return callback(null, {
value: value,
remainderPath: parts.join('/')
})
}
remainderPath: parts.join('/')
}
node = value
}
})
}

function flattenObject (obj, delimiter) {
delimiter = delimiter || '/'

if (Object.keys(obj).length === 0) {
return []
}

return traverse(obj).reduce(function (acc, x) {
if (CID.isCID(x)) {
this.update(undefined)
}
const path = this.path.join(delimiter)

if (path !== '') {
acc.push({ path: path, value: x })
}
return acc
}, [])
return {
value: node,
remainderPath: ''
}
}

/*
* tree: returns a flattened array with paths: values of the project. options
* are option (i.e. nestness)
*/
exports.tree = (binaryBlob, options, callback) => {
if (typeof options === 'function') {
callback = options
options = undefined
const traverse = function * (node, path) {
// Traverse only objects and arrays
if (Buffer.isBuffer(node) || CID.isCID(node) || typeof node === 'string' ||
node === null) {
rvagg marked this conversation as resolved.
Show resolved Hide resolved
return
}
for (const item of Object.keys(node)) {
const nextpath = path === undefined ? item : path + '/' + item
yield nextpath
yield * traverse(node[item], nextpath)
}

options = options || {}

util.deserialize(binaryBlob, (err, node) => {
if (err) {
return callback(err)
}
const flat = flattenObject(node)
const paths = flat.map((el) => el.path)

callback(null, paths)
})
}

exports.isLink = (binaryBlob, path, callback) => {
exports.resolve(binaryBlob, path, (err, result) => {
if (err) {
return callback(err)
}

if (result.remainderPath.length > 0) {
return callback(new Error('path out of scope'))
}
/**
* Return all available paths of a block.
*
* @generator
* @param {Buffer} binaryBlob - Binary representation of a CBOR block
* @yields {string} - A single path
*/
exports.tree = function * (binaryBlob) {
const node = util.deserialize(binaryBlob)

if (CID.isCID(result.value)) {
callback(null, result.value)
} else {
callback(null, false)
}
})
yield * traverse(node)
}
Loading