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

Commit

Permalink
feat: new IPLD Format API
Browse files Browse the repository at this point in the history
BREAKING CHANGE: The API is now async/await based

There are numerous changes, the most significant one is that the API
is no longer callback based, but it using async/await.

For the full new API please see the [IPLD Formats spec].

[IPLD Formats spec]: https://github.com/ipld/interface-ipld-format
  • Loading branch information
vmx committed May 6, 2019
1 parent ba28199 commit cfc8519
Show file tree
Hide file tree
Showing 9 changed files with 306 additions and 543 deletions.
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) {
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

0 comments on commit cfc8519

Please sign in to comment.