{"action":"created","comment":{"url":"https://api.github.com/repos/nodejs/node-eps/pulls/comments/69721967","id":69721967,"diff_hunk":"@@ -0,0 +1,309 @@\n+| Title  | AsyncWrap API |\n+|--------|---------------|\n+| Author | @trevnorris   |\n+| Status | DRAFT         |\n+| Date   | 2016-04-05    |\n+\n+## Description\n+\n+Since its initial introduction with the `AsyncListener` API, `AsyncWrap` has\n+slowly evolved to ensure a generalized API that would serve as a solid base for\n+module authors who wished to add events to the life cycle of the event loop.\n+The API presented below aims to accomplish this.\n+\n+## API\n+\n+### Overview\n+\n+```js\n+// Standard way of requiring a module. Snake case follows core module\n+// convention.\n+const async_wrap = require('async_wrap');\n+\n+// List of asynchronous providers (e.g. CRYPTO or TCPWRAP). Can be used for\n+// trace filtering of events.\n+const providers = async_wrap.Providers;\n+\n+// init() is called during object construction. The object handle being\n+// constructed can be accessed as \"this\". Because init() is called during\n+// construction it is not safe to blindly inspect the constructed object at the\n+// moment. Though we are currently investigating how to make this safe.\n+function init(id, type, parentId) { }\n+\n+// pre() is called just before the handle's callback is called. It can be\n+// called 0-N times for handles (e.g. TCPWRAP), and should be called exactly 1\n+// time for requests (e.g. FSREQWRAP).\n+function pre(id) { }\n+\n+// post() is called just after the handle's callback has finished, and will be\n+// called regardless whether the handle's callback threw. If the handle's\n+// callback did throw then hasThrown will be true.\n+function post(id, hasThrown) { }\n+\n+// destroy() is called when an AsyncWrap instance is destroyed. In cases like\n+// HTTPPARSER where the resource is reused, or timers where the handle is only\n+// a JS object, destroy() will be triggered manually soon after post() has\n+// completed. This is the only callback that does not receive the context as\n+// \"this\". Because it is called while the associated class is being destructed\n+// it is unsafe to operate on the object.\n+function destroy(id) { }\n+\n+// Add a set of hooks to be called during the lifetime of asynchronous events.\n+// createHook() returns an instance of the AsyncHook constructor that can\n+// control how those hooks are used. For example, enabling or disabling their\n+// execution.\n+asyncHook = async_wrap.createHook({ init, pre, post, destroy });\n+\n+// Allow callbacks of this AsyncHook instance to fire. This is not an implicit\n+// action after running createHook(), and must be explicitly run to being\n+// executing callbacks.\n+asyncHook.enable();\n+\n+// Disable listening for new asynchronous events. Though this will not prevent\n+// callbacks from firing on asynchronous chains that have already run within\n+// the scope of an enabled AsyncHook instance.\n+asyncHook.disable();\n+\n+// Enable hooks for the current synchronous execution scope. This will ensure\n+// the hooks are not in effect in case of multiple returns, or if an exception\n+// is thrown.\n+asyncHook.scope();\n+```\n+\n+### `async_wrap`\n+\n+The object returned from `require('async_wrap')`.\n+\n+#### `async_wrap.createHook(hooks)`\n+\n+* `hooks` {Object}\n+* Return: {AsyncHook}\n+\n+Create a new `AsyncHook` instance that controls a given set of hooks. The four\n+optional hooks are `init`/`pre`/`post`/`destroy`.\n+\n+#### `async_wrap.Providers`\n+\n+List of all providers that may trigger the `init` callback.\n+\n+### Constructor: `AsyncHook`\n+\n+The `AsyncHook` constructor returns an instance that contains information about\n+the callbacks that are to fire during specific asynchronous events in the\n+lifetime of the event loop. The focal point of these calls centers around the\n+lifetime of `AsyncWrap`. These callbacks will also be called to emulate the\n+lifetime of handles and requests that do not fit this model. For example,\n+`HTTPPARSER` instances are recycled to improve performance. So the `destroy()`\n+callback would be called manually after a connection is done using it, just\n+before it's placed back into the unused resource pool.\n+\n+All callbacks are optional. So if only resource cleanup needs to be tracked\n+then only the `destroy()` callback needs to be passed.\n+\n+**Error Handling**: If any callback throws the application will print the\n+stack trace and exit. The exit path does follow that of any uncaught exception,\n+so any `'exit'` callbacks will fire. Unless the application is run with\n+`--abort-on-uncaught-exception`. In which case a stack trace will be printed\n+and the application will exit, leaving a core file.\n+\n+The reason for this behavior is that these callbacks are running at potentially\n+volatile points in an object's lifetime. For example during class construction\n+and destruction. Because of this, it is deemed necessary to bring down the\n+process quickly as to prevent an unintentional abort in the future. This is\n+subject to change in the future if a comprehensive analysis is performed to\n+ensure an exception can follow the normal control flow without unintentional\n+side effects.\n+\n+#### `asyncHook.enable()`\n+\n+Enable the callbacks for a given `AsyncHook` instance. Once a callback fires on\n+an asynchronous event they will continue to fire for all nested asynchronous\n+events. Even after the instance has been disabled.\n+\n+Callbacks are not implicitly enabled after an instance is created. Reason for\n+this is to not make any assumptions about the user's use case. Since\n+constructing the `asyncHook` during startup but not using it until later is a\n+perfectly reasonable use case. This API is meant to err on the side of\n+requiring explicit instructions from the user.\n+\n+#### `asyncHook.disable()`\n+\n+Disable the callbacks for a given `AsyncHook` instance. Doing this will prevent\n+the `init()`, etc., calls from firing for any new roots of asynchronous call\n+stacks, but will not prevent existing asynchronous call stacks that have\n+already been captured by the `AsyncHook` instance from continuing to fire.\n+\n+While not part of the immediate development plan, it should be possible in the\n+future to allow selective tracking of asynchronous call stacks. The following\n+example demonstrates this:\n+\n+```js\n+const async_wrap = require('async_wrap');\n+const net = require('net');\n+\n+// Pretend init, pre, post are all defined\n+const asyncHook = async_wrap.createHook({ init, pre, post });\n+asyncHook.enable();\n+\n+net.createServer((c) => {\n+  // Only want to follow connections that match IP range.\n+  if (ipRangeRegExp.test(c.address().address))\n+    asyncHook.disable();\n+}).listen(PORT);\n+```\n+\n+At which point no further calls to the hooks on that instance will be made for\n+that asynchronous branch.\n+\n+#### `asyncHook.scope()`\n+\n+Enable capture of asynchronous events until the current synchronous code\n+execution has completed. This is basically a small amount of sugar to prevent\n+accidentally forgetting to `disable()` a set of hooks in a function with\n+multiple returns. Also in case an error is thrown and caught by a domain or\n+`uncaughtException`.\n+\n+### Hook Callbacks\n+\n+Key events in the lifetime of asynchronous events have been categorized into\n+four areas. On instantiation, before/after the callback is called and when the\n+instance is destructed. For cases where resources are reused, instantiation and\n+destructor calls are emulated.\n+\n+#### `init(id, type, parentId)`\n+\n+* `id` {Number}\n+* `type` {String}\n+* `parentId` {Number}\n+\n+Called when a class is constructed that has the possibility to trigger an\n+asynchronous event. This does mean the instance _will_ trigger a\n+`pre()`/`post()` event. Only that the possibility exists.\n+\n+The `this` of each call is that of the object being constructed. While core\n+attempts to make accessing properties of these handles safe at construction, it\n+is not guaranteed (especially if third-party modules are involved) that\n+everything can be safely accessed. So it is not recommended to blindly\n+iterate over the object's properties. Despite this, it is important to give\n+access of the handle to the user so they can utilize tracking methods of their\n+choosing. For example:\n+\n+```js\n+const handleMap = new Map();\n+\n+function HandleInst(inst) {\n+  // Emulate performance.now()\n+  const t = process.hrtime();\n+  this.time = t[0] * 1e3 + t[1] / 1e6;\n+  this.inst = inst;\n+}\n+\n+function init(id) {\n+  handleMap.set(id, new HandleInst(this));\n+}\n+```\n+\n+Every instance is assigned a unique id. Taking advantage of the fraction space\n+in the 64-bit IEEE 754-2008 that ECMAScript defines as the **Number** type, the\n+number of id's that can be assigned are `2^53 - 1` (also defined as\n+`Number.MAX_SAFE_INTEGER`). At this size node can assign a new id every 100\n+nanoseconds and not run out for over 28 years. Because of this circumstance it\n+is not deemed necessary to find an alternative approach.\n+\n+The id of the parent in the async stack trace is also passed. This can be used\n+to create call graphs, trace resource usage/leakage, etc.\n+\n+#### `pre(id)`\n+\n+* `id` {Number}\n+\n+Called just before the return callback is called after completing an\n+asynchronous request. Or called on handles with events such as receiving a new\n+connection. For requests, such as `fs.open()`, this should be called exactly\n+once. For handles, such as a TCP server, this may be called 0-N times. Useful\n+for recording metrics or setting up state.\n+\n+#### `post(id, didThrow)`\n+\n+* `id` {Number}\n+* `didThrow` {Boolean}\n+\n+Called immediately after the return callback is completed. If the callback\n+threw but was caught by a domain or `uncaughtException` `didThrow` will be set\n+to `true`. If the callback threw but was not caught then the process will exit\n+immediately without calling `post()`.\n+\n+#### `destroy(id)`\n+\n+* `id` {Number}\n+\n+Called either when the class destructor is run, or if the resource is marked as\n+free. The destructor will usually run when explicitly called (the case for\n+handles) or when a request has completed. In at least one case this will be\n+triggered by GC. In the case of shared or cached resources, such as\n+`HTTPParser`, `destroy()` will be manually called when the TCP connection no\n+longer needs it.\n+\n+Because the callback is called during deconstruction of the class instance it\n+is not safe to access the JS handle during `destroy()` execution. Reason this\n+callback is important is to allow resource cleanup. While APIs like `WeakMap()`\n+are great, they are currently not iterable and not as performant. Taking an\n+above example, here is how `destroy` can be used for resource cleanup:\n+\n+```js\n+const handleMap = new Map();\n+\n+function HandleInst(inst) {\n+  // Emulate performance.now()\n+  const t = process.hrtime();\n+  this.time = t[0] * 1e3 + t[1] / 1e6;\n+  this.inst = inst;\n+}\n+\n+function init(id) {\n+  handleMap.set(id, new HandleInst(this));\n+}\n+\n+function destroy(id) {\n+  handleMap.delete(id);\n+}\n+```\n+\n+\n+## API Exceptions\n+\n+### net Client connection Event\n+\n+Technically the `pre()`/`post()` events of the `'connection'` event for\n+`net.Server` would place the server as the active id. Problem is that this is\n+not intuitive in how the asynchronous chain would propagate. So instead make\n+the client the active id for the duration of the `'connection'` callback.\n+\n+### Reused Resources\n+\n+Resources like `HTTPParser` are reused throughout the lifetime of the\n+application. This means node will have to synthesize the `init()` and\n+`destroy()` calls. Also the id on the class instance will need to be changed\n+every time the resource is acquired for use.\n+\n+For shared resources like `TimerWrap` this is not necessary since there is a\n+unique JS handle that will contain the unique id necessary for the calls.\n+\n+\n+## Notes\n+","path":"XXX-asyncwrap-api.md","position":405,"original_position":294,"commit_id":"9e95a3fd16f053e1fc0d59226a26bb7fbc552d30","original_commit_id":"762c337b9088b3df4d39ca15de154ee467eb6fa5","user":{"login":"rvagg","id":495647,"avatar_url":"https://avatars.githubusercontent.com/u/495647?v=3","gravatar_id":"","url":"https://api.github.com/users/rvagg","html_url":"https://github.com/rvagg","followers_url":"https://api.github.com/users/rvagg/followers","following_url":"https://api.github.com/users/rvagg/following{/other_user}","gists_url":"https://api.github.com/users/rvagg/gists{/gist_id}","starred_url":"https://api.github.com/users/rvagg/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rvagg/subscriptions","organizations_url":"https://api.github.com/users/rvagg/orgs","repos_url":"https://api.github.com/users/rvagg/repos","events_url":"https://api.github.com/users/rvagg/events{/privacy}","received_events_url":"https://api.github.com/users/rvagg/received_events","type":"User","site_admin":false},"body":"Need this information @trevnorris, whatever magic you have here. Also, we could work with NAN to make this easier, perhaps even by providing automatic support for existing users if that makes sense (i.e. `MakeCallback` is already heavily wrapped by NAN now so additional things could be put into it if possible).","created_at":"2016-07-06T12:38:36Z","updated_at":"2016-07-06T12:38:36Z","html_url":"https://github.com/nodejs/node-eps/pull/18#discussion_r69721967","pull_request_url":"https://api.github.com/repos/nodejs/node-eps/pulls/18","_links":{"self":{"href":"https://api.github.com/repos/nodejs/node-eps/pulls/comments/69721967"},"html":{"href":"https://github.com/nodejs/node-eps/pull/18#discussion_r69721967"},"pull_request":{"href":"https://api.github.com/repos/nodejs/node-eps/pulls/18"}}},"pull_request":{"url":"https://api.github.com/repos/nodejs/node-eps/pulls/18","id":68137447,"html_url":"https://github.com/nodejs/node-eps/pull/18","diff_url":"https://github.com/nodejs/node-eps/pull/18.diff","patch_url":"https://github.com/nodejs/node-eps/pull/18.patch","issue_url":"https://api.github.com/repos/nodejs/node-eps/issues/18","number":18,"state":"open","locked":false,"title":"AsyncWrap public API proposal","user":{"login":"trevnorris","id":289174,"avatar_url":"https://avatars.githubusercontent.com/u/289174?v=3","gravatar_id":"","url":"https://api.github.com/users/trevnorris","html_url":"https://github.com/trevnorris","followers_url":"https://api.github.com/users/trevnorris/followers","following_url":"https://api.github.com/users/trevnorris/following{/other_user}","gists_url":"https://api.github.com/users/trevnorris/gists{/gist_id}","starred_url":"https://api.github.com/users/trevnorris/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/trevnorris/subscriptions","organizations_url":"https://api.github.com/users/trevnorris/orgs","repos_url":"https://api.github.com/users/trevnorris/repos","events_url":"https://api.github.com/users/trevnorris/events{/privacy}","received_events_url":"https://api.github.com/users/trevnorris/received_events","type":"User","site_admin":false},"body":"After much investigation and communication this is the API that has\r\nsurfaced. Meant to be minimal, not impose any performance penalty to\r\ncore when not being used, and minimal impact when it is used, this\r\nshould serve public needs that have been expressed over the last two\r\nyears.\r\n\r\n@nodejs/ctc I'd like the initial review explicitly from the CTC before this is opened for too much external debate. Because experience has shown me that there will be suggestions/changes for those who want specific features and/or additions to suit their specific use case. Usually not taking the time to realize that this API is enough. They just need to write the additional code for the hooks.","created_at":"2016-04-27T22:43:35Z","updated_at":"2016-07-06T12:38:36Z","closed_at":null,"merged_at":null,"merge_commit_sha":"29eb83e2abf6e9081889aab89885991f8dab1f72","assignee":null,"assignees":[],"milestone":null,"commits_url":"https://api.github.com/repos/nodejs/node-eps/pulls/18/commits","review_comments_url":"https://api.github.com/repos/nodejs/node-eps/pulls/18/comments","review_comment_url":"https://api.github.com/repos/nodejs/node-eps/pulls/comments{/number}","comments_url":"https://api.github.com/repos/nodejs/node-eps/issues/18/comments","statuses_url":"https://api.github.com/repos/nodejs/node-eps/statuses/9e95a3fd16f053e1fc0d59226a26bb7fbc552d30","head":{"label":"nodejs:async-wrap-ep","ref":"async-wrap-ep","sha":"9e95a3fd16f053e1fc0d59226a26bb7fbc552d30","user":{"login":"nodejs","id":9950313,"avatar_url":"https://avatars.githubusercontent.com/u/9950313?v=3","gravatar_id":"","url":"https://api.github.com/users/nodejs","html_url":"https://github.com/nodejs","followers_url":"https://api.github.com/users/nodejs/followers","following_url":"https://api.github.com/users/nodejs/following{/other_user}","gists_url":"https://api.github.com/users/nodejs/gists{/gist_id}","starred_url":"https://api.github.com/users/nodejs/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/nodejs/subscriptions","organizations_url":"https://api.github.com/users/nodejs/orgs","repos_url":"https://api.github.com/users/nodejs/repos","events_url":"https://api.github.com/users/nodejs/events{/privacy}","received_events_url":"https://api.github.com/users/nodejs/received_events","type":"Organization","site_admin":false},"repo":{"id":48204104,"name":"node-eps","full_name":"nodejs/node-eps","owner":{"login":"nodejs","id":9950313,"avatar_url":"https://avatars.githubusercontent.com/u/9950313?v=3","gravatar_id":"","url":"https://api.github.com/users/nodejs","html_url":"https://github.com/nodejs","followers_url":"https://api.github.com/users/nodejs/followers","following_url":"https://api.github.com/users/nodejs/following{/other_user}","gists_url":"https://api.github.com/users/nodejs/gists{/gist_id}","starred_url":"https://api.github.com/users/nodejs/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/nodejs/subscriptions","organizations_url":"https://api.github.com/users/nodejs/orgs","repos_url":"https://api.github.com/users/nodejs/repos","events_url":"https://api.github.com/users/nodejs/events{/privacy}","received_events_url":"https://api.github.com/users/nodejs/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/nodejs/node-eps","description":"Node.js Enhancement Proposals for discussion on future API additions/changes to Node core","fork":false,"url":"https://api.github.com/repos/nodejs/node-eps","forks_url":"https://api.github.com/repos/nodejs/node-eps/forks","keys_url":"https://api.github.com/repos/nodejs/node-eps/keys{/key_id}","collaborators_url":"https://api.github.com/repos/nodejs/node-eps/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/nodejs/node-eps/teams","hooks_url":"https://api.github.com/repos/nodejs/node-eps/hooks","issue_events_url":"https://api.github.com/repos/nodejs/node-eps/issues/events{/number}","events_url":"https://api.github.com/repos/nodejs/node-eps/events","assignees_url":"https://api.github.com/repos/nodejs/node-eps/assignees{/user}","branches_url":"https://api.github.com/repos/nodejs/node-eps/branches{/branch}","tags_url":"https://api.github.com/repos/nodejs/node-eps/tags","blobs_url":"https://api.github.com/repos/nodejs/node-eps/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/nodejs/node-eps/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/nodejs/node-eps/git/refs{/sha}","trees_url":"https://api.github.com/repos/nodejs/node-eps/git/trees{/sha}","statuses_url":"https://api.github.com/repos/nodejs/node-eps/statuses/{sha}","languages_url":"https://api.github.com/repos/nodejs/node-eps/languages","stargazers_url":"https://api.github.com/repos/nodejs/node-eps/stargazers","contributors_url":"https://api.github.com/repos/nodejs/node-eps/contributors","subscribers_url":"https://api.github.com/repos/nodejs/node-eps/subscribers","subscription_url":"https://api.github.com/repos/nodejs/node-eps/subscription","commits_url":"https://api.github.com/repos/nodejs/node-eps/commits{/sha}","git_commits_url":"https://api.github.com/repos/nodejs/node-eps/git/commits{/sha}","comments_url":"https://api.github.com/repos/nodejs/node-eps/comments{/number}","issue_comment_url":"https://api.github.com/repos/nodejs/node-eps/issues/comments{/number}","contents_url":"https://api.github.com/repos/nodejs/node-eps/contents/{+path}","compare_url":"https://api.github.com/repos/nodejs/node-eps/compare/{base}...{head}","merges_url":"https://api.github.com/repos/nodejs/node-eps/merges","archive_url":"https://api.github.com/repos/nodejs/node-eps/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/nodejs/node-eps/downloads","issues_url":"https://api.github.com/repos/nodejs/node-eps/issues{/number}","pulls_url":"https://api.github.com/repos/nodejs/node-eps/pulls{/number}","milestones_url":"https://api.github.com/repos/nodejs/node-eps/milestones{/number}","notifications_url":"https://api.github.com/repos/nodejs/node-eps/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/nodejs/node-eps/labels{/name}","releases_url":"https://api.github.com/repos/nodejs/node-eps/releases{/id}","deployments_url":"https://api.github.com/repos/nodejs/node-eps/deployments","created_at":"2015-12-17T23:39:07Z","updated_at":"2016-07-04T18:05:45Z","pushed_at":"2016-07-04T16:05:57Z","git_url":"git://github.com/nodejs/node-eps.git","ssh_url":"git@github.com:nodejs/node-eps.git","clone_url":"https://github.com/nodejs/node-eps.git","svn_url":"https://github.com/nodejs/node-eps","homepage":null,"size":93,"stargazers_count":133,"watchers_count":133,"language":null,"has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":22,"mirror_url":null,"open_issues_count":16,"forks":22,"open_issues":16,"watchers":133,"default_branch":"master"}},"base":{"label":"nodejs:master","ref":"master","sha":"5dae5a537c2d56fbaf23aaf2ae9da15e74474021","user":{"login":"nodejs","id":9950313,"avatar_url":"https://avatars.githubusercontent.com/u/9950313?v=3","gravatar_id":"","url":"https://api.github.com/users/nodejs","html_url":"https://github.com/nodejs","followers_url":"https://api.github.com/users/nodejs/followers","following_url":"https://api.github.com/users/nodejs/following{/other_user}","gists_url":"https://api.github.com/users/nodejs/gists{/gist_id}","starred_url":"https://api.github.com/users/nodejs/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/nodejs/subscriptions","organizations_url":"https://api.github.com/users/nodejs/orgs","repos_url":"https://api.github.com/users/nodejs/repos","events_url":"https://api.github.com/users/nodejs/events{/privacy}","received_events_url":"https://api.github.com/users/nodejs/received_events","type":"Organization","site_admin":false},"repo":{"id":48204104,"name":"node-eps","full_name":"nodejs/node-eps","owner":{"login":"nodejs","id":9950313,"avatar_url":"https://avatars.githubusercontent.com/u/9950313?v=3","gravatar_id":"","url":"https://api.github.com/users/nodejs","html_url":"https://github.com/nodejs","followers_url":"https://api.github.com/users/nodejs/followers","following_url":"https://api.github.com/users/nodejs/following{/other_user}","gists_url":"https://api.github.com/users/nodejs/gists{/gist_id}","starred_url":"https://api.github.com/users/nodejs/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/nodejs/subscriptions","organizations_url":"https://api.github.com/users/nodejs/orgs","repos_url":"https://api.github.com/users/nodejs/repos","events_url":"https://api.github.com/users/nodejs/events{/privacy}","received_events_url":"https://api.github.com/users/nodejs/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/nodejs/node-eps","description":"Node.js Enhancement Proposals for discussion on future API additions/changes to Node core","fork":false,"url":"https://api.github.com/repos/nodejs/node-eps","forks_url":"https://api.github.com/repos/nodejs/node-eps/forks","keys_url":"https://api.github.com/repos/nodejs/node-eps/keys{/key_id}","collaborators_url":"https://api.github.com/repos/nodejs/node-eps/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/nodejs/node-eps/teams","hooks_url":"https://api.github.com/repos/nodejs/node-eps/hooks","issue_events_url":"https://api.github.com/repos/nodejs/node-eps/issues/events{/number}","events_url":"https://api.github.com/repos/nodejs/node-eps/events","assignees_url":"https://api.github.com/repos/nodejs/node-eps/assignees{/user}","branches_url":"https://api.github.com/repos/nodejs/node-eps/branches{/branch}","tags_url":"https://api.github.com/repos/nodejs/node-eps/tags","blobs_url":"https://api.github.com/repos/nodejs/node-eps/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/nodejs/node-eps/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/nodejs/node-eps/git/refs{/sha}","trees_url":"https://api.github.com/repos/nodejs/node-eps/git/trees{/sha}","statuses_url":"https://api.github.com/repos/nodejs/node-eps/statuses/{sha}","languages_url":"https://api.github.com/repos/nodejs/node-eps/languages","stargazers_url":"https://api.github.com/repos/nodejs/node-eps/stargazers","contributors_url":"https://api.github.com/repos/nodejs/node-eps/contributors","subscribers_url":"https://api.github.com/repos/nodejs/node-eps/subscribers","subscription_url":"https://api.github.com/repos/nodejs/node-eps/subscription","commits_url":"https://api.github.com/repos/nodejs/node-eps/commits{/sha}","git_commits_url":"https://api.github.com/repos/nodejs/node-eps/git/commits{/sha}","comments_url":"https://api.github.com/repos/nodejs/node-eps/comments{/number}","issue_comment_url":"https://api.github.com/repos/nodejs/node-eps/issues/comments{/number}","contents_url":"https://api.github.com/repos/nodejs/node-eps/contents/{+path}","compare_url":"https://api.github.com/repos/nodejs/node-eps/compare/{base}...{head}","merges_url":"https://api.github.com/repos/nodejs/node-eps/merges","archive_url":"https://api.github.com/repos/nodejs/node-eps/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/nodejs/node-eps/downloads","issues_url":"https://api.github.com/repos/nodejs/node-eps/issues{/number}","pulls_url":"https://api.github.com/repos/nodejs/node-eps/pulls{/number}","milestones_url":"https://api.github.com/repos/nodejs/node-eps/milestones{/number}","notifications_url":"https://api.github.com/repos/nodejs/node-eps/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/nodejs/node-eps/labels{/name}","releases_url":"https://api.github.com/repos/nodejs/node-eps/releases{/id}","deployments_url":"https://api.github.com/repos/nodejs/node-eps/deployments","created_at":"2015-12-17T23:39:07Z","updated_at":"2016-07-04T18:05:45Z","pushed_at":"2016-07-04T16:05:57Z","git_url":"git://github.com/nodejs/node-eps.git","ssh_url":"git@github.com:nodejs/node-eps.git","clone_url":"https://github.com/nodejs/node-eps.git","svn_url":"https://github.com/nodejs/node-eps","homepage":null,"size":93,"stargazers_count":133,"watchers_count":133,"language":null,"has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":22,"mirror_url":null,"open_issues_count":16,"forks":22,"open_issues":16,"watchers":133,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/nodejs/node-eps/pulls/18"},"html":{"href":"https://github.com/nodejs/node-eps/pull/18"},"issue":{"href":"https://api.github.com/repos/nodejs/node-eps/issues/18"},"comments":{"href":"https://api.github.com/repos/nodejs/node-eps/issues/18/comments"},"review_comments":{"href":"https://api.github.com/repos/nodejs/node-eps/pulls/18/comments"},"review_comment":{"href":"https://api.github.com/repos/nodejs/node-eps/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/nodejs/node-eps/pulls/18/commits"},"statuses":{"href":"https://api.github.com/repos/nodejs/node-eps/statuses/9e95a3fd16f053e1fc0d59226a26bb7fbc552d30"}}}}
{"actor":{"display_login":"rvagg"}}