{"action":"created","comment":{"url":"https://api.github.com/repos/ipfs/js-ipfs/pulls/comments/137469373","pull_request_review_id":61137894,"id":137469373,"diff_hunk":"@@ -0,0 +1,275 @@\n+'use strict'\n+\n+const multihashes = require('multihashes')\n+const CID = require('cids')\n+const protobuf = require('protocol-buffers')\n+const crypto = require('crypto')\n+const fnv1a = require('fnv1a')\n+const dagPB = require('ipld-dag-pb')\n+const DAGNode = dagPB.DAGNode\n+const DAGLink = dagPB.DAGLink\n+const varint = require('varint')\n+\n+const emptyKeyHash = 'QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n'\n+const emptyKey = multihashes.fromB58String(emptyKeyHash)\n+const defaultFanout = 256\n+const maxItems = 8192\n+\n+// Protobuf interface\n+const pbSchema = (\n+  // from go-ipfs/pin/internal/pb/header.proto\n+  'message Set { ' +\n+    // 1 for now\n+    'optional uint32 version = 1; ' +\n+    // how many of the links are subtrees\n+    'optional uint32 fanout = 2; ' +\n+    // hash seed for subtree selection, a random number\n+    'optional fixed32 seed = 3; ' +\n+  '}'\n+)\n+const pb = protobuf(pbSchema)\n+function readHeader (rootNode) {\n+  // rootNode.data should be a buffer of the format:\n+  // < varint(headerLength) | header | itemData... >\n+  const rootData = rootNode.data\n+  const hdrLength = varint.decode(rootData)\n+  const vBytes = varint.decode.bytes\n+  if (vBytes <= 0) {\n+    return { err: 'Invalid Set header length' }\n+  }\n+  if (vBytes + hdrLength > rootData.length) {\n+    return { err: 'Impossibly large set header length' }\n+  }\n+  const hdrSlice = rootData.slice(vBytes, hdrLength + vBytes)\n+  const header = pb.Set.decode(hdrSlice)\n+  if (header.version !== 1) {\n+    return { err: 'Unsupported Set version: ' + header.version }\n+  }\n+  if (header.fanout > rootNode.links.length) {\n+    return { err: 'Impossibly large fanout' }\n+  }\n+  return {\n+    header: header,\n+    data: rootData.slice(hdrLength + vBytes)\n+  }\n+}\n+\n+exports = module.exports = function (dag) {\n+  const pinSet = {\n+    // should this be part of `object` API?\n+    hasChild: (root, childhash, callback, _links, _checked, _seen) => {\n+      // callback (err, has)\n+      if (callback.fired) { return }\n+      if (typeof childhash === 'object') {\n+        childhash = multihashes.toB58String(childhash)\n+      }\n+      _links = _links || root.links.length\n+      _checked = _checked || 0\n+      _seen = _seen || {}\n+\n+      if (!root.links.length && _links === _checked) {\n+        // all nodes have been checked\n+        return callback(null, false)\n+      }\n+      root.links.forEach((link) => {\n+        const bs58link = multihashes.toB58String(link.multihash)\n+        if (bs58link === childhash) {\n+          callback.fired = true\n+          return callback(null, true)\n+        }\n+        dag.get(new CID(link.multihash), (err, res) => {\n+          if (err) {\n+            callback.fired = true\n+            return callback(err)\n+          }\n+          // don't check the same links twice\n+          if (bs58link in _seen) { return }\n+          _seen[bs58link] = true\n+\n+          _checked++\n+          _links += res.value.links.length\n+          pinSet.hasChild(res.value, childhash, callback, _links, _checked, _seen)\n+        })\n+      })\n+    },\n+\n+    storeSet: (keys, logInternalKey, callback) => {\n+      // callback (err, rootNode)\n+      const items = keys.map((key) => {\n+        return {\n+          key: key,\n+          data: null\n+        }\n+      })\n+      pinSet.storeItems(items, logInternalKey, (err, rootNode) => {\n+        if (err) { return callback(err) }\n+        const opts = { cid: new CID(rootNode.multihash) }\n+        dag.put(rootNode, opts, (err, cid) => {\n+          if (err) { return callback(err) }\n+          logInternalKey(rootNode.multihash)\n+          callback(null, rootNode)\n+        })\n+      })\n+    },\n+\n+    storeItems: (items, logInternalKey, callback, _subcalls, _done) => {\n+      // callback (err, rootNode)\n+      const seed = crypto.randomBytes(4).readUInt32LE(0, true)\n+      const pbHeader = pb.Set.encode({\n+        version: 1,\n+        fanout: defaultFanout,\n+        seed: seed\n+      })\n+      let rootData = Buffer.concat([\n+        new Buffer(varint.encode(pbHeader.length)), pbHeader\n+      ])\n+      let rootLinks = []\n+      for (let i = 0; i < defaultFanout; i++) {\n+        rootLinks.push(new DAGLink('', 1, emptyKey))\n+      }\n+      logInternalKey(emptyKey)\n+\n+      if (items.length <= maxItems) {\n+        // the items will fit in a single root node\n+        const itemLinks = []\n+        const itemData = []\n+        const indices = []\n+        for (let i = 0; i < items.length; i++) {\n+          itemLinks.push(new DAGLink('', 1, items[i].key))\n+          itemData.push(items[i].data || new Buffer(0))\n+          indices.push(i)\n+        }\n+        indices.sort((a, b) => {\n+          const x = Buffer.compare(itemLinks[a].multihash, itemLinks[b].multihash)\n+          if (x) { return x }\n+          return (a < b ? -1 : 1)\n+        })\n+        const sortedLinks = indices.map((i) => { return itemLinks[i] })\n+        const sortedData = indices.map((i) => { return itemData[i] })\n+        rootLinks = rootLinks.concat(sortedLinks)\n+        rootData = Buffer.concat([rootData].concat(sortedData))\n+        DAGNode.create(rootData, rootLinks, (err, rootNode) => {\n+          if (err) { return callback(err) }\n+          return callback(null, rootNode)\n+        })\n+      } else {\n+        // need to split up the items into multiple root nodes\n+        // (using go-ipfs \"wasteful but simple\" approach for consistency)\n+        _subcalls = _subcalls || 0\n+        _done = _done || 0\n+        const hashed = {}\n+        const hashFn = (seed, key) => {\n+          const buf = new Buffer(4)\n+          buf.writeUInt32LE(seed, 0)\n+          const data = Buffer.concat([\n+            buf, new Buffer(multihashes.toB58String(key))\n+          ])\n+          return fnv1a(data.toString('binary'))\n+        }\n+        // items will be distributed among `defaultFanout` bins\n+        for (let i = 0; i < items.length; i++) {\n+          let h = hashFn(seed, items[i].key) % defaultFanout\n+          hashed[h] = hashed[h] || []\n+          hashed[h].push(items[i])\n+        }\n+        const storeItemsCb = (err, child) => {\n+          if (callback.fired) { return }\n+          if (err) {\n+            callback.fired = true\n+            return callback(err)\n+          }\n+          dag.put(child, (err) => {\n+            if (callback.fired) { return }\n+            if (err) {\n+              callback.fired = true\n+              return callback(err)\n+            }\n+            logInternalKey(child.multihash)\n+            rootLinks[this.h] = new DAGLink(\n+              '', child.size, child.multihash\n+            )\n+            _done++\n+            if (_done === _subcalls) {\n+              // all finished\n+              DAGNode.create(rootData, rootLinks, (err, rootNode) => {\n+                if (err) { return callback(err) }\n+                return callback(null, rootNode)\n+              })\n+            }\n+          })\n+        }\n+        _subcalls += Object.keys(hashed).length\n+        for (let h in hashed) {\n+          if (hashed.hasOwnProperty(h)) {\n+            pinSet.storeItems(\n+              hashed[h],\n+              logInternalKey,\n+              storeItemsCb.bind({h: h}),\n+              _subcalls,\n+              _done\n+            )\n+          }\n+        }\n+      }\n+    },\n+\n+    loadSet: (rootNode, name, logInternalKey, callback) => {\n+      // callback (err, keys)\n+      const link = rootNode.links.filter(l => l.name === name).pop()\n+      if (!link) { return callback('No link found with name ' + name) }\n+      logInternalKey(link.multihash)\n+      dag.get(new CID(link.multihash), (err, res) => {\n+        if (err) { return callback(err) }\n+        const keys = []\n+        const walkerFn = (link) => {\n+          keys.push(link.multihash)\n+        }\n+        pinSet.walkItems(res.value, walkerFn, logInternalKey, (err) => {\n+          if (err) { return callback(err) }\n+          return callback(null, keys)\n+        })\n+      })\n+    },\n+\n+    walkItems: (node, walkerFn, logInternalKey, callback) => {\n+      // callback (err)\n+      const h = readHeader(node)\n+      if (h.err) { return callback(h.err) }\n+      const fanout = h.header.fanout\n+      let subwalkCount = 0\n+      let finishedCount = 0\n+\n+      const walkCb = (err) => {\n+        if (err) { return callback(err) }\n+        finishedCount++\n+        if (subwalkCount === finishedCount) {\n+          return callback()\n+        }\n+      }\n+\n+      for (let i = 0; i < node.links.length; i++) {\n+        const link = node.links[i]\n+        if (i >= fanout) {\n+          // item link\n+          walkerFn(link, i, h.data)\n+        } else {\n+          // fanout link\n+          logInternalKey(link.multihash)\n+          if (!emptyKey.equals(link.multihash)) {\n+            subwalkCount++\n+            dag.get(new CID(link.multihash), (err, res) => {\n+              if (err) { return callback(err) }\n+              pinSet.walkItems(\n+                res.value, walkerFn, logInternalKey, walkCb\n+              )\n+            })\n+          }\n+        }\n+      }\n+      if (!subwalkCount) {\n+        return callback()\n+      }\n+    }\n+  }\n+  return pinSet\n+}","path":"src/core/components/pin-set.js","position":275,"original_position":275,"commit_id":"727478413fcace71a09b85347fd04b3c1493fd84","original_commit_id":"727478413fcace71a09b85347fd04b3c1493fd84","user":{"login":"diasdavid","id":1211152,"avatar_url":"https://avatars0.githubusercontent.com/u/1211152?v=4","gravatar_id":"","url":"https://api.github.com/users/diasdavid","html_url":"https://github.com/diasdavid","followers_url":"https://api.github.com/users/diasdavid/followers","following_url":"https://api.github.com/users/diasdavid/following{/other_user}","gists_url":"https://api.github.com/users/diasdavid/gists{/gist_id}","starred_url":"https://api.github.com/users/diasdavid/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/diasdavid/subscriptions","organizations_url":"https://api.github.com/users/diasdavid/orgs","repos_url":"https://api.github.com/users/diasdavid/repos","events_url":"https://api.github.com/users/diasdavid/events{/privacy}","received_events_url":"https://api.github.com/users/diasdavid/received_events","type":"User","site_admin":false},"body":"There is a ton of good work! 👌🏽\r\n\r\nAt the current moment, seems a bit complex, might be good to factor it out into separate files and individually test each.","created_at":"2017-09-07T07:52:18Z","updated_at":"2017-09-07T07:59:51Z","html_url":"https://github.com/ipfs/js-ipfs/pull/107#discussion_r137469373","pull_request_url":"https://api.github.com/repos/ipfs/js-ipfs/pulls/107","author_association":"OWNER","_links":{"self":{"href":"https://api.github.com/repos/ipfs/js-ipfs/pulls/comments/137469373"},"html":{"href":"https://github.com/ipfs/js-ipfs/pull/107#discussion_r137469373"},"pull_request":{"href":"https://api.github.com/repos/ipfs/js-ipfs/pulls/107"}}},"pull_request":{"url":"https://api.github.com/repos/ipfs/js-ipfs/pulls/107","id":65078309,"html_url":"https://github.com/ipfs/js-ipfs/pull/107","diff_url":"https://github.com/ipfs/js-ipfs/pull/107.diff","patch_url":"https://github.com/ipfs/js-ipfs/pull/107.patch","issue_url":"https://api.github.com/repos/ipfs/js-ipfs/issues/107","number":107,"state":"open","locked":false,"title":"feat: pin","user":{"login":"AdamStone","id":6251611,"avatar_url":"https://avatars3.githubusercontent.com/u/6251611?v=4","gravatar_id":"","url":"https://api.github.com/users/AdamStone","html_url":"https://github.com/AdamStone","followers_url":"https://api.github.com/users/AdamStone/followers","following_url":"https://api.github.com/users/AdamStone/following{/other_user}","gists_url":"https://api.github.com/users/AdamStone/gists{/gist_id}","starred_url":"https://api.github.com/users/AdamStone/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/AdamStone/subscriptions","organizations_url":"https://api.github.com/users/AdamStone/orgs","repos_url":"https://api.github.com/users/AdamStone/repos","events_url":"https://api.github.com/users/AdamStone/events{/privacy}","received_events_url":"https://api.github.com/users/AdamStone/received_events","type":"User","site_admin":false},"body":"- [x] core\n  - [x] add\n  - [x] rm\n  - [x] ls\n  - [x] isPinned\n  - [x] isPinnedWithType\n  - [x] directKeys / recursiveKeys / internalKeys\n  - [x] flush (persist keys to datastore)\n  - [x] load (restore keys from datastore)\n- [x] cli\n  - [x] add\n  - [x] rm\n  - [x] ls\n- [x] http\n  - [x] add\n  - [x] rm\n  - [x] ls\n\nUpdated core interface to align with [interface-ipfs-core](https://github.com/ipfs/interface-ipfs-core), still need to update cli and http.\n","created_at":"2016-04-03T19:45:21Z","updated_at":"2017-09-07T07:59:51Z","closed_at":null,"merged_at":null,"merge_commit_sha":"6f0b9dcca8c9e974b043eed592df77257e97a058","assignee":null,"assignees":[],"requested_reviewers":[],"milestone":null,"commits_url":"https://api.github.com/repos/ipfs/js-ipfs/pulls/107/commits","review_comments_url":"https://api.github.com/repos/ipfs/js-ipfs/pulls/107/comments","review_comment_url":"https://api.github.com/repos/ipfs/js-ipfs/pulls/comments{/number}","comments_url":"https://api.github.com/repos/ipfs/js-ipfs/issues/107/comments","statuses_url":"https://api.github.com/repos/ipfs/js-ipfs/statuses/727478413fcace71a09b85347fd04b3c1493fd84","head":{"label":"AdamStone:feature/pin","ref":"feature/pin","sha":"727478413fcace71a09b85347fd04b3c1493fd84","user":{"login":"AdamStone","id":6251611,"avatar_url":"https://avatars3.githubusercontent.com/u/6251611?v=4","gravatar_id":"","url":"https://api.github.com/users/AdamStone","html_url":"https://github.com/AdamStone","followers_url":"https://api.github.com/users/AdamStone/followers","following_url":"https://api.github.com/users/AdamStone/following{/other_user}","gists_url":"https://api.github.com/users/AdamStone/gists{/gist_id}","starred_url":"https://api.github.com/users/AdamStone/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/AdamStone/subscriptions","organizations_url":"https://api.github.com/users/AdamStone/orgs","repos_url":"https://api.github.com/users/AdamStone/repos","events_url":"https://api.github.com/users/AdamStone/events{/privacy}","received_events_url":"https://api.github.com/users/AdamStone/received_events","type":"User","site_admin":false},"repo":{"id":54295227,"name":"js-ipfs","full_name":"AdamStone/js-ipfs","owner":{"login":"AdamStone","id":6251611,"avatar_url":"https://avatars3.githubusercontent.com/u/6251611?v=4","gravatar_id":"","url":"https://api.github.com/users/AdamStone","html_url":"https://github.com/AdamStone","followers_url":"https://api.github.com/users/AdamStone/followers","following_url":"https://api.github.com/users/AdamStone/following{/other_user}","gists_url":"https://api.github.com/users/AdamStone/gists{/gist_id}","starred_url":"https://api.github.com/users/AdamStone/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/AdamStone/subscriptions","organizations_url":"https://api.github.com/users/AdamStone/orgs","repos_url":"https://api.github.com/users/AdamStone/repos","events_url":"https://api.github.com/users/AdamStone/events{/privacy}","received_events_url":"https://api.github.com/users/AdamStone/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/AdamStone/js-ipfs","description":"IPFS implementation in JavaScript","fork":true,"url":"https://api.github.com/repos/AdamStone/js-ipfs","forks_url":"https://api.github.com/repos/AdamStone/js-ipfs/forks","keys_url":"https://api.github.com/repos/AdamStone/js-ipfs/keys{/key_id}","collaborators_url":"https://api.github.com/repos/AdamStone/js-ipfs/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/AdamStone/js-ipfs/teams","hooks_url":"https://api.github.com/repos/AdamStone/js-ipfs/hooks","issue_events_url":"https://api.github.com/repos/AdamStone/js-ipfs/issues/events{/number}","events_url":"https://api.github.com/repos/AdamStone/js-ipfs/events","assignees_url":"https://api.github.com/repos/AdamStone/js-ipfs/assignees{/user}","branches_url":"https://api.github.com/repos/AdamStone/js-ipfs/branches{/branch}","tags_url":"https://api.github.com/repos/AdamStone/js-ipfs/tags","blobs_url":"https://api.github.com/repos/AdamStone/js-ipfs/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/AdamStone/js-ipfs/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/AdamStone/js-ipfs/git/refs{/sha}","trees_url":"https://api.github.com/repos/AdamStone/js-ipfs/git/trees{/sha}","statuses_url":"https://api.github.com/repos/AdamStone/js-ipfs/statuses/{sha}","languages_url":"https://api.github.com/repos/AdamStone/js-ipfs/languages","stargazers_url":"https://api.github.com/repos/AdamStone/js-ipfs/stargazers","contributors_url":"https://api.github.com/repos/AdamStone/js-ipfs/contributors","subscribers_url":"https://api.github.com/repos/AdamStone/js-ipfs/subscribers","subscription_url":"https://api.github.com/repos/AdamStone/js-ipfs/subscription","commits_url":"https://api.github.com/repos/AdamStone/js-ipfs/commits{/sha}","git_commits_url":"https://api.github.com/repos/AdamStone/js-ipfs/git/commits{/sha}","comments_url":"https://api.github.com/repos/AdamStone/js-ipfs/comments{/number}","issue_comment_url":"https://api.github.com/repos/AdamStone/js-ipfs/issues/comments{/number}","contents_url":"https://api.github.com/repos/AdamStone/js-ipfs/contents/{+path}","compare_url":"https://api.github.com/repos/AdamStone/js-ipfs/compare/{base}...{head}","merges_url":"https://api.github.com/repos/AdamStone/js-ipfs/merges","archive_url":"https://api.github.com/repos/AdamStone/js-ipfs/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/AdamStone/js-ipfs/downloads","issues_url":"https://api.github.com/repos/AdamStone/js-ipfs/issues{/number}","pulls_url":"https://api.github.com/repos/AdamStone/js-ipfs/pulls{/number}","milestones_url":"https://api.github.com/repos/AdamStone/js-ipfs/milestones{/number}","notifications_url":"https://api.github.com/repos/AdamStone/js-ipfs/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/AdamStone/js-ipfs/labels{/name}","releases_url":"https://api.github.com/repos/AdamStone/js-ipfs/releases{/id}","deployments_url":"https://api.github.com/repos/AdamStone/js-ipfs/deployments","created_at":"2016-03-20T01:36:31Z","updated_at":"2016-03-31T11:36:48Z","pushed_at":"2017-09-02T02:38:21Z","git_url":"git://github.com/AdamStone/js-ipfs.git","ssh_url":"git@github.com:AdamStone/js-ipfs.git","clone_url":"https://github.com/AdamStone/js-ipfs.git","svn_url":"https://github.com/AdamStone/js-ipfs","homepage":"http://ipfs.io/","size":2651,"stargazers_count":1,"watchers_count":1,"language":"JavaScript","has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":1,"default_branch":"master"}},"base":{"label":"ipfs:master","ref":"master","sha":"a85cf708faf8354ebd5349c187f7f5ecc528ef81","user":{"login":"ipfs","id":10536621,"avatar_url":"https://avatars0.githubusercontent.com/u/10536621?v=4","gravatar_id":"","url":"https://api.github.com/users/ipfs","html_url":"https://github.com/ipfs","followers_url":"https://api.github.com/users/ipfs/followers","following_url":"https://api.github.com/users/ipfs/following{/other_user}","gists_url":"https://api.github.com/users/ipfs/gists{/gist_id}","starred_url":"https://api.github.com/users/ipfs/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ipfs/subscriptions","organizations_url":"https://api.github.com/users/ipfs/orgs","repos_url":"https://api.github.com/users/ipfs/repos","events_url":"https://api.github.com/users/ipfs/events{/privacy}","received_events_url":"https://api.github.com/users/ipfs/received_events","type":"Organization","site_admin":false},"repo":{"id":20312497,"name":"js-ipfs","full_name":"ipfs/js-ipfs","owner":{"login":"ipfs","id":10536621,"avatar_url":"https://avatars0.githubusercontent.com/u/10536621?v=4","gravatar_id":"","url":"https://api.github.com/users/ipfs","html_url":"https://github.com/ipfs","followers_url":"https://api.github.com/users/ipfs/followers","following_url":"https://api.github.com/users/ipfs/following{/other_user}","gists_url":"https://api.github.com/users/ipfs/gists{/gist_id}","starred_url":"https://api.github.com/users/ipfs/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ipfs/subscriptions","organizations_url":"https://api.github.com/users/ipfs/orgs","repos_url":"https://api.github.com/users/ipfs/repos","events_url":"https://api.github.com/users/ipfs/events{/privacy}","received_events_url":"https://api.github.com/users/ipfs/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/ipfs/js-ipfs","description":"IPFS implementation in JavaScript","fork":false,"url":"https://api.github.com/repos/ipfs/js-ipfs","forks_url":"https://api.github.com/repos/ipfs/js-ipfs/forks","keys_url":"https://api.github.com/repos/ipfs/js-ipfs/keys{/key_id}","collaborators_url":"https://api.github.com/repos/ipfs/js-ipfs/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/ipfs/js-ipfs/teams","hooks_url":"https://api.github.com/repos/ipfs/js-ipfs/hooks","issue_events_url":"https://api.github.com/repos/ipfs/js-ipfs/issues/events{/number}","events_url":"https://api.github.com/repos/ipfs/js-ipfs/events","assignees_url":"https://api.github.com/repos/ipfs/js-ipfs/assignees{/user}","branches_url":"https://api.github.com/repos/ipfs/js-ipfs/branches{/branch}","tags_url":"https://api.github.com/repos/ipfs/js-ipfs/tags","blobs_url":"https://api.github.com/repos/ipfs/js-ipfs/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/ipfs/js-ipfs/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/ipfs/js-ipfs/git/refs{/sha}","trees_url":"https://api.github.com/repos/ipfs/js-ipfs/git/trees{/sha}","statuses_url":"https://api.github.com/repos/ipfs/js-ipfs/statuses/{sha}","languages_url":"https://api.github.com/repos/ipfs/js-ipfs/languages","stargazers_url":"https://api.github.com/repos/ipfs/js-ipfs/stargazers","contributors_url":"https://api.github.com/repos/ipfs/js-ipfs/contributors","subscribers_url":"https://api.github.com/repos/ipfs/js-ipfs/subscribers","subscription_url":"https://api.github.com/repos/ipfs/js-ipfs/subscription","commits_url":"https://api.github.com/repos/ipfs/js-ipfs/commits{/sha}","git_commits_url":"https://api.github.com/repos/ipfs/js-ipfs/git/commits{/sha}","comments_url":"https://api.github.com/repos/ipfs/js-ipfs/comments{/number}","issue_comment_url":"https://api.github.com/repos/ipfs/js-ipfs/issues/comments{/number}","contents_url":"https://api.github.com/repos/ipfs/js-ipfs/contents/{+path}","compare_url":"https://api.github.com/repos/ipfs/js-ipfs/compare/{base}...{head}","merges_url":"https://api.github.com/repos/ipfs/js-ipfs/merges","archive_url":"https://api.github.com/repos/ipfs/js-ipfs/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/ipfs/js-ipfs/downloads","issues_url":"https://api.github.com/repos/ipfs/js-ipfs/issues{/number}","pulls_url":"https://api.github.com/repos/ipfs/js-ipfs/pulls{/number}","milestones_url":"https://api.github.com/repos/ipfs/js-ipfs/milestones{/number}","notifications_url":"https://api.github.com/repos/ipfs/js-ipfs/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/ipfs/js-ipfs/labels{/name}","releases_url":"https://api.github.com/repos/ipfs/js-ipfs/releases{/id}","deployments_url":"https://api.github.com/repos/ipfs/js-ipfs/deployments","created_at":"2014-05-30T00:31:06Z","updated_at":"2017-09-07T07:52:00Z","pushed_at":"2017-09-07T06:27:00Z","git_url":"git://github.com/ipfs/js-ipfs.git","ssh_url":"git@github.com:ipfs/js-ipfs.git","clone_url":"https://github.com/ipfs/js-ipfs.git","svn_url":"https://github.com/ipfs/js-ipfs","homepage":"https://ipfs.io","size":3638,"stargazers_count":1119,"watchers_count":1119,"language":"JavaScript","has_issues":true,"has_projects":false,"has_downloads":true,"has_wiki":false,"has_pages":true,"forks_count":164,"mirror_url":null,"open_issues_count":63,"forks":164,"open_issues":63,"watchers":1119,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/ipfs/js-ipfs/pulls/107"},"html":{"href":"https://github.com/ipfs/js-ipfs/pull/107"},"issue":{"href":"https://api.github.com/repos/ipfs/js-ipfs/issues/107"},"comments":{"href":"https://api.github.com/repos/ipfs/js-ipfs/issues/107/comments"},"review_comments":{"href":"https://api.github.com/repos/ipfs/js-ipfs/pulls/107/comments"},"review_comment":{"href":"https://api.github.com/repos/ipfs/js-ipfs/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/ipfs/js-ipfs/pulls/107/commits"},"statuses":{"href":"https://api.github.com/repos/ipfs/js-ipfs/statuses/727478413fcace71a09b85347fd04b3c1493fd84"}},"author_association":"NONE"}}
{"actor":{"display_login":"diasdavid"}}