diff --git a/config.apps.yaml b/config.apps.yaml
index 20d816c1..9a3d9b63 100644
--- a/config.apps.yaml
+++ b/config.apps.yaml
@@ -86,6 +86,14 @@ services:
user-agent: '{{user-agent}}'
accept-language: '{{accept-language}}'
body: '{{ default(request.query, {}) }}'
+ # the template used for contacting MW Rest API
+ mwrestapi_req:
+ method: '{{request.method}}'
+ uri: https://{{domain}}/w/rest.php/v1/{+path}
+ query: '{{ default(request.query, {}) }}'
+ headers: '{{request.headers}}'
+ body: '{{request.body}}'
+ timeout: 60000 # 60 * 1000
# the template used for requesting ResourceLoader module content from load.php
mw_resource_loader_req:
method: post
diff --git a/config.dev.yaml b/config.dev.yaml
index 6043846a..f5644aef 100644
--- a/config.dev.yaml
+++ b/config.dev.yaml
@@ -87,6 +87,14 @@ services:
user-agent: '{{user-agent}}'
accept-language: '{{accept-language}}'
body: '{{ default(request.query, {}) }}'
+ # the template used for contacting MW Rest API
+ mwrestapi_req:
+ method: '{{request.method}}'
+ uri: https://{{domain}}/w/rest.php/v1/{+path}
+ query: '{{ default(request.query, {}) }}'
+ headers: '{{request.headers}}'
+ body: '{{request.body}}'
+ timeout: 60000 # 60 * 1000
# the template used for requesting ResourceLoader module content from load.php
mw_resource_loader_req:
method: post
diff --git a/config.labs.yaml b/config.labs.yaml
index 1d12195b..370da135 100644
--- a/config.labs.yaml
+++ b/config.labs.yaml
@@ -86,6 +86,14 @@ services:
user-agent: '{{user-agent}}'
accept-language: '{{accept-language}}'
body: '{{ default(request.query, {}) }}'
+ # the template used for contacting MW Rest API
+ mwrestapi_req:
+ method: '{{request.method}}'
+ uri: https://{{domain}}/w/rest.php/v1/{+path}
+ query: '{{ default(request.query, {}) }}'
+ headers: '{{request.headers}}'
+ body: '{{request.body}}'
+ timeout: 60000 # 60 * 1000
# the template used for requesting ResourceLoader module content from load.php
mw_resource_loader_req:
method: post
diff --git a/config.prod.yaml b/config.prod.yaml
index 077187fb..b94424ad 100644
--- a/config.prod.yaml
+++ b/config.prod.yaml
@@ -99,6 +99,14 @@ services:
user-agent: '{{user-agent}}'
accept-language: '{{accept-language}}'
body: '{{ default(request.query, {}) }}'
+ # the template used for contacting MW Rest API
+ mwrestapi_req:
+ method: '{{request.method}}'
+ uri: https://{{domain}}/w/rest.php/v1/{+path}
+ query: '{{ default(request.query, {}) }}'
+ headers: '{{request.headers}}'
+ body: '{{request.body}}'
+ timeout: 60000 # 60 * 1000
# the template used for requesting ResourceLoader module content from load.php
mw_resource_loader_req:
method: post
diff --git a/dist/config.yaml b/dist/config.yaml
index 9abb73df..1563046c 100644
--- a/dist/config.yaml
+++ b/dist/config.yaml
@@ -68,6 +68,13 @@ services:
headers:
user-agent: '{{user-agent}}'
body: '{{ default(request.query, {}) }}'
+ # the template used for contacting MW Rest API
+ mwrestapi_req:
+ method: '{{request.method}}'
+ uri: https://{{domain}}/w/rest.php/v1/{+path}
+ query: '{{ default(request.query, {}) }}'
+ headers: '{{request.headers}}'
+ body: '{{request.body}}'
# the template used for contacting RESTBase
restbase_req:
method: '{{request.method}}'
diff --git a/lib/api-util.js b/lib/api-util.js
index 5011ff49..b4a73389 100644
--- a/lib/api-util.js
+++ b/lib/api-util.js
@@ -14,6 +14,46 @@ function prettyMwApiReq(request) {
return `${request.uri}?${querystring.stringify(request.body)}`;
}
+/**
+ * Calls the MW REST API with the supplied domain, path and request parameters
+ * @param {!Object} req the incoming request object
+ * @param {?string} path the REST API path to contact without the leading slash
+ * @param {?Object} [restReq={}] the object containing the REST request details
+ * @param {?string} [restReq.method=get] the request method
+ * @param {?Object} [restReq.query={}] the query string to send, if any
+ * @param {?Object} [restReq.headers={}] the request headers to send
+ * @param {?Object} [restReq.body=null] the body of the request, if any
+ * @return {!Promise} a promise resolving as the response object from the REST API
+ *
+ */
+function mwRestApiGet(req, path, restReq) {
+
+ const app = req.app;
+ if (path.constructor === Object) {
+ restReq = path;
+ path = undefined;
+ }
+ restReq = restReq || {};
+ restReq.method = restReq.method || 'get';
+ restReq.query = restReq.query || {};
+ restReq.headers = restReq.headers || {};
+ restReq.params = restReq.params || {};
+ restReq.params.path = path || restReq.params.path;
+ restReq.params.domain = restReq.params.domain || req.params.domain;
+ if (!restReq.params.path || !restReq.params.domain) {
+ return BBPromise.reject(new HTTPError({
+ status: 500,
+ type: 'internal_error',
+ title: 'Invalid internal call',
+ detail: 'domain and path need to be defined for the REST API call'
+ }));
+ }
+ restReq.params.path = restReq.params.path[0] === '/' ?
+ restReq.params.path.slice(1) : restReq.params.path;
+
+ return req.issueRequest(app.mwrestapi_tbl.expand({ request: restReq }));
+}
+
/**
* Make a request for ResourceLoader modules to MediaWiki's load.php entry point.
*
@@ -229,6 +269,7 @@ function setupApiTemplates(app) {
};
}
app.restbase_tpl = new Template(app.conf.restbase_req);
+ app.mwrestapi_tbl = new Template(app.conf.mwrestapi_req);
// set up the MediaWiki page request template
if (!app.conf.mw_resource_loader_req) {
@@ -240,7 +281,6 @@ function setupApiTemplates(app) {
};
}
app.mw_resource_loader_tpl = new Template(app.conf.mw_resource_loader_req);
-
}
/**
@@ -277,6 +317,7 @@ function getCommonsDomain(domain) {
module.exports = {
// Shared with service-template-node
mwApiGet,
+ mwRestApiGet,
restApiGet,
setupApiTemplates,
diff --git a/lib/mobile-util.js b/lib/mobile-util.js
index f2098e9e..877c328d 100644
--- a/lib/mobile-util.js
+++ b/lib/mobile-util.js
@@ -26,6 +26,7 @@ mUtil.CONTENT_TYPES = {
type: 'application/json'
},
talk: { name: 'Talk', version: '0.1.1', type: 'application/json' },
+ significantChanges: { name: 'SignificantChanges', version: '0.0.1', type: 'application/json' },
mediaList: { name: 'MediaList', version: '1.1.0', type: 'application/json' },
i18n: { name: 'i18n', version: '0.0.1', type: 'application/json' }
};
diff --git a/lib/mwapi.js b/lib/mwapi.js
index 933699a7..3e5db17f 100644
--- a/lib/mwapi.js
+++ b/lib/mwapi.js
@@ -127,6 +127,53 @@ mwapi.getSiteInfo = function(req) {
return siteInfoCache[rp.domain];
};
+mwapi.queryForRevisions = function(req, title, pageSize, rvStart, rvEnd) {
+ const query = apiParams({
+ action: 'query',
+ prop: 'revisions',
+ titles: title || req.params.title,
+ rvslots: 'main',
+ rvprop: 'ids|timestamp|user|userid|size|parsedcomment|comment|tags|flags|size',
+ rvdir: 'older',
+ format: 'json',
+ rvlimit: pageSize || '20'
+ });
+
+ // to get around "parameters rvstartid & rvstart cannot be used together" error
+ if (rvStart && rvEnd) {
+ query.rvstart = rvStart;
+ query.rvend = rvEnd;
+ } else if (rvEnd) {
+ query.rvend = rvEnd;
+ } else {
+ query.rvstartid = req.query.rvstartid;
+ }
+
+ return api.mwApiGet(req, query);
+};
+
+mwapi.queryForUsers = function(req, userids) {
+
+ var useridstring = '';
+ for (let i = 0; i < userids.length; i++) {
+ const userid = userids[i];
+ if (i === 0) {
+ useridstring += userid;
+ } else {
+ useridstring += `|${userid}`;
+ }
+ }
+
+ const query = apiParams({
+ action: 'query',
+ list: 'users',
+ ususerids: useridstring,
+ usprop: 'groups|editcount',
+ });
+
+ return api.mwApiGet(req, query);
+};
+
/**
* Given protection status for an article simplify it to allow easy reference
*
diff --git a/lib/mwrestapi.js b/lib/mwrestapi.js
new file mode 100644
index 00000000..53765ccd
--- /dev/null
+++ b/lib/mwrestapi.js
@@ -0,0 +1,10 @@
+const api = require('./api-util');
+const mwrestapi = {};
+
+mwrestapi.queryForDiff = function(req, fromRevID, toRevID) {
+ const path = `revision/${fromRevID}/compare/${toRevID}`;
+
+ return api.mwRestApiGet(req, path);
+};
+
+module.exports = mwrestapi;
diff --git a/package-lock.json b/package-lock.json
index ae5da377..715d0361 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -135,6 +135,11 @@
"to-fast-properties": "^2.0.0"
}
},
+ "@root/encoding": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@root/encoding/-/encoding-1.0.1.tgz",
+ "integrity": "sha512-OaEub02ufoU038gy6bsNHQOjIn8nUjGiLcaRmJ40IUykneJkIW5fxDqKxQx48cszuNflYldsJLPPXCrGfHs8yQ=="
+ },
"@sindresorhus/is": {
"version": "0.14.0",
"resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz",
@@ -239,6 +244,17 @@
"uri-js": "^4.2.2"
}
},
+ "align-text": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz",
+ "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=",
+ "optional": true,
+ "requires": {
+ "kind-of": "^3.0.2",
+ "longest": "^1.0.1",
+ "repeat-string": "^1.5.2"
+ }
+ },
"ansi-align": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.0.tgz",
@@ -708,6 +724,16 @@
"long": "^4.0.0"
}
},
+ "center-align": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz",
+ "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=",
+ "optional": true,
+ "requires": {
+ "align-text": "^0.1.3",
+ "lazy-cache": "^1.0.3"
+ }
+ },
"chalk": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
@@ -1018,6 +1044,11 @@
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
"integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
},
+ "core-js": {
+ "version": "2.6.11",
+ "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.11.tgz",
+ "integrity": "sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg=="
+ },
"core-util-is": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
@@ -3148,6 +3179,15 @@
"html-escaper": "^2.0.0"
}
},
+ "jodid25519": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/jodid25519/-/jodid25519-1.0.2.tgz",
+ "integrity": "sha1-BtSRIlUJNBlHfUJWM2BuDpB4KWc=",
+ "optional": true,
+ "requires": {
+ "jsbn": "~0.1.0"
+ }
+ },
"js-beautify": {
"version": "1.13.0",
"resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.13.0.tgz",
@@ -3415,6 +3455,23 @@
"json-buffer": "3.0.0"
}
},
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "optional": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ },
+ "dependencies": {
+ "is-buffer": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
+ "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
+ "optional": true
+ }
+ }
+ },
"latest-version": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/latest-version/-/latest-version-5.1.0.tgz",
@@ -3424,6 +3481,12 @@
"package-json": "^6.3.0"
}
},
+ "lazy-cache": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz",
+ "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=",
+ "optional": true
+ },
"less": {
"version": "3.12.2",
"resolved": "https://registry.npmjs.org/less/-/less-3.12.2.tgz",
@@ -3607,6 +3670,12 @@
"resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz",
"integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA=="
},
+ "longest": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz",
+ "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=",
+ "optional": true
+ },
"lowercase-keys": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz",
@@ -4533,215 +4602,3537 @@
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
"integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="
},
- "path-exists": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
- "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU="
- },
- "path-is-absolute": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
- "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
- },
- "path-is-inside": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz",
- "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=",
- "dev": true
- },
- "path-key": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
- "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=",
- "dev": true
- },
- "path-parse": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
- "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
- "dev": true
- },
- "path-to-regexp": {
- "version": "0.1.7",
- "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
- "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
- },
- "path-type": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz",
- "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==",
- "dev": true,
- "requires": {
- "pify": "^3.0.0"
- }
- },
- "performance-now": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
- "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns="
- },
- "picomatch": {
- "version": "2.2.2",
- "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz",
- "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==",
- "dev": true
- },
- "pify": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
- "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
- "dev": true
- },
- "pkg-dir": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz",
- "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==",
- "dev": true,
- "requires": {
- "find-up": "^3.0.0"
- }
- },
- "postcss": {
- "version": "7.0.32",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.32.tgz",
- "integrity": "sha512-03eXong5NLnNCD05xscnGKGDZ98CyzoqPSMjOe6SuoQY7Z2hIj0Ld1g/O/UQRuOle2aRtiIRDg9tDcTGAkLfKw==",
- "requires": {
- "chalk": "^2.4.2",
- "source-map": "^0.6.1",
- "supports-color": "^6.1.0"
+ "parsoid": {
+ "version": "0.7.1",
+ "resolved": "https://registry.npmjs.org/parsoid/-/parsoid-0.7.1.tgz",
+ "integrity": "sha1-Eh3PWdC6zZJHQFmIUliI+aa6Iyg=",
+ "requires": {
+ "async": "^0.9.2",
+ "babybird": "^0.0.1",
+ "body-parser": "^1.16.0",
+ "compression": "^1.6.2",
+ "connect-busboy": "^0.0.2",
+ "content-type": "git+https://github.com/wikimedia/content-type.git#master",
+ "core-js": "^2.4.1",
+ "diff": "^1.0.7",
+ "domino": "^1.0.28",
+ "entities": "^1.1.1",
+ "express": "^4.14.0",
+ "express-handlebars": "^3.0.0",
+ "finalhandler": "^0.5.0",
+ "js-yaml": "^3.6.1",
+ "mediawiki-title": "^0.5.6",
+ "negotiator": "git+https://github.com/arlolra/negotiator.git#full-parse-access",
+ "node-uuid": "^1.4.7",
+ "pegjs": "git+https://github.com/tstarling/pegjs.git#fork",
+ "prfun": "^2.1.4",
+ "request": "^2.79.0",
+ "semver": "^5.3.0",
+ "serve-favicon": "^2.3.2",
+ "service-runner": "^2.1.13",
+ "simplediff": "^0.1.1",
+ "yargs": "^5.0.0"
},
"dependencies": {
- "supports-color": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz",
- "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==",
+ "JSV": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/JSV/-/JSV-4.0.2.tgz",
+ "integrity": "sha1-0Hf2glVx+CEy+d/67Vh7QCn+/1c="
+ },
+ "abbrev": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.9.tgz",
+ "integrity": "sha1-kbR5JYinc4wl813W9jdSovh3YTU="
+ },
+ "accepts": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.3.tgz",
+ "integrity": "sha1-w8p0NJOGSMPg2cHjKN1otiLChMo=",
"requires": {
- "has-flag": "^3.0.0"
+ "mime-types": "~2.1.11",
+ "negotiator": "0.6.1"
}
- }
- }
- },
- "pre-commit": {
- "version": "1.2.2",
- "resolved": "https://registry.npmjs.org/pre-commit/-/pre-commit-1.2.2.tgz",
- "integrity": "sha1-287g7p3nI15X95xW186UZBpp7sY=",
- "dev": true,
- "requires": {
- "cross-spawn": "^5.0.1",
- "spawn-sync": "^1.0.15",
- "which": "1.2.x"
- },
- "dependencies": {
- "cross-spawn": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
- "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=",
- "dev": true,
+ },
+ "alea": {
+ "version": "0.0.9",
+ "resolved": "https://registry.npmjs.org/alea/-/alea-0.0.9.tgz",
+ "integrity": "sha1-9zjLRfg0MAafRc9pzL8xLdV6nho="
+ },
+ "amdefine": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz",
+ "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU="
+ },
+ "ansi-regex": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+ "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
+ },
+ "ansi-styles": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
+ "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4="
+ },
+ "argparse": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.9.tgz",
+ "integrity": "sha1-c9g7wmP4bpf4zE9rrhsOkKfSLIY=",
"requires": {
- "lru-cache": "^4.0.1",
- "shebang-command": "^1.2.0",
- "which": "^1.2.9"
+ "sprintf-js": "~1.0.2"
}
},
- "which": {
- "version": "1.2.14",
- "resolved": "https://registry.npmjs.org/which/-/which-1.2.14.tgz",
- "integrity": "sha1-mofEN48D6CfOyvGs31bHNsAcFOU=",
- "dev": true,
+ "array-flatten": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
+ "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
+ },
+ "asap": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.5.tgz",
+ "integrity": "sha1-UidltQw1EEkOUtfc/ghe+bqWlY8="
+ },
+ "asn1": {
+ "version": "0.2.3",
+ "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz",
+ "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y="
+ },
+ "assert-plus": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz",
+ "integrity": "sha1-104bh+ev/A24qttwIfP+SBAasjQ="
+ },
+ "assertion-error": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.0.2.tgz",
+ "integrity": "sha1-E8pRXYYgbaC6xm6DTdOX2HWBCUw="
+ },
+ "async": {
+ "version": "0.9.2",
+ "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz",
+ "integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0="
+ },
+ "asynckit": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+ "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
+ },
+ "aws-sign2": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz",
+ "integrity": "sha1-FDQt0428yU0OW4fXY81jYSwOeU8="
+ },
+ "aws4": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.5.0.tgz",
+ "integrity": "sha1-Cin/t5wxyecS7rCH6OemS0pW11U="
+ },
+ "babel-runtime": {
+ "version": "6.20.0",
+ "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.20.0.tgz",
+ "integrity": "sha1-hzAL3PTNdw8JvwBIxkIE4XgG0W8=",
"requires": {
- "isexe": "^2.0.0"
+ "core-js": "^2.4.0",
+ "regenerator-runtime": "^0.10.0"
}
- }
- }
- },
- "prelude-ls": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
- "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=",
- "dev": true
- },
- "prepend-http": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz",
- "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=",
- "dev": true
- },
- "preq": {
- "version": "0.5.14",
- "resolved": "https://registry.npmjs.org/preq/-/preq-0.5.14.tgz",
- "integrity": "sha512-kuJ5ndEgjs27kTTQ/P2ipPQoHeCJcAI4i97mU3xSjkjx6CsuQOsCe2l5twTGC0SCB5UkzRpmrpXmvN0Ip4ZCxA==",
- "requires": {
- "bluebird": "^3.5.5",
- "request": "^2.88.0",
- "requestretry": "4.0.2"
- }
- },
- "process-nextick-args": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
- "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
- },
- "progress": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
- "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
- "dev": true
- },
- "proto-list": {
- "version": "1.2.4",
- "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz",
- "integrity": "sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk=",
- "dev": true
- },
- "proxy-addr": {
- "version": "2.0.6",
- "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz",
- "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==",
- "requires": {
- "forwarded": "~0.1.2",
- "ipaddr.js": "1.9.1"
- }
- },
- "prr": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz",
- "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=",
- "dev": true,
- "optional": true
- },
- "pseudomap": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
- "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=",
- "dev": true
- },
- "psl": {
- "version": "1.7.0",
- "resolved": "https://registry.npmjs.org/psl/-/psl-1.7.0.tgz",
- "integrity": "sha512-5NsSEDv8zY70ScRnOTn7bK7eanl2MvFrOrS/R6x+dBt5g1ghnj9Zv90kO8GwT8gxcu2ANyFprnFYB85IogIJOQ=="
- },
- "pstree.remy": {
- "version": "1.1.8",
- "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz",
- "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==",
- "dev": true
- },
- "pump": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
- "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
- "dev": true,
- "requires": {
- "end-of-stream": "^1.1.0",
- "once": "^1.3.1"
- }
- },
- "punycode": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
- "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
- },
- "pupa": {
+ },
+ "babybird": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/babybird/-/babybird-0.0.1.tgz",
+ "integrity": "sha1-2oDHnG10Qc3+x8L/LcvXwT69vqI=",
+ "requires": {
+ "asap": "^2.0.3",
+ "is-arguments": "^1.0.2"
+ }
+ },
+ "babylon": {
+ "version": "6.15.0",
+ "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.15.0.tgz",
+ "integrity": "sha1-umXPoagOF1mw6J+1YuJ9zK5wNI4="
+ },
+ "balanced-match": {
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz",
+ "integrity": "sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg="
+ },
+ "bl": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.0.tgz",
+ "integrity": "sha1-E5fn7ELF9dw4dHDFAONKn2vp6pg=",
+ "requires": {
+ "readable-stream": "^2.0.5"
+ },
+ "dependencies": {
+ "isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
+ },
+ "readable-stream": {
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.2.tgz",
+ "integrity": "sha1-qeb+w8fdqF+LsbO6cChgRVb8gl4=",
+ "requires": {
+ "buffer-shims": "^1.0.0",
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.1",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~1.0.6",
+ "string_decoder": "~0.10.x",
+ "util-deprecate": "~1.0.1"
+ }
+ }
+ }
+ },
+ "bluebird": {
+ "version": "3.4.7",
+ "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.7.tgz",
+ "integrity": "sha1-9y12C+Cbf3bQjtj66Ysomo0F+rM="
+ },
+ "body-parser": {
+ "version": "1.16.0",
+ "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.16.0.tgz",
+ "integrity": "sha1-kkpeRyxiKfudabhaINXyUy3seIs=",
+ "requires": {
+ "bytes": "2.4.0",
+ "content-type": "~1.0.2",
+ "debug": "2.6.0",
+ "depd": "~1.1.0",
+ "http-errors": "~1.5.1",
+ "iconv-lite": "0.4.15",
+ "on-finished": "~2.3.0",
+ "qs": "6.2.1",
+ "raw-body": "~2.2.0",
+ "type-is": "~1.6.14"
+ },
+ "dependencies": {
+ "content-type": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.2.tgz",
+ "integrity": "sha1-t9ETrueo3Se9IRM8TcJSnfFyHu0="
+ }
+ }
+ },
+ "boom": {
+ "version": "2.10.1",
+ "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz",
+ "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=",
+ "requires": {
+ "hoek": "2.x.x"
+ }
+ },
+ "brace-expansion": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.6.tgz",
+ "integrity": "sha1-cZfX6qm4fmSDkOph/GbIRCdCDfk=",
+ "requires": {
+ "balanced-match": "^0.4.1",
+ "concat-map": "0.0.1"
+ }
+ },
+ "buffer-shims": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz",
+ "integrity": "sha1-mXjOMXOIxkmth5MCjDR37wRKi1E="
+ },
+ "builtin-modules": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz",
+ "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8="
+ },
+ "bunyan": {
+ "version": "1.8.5",
+ "resolved": "https://registry.npmjs.org/bunyan/-/bunyan-1.8.5.tgz",
+ "integrity": "sha1-DWGegwBfuJBw9fR5gvwb8AYAh4o=",
+ "requires": {
+ "dtrace-provider": "~0.8",
+ "moment": "^2.10.6",
+ "mv": "~2",
+ "safe-json-stringify": "~1"
+ }
+ },
+ "bunyan-syslog-udp": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/bunyan-syslog-udp/-/bunyan-syslog-udp-0.1.0.tgz",
+ "integrity": "sha1-+/ruA6gc0qlavBj5LJnyu4fiQpw="
+ },
+ "busboy": {
+ "version": "0.2.14",
+ "resolved": "https://registry.npmjs.org/busboy/-/busboy-0.2.14.tgz",
+ "integrity": "sha1-bCpiLvz0fFe7vh4qnDetNseSVFM=",
+ "requires": {
+ "dicer": "0.2.5",
+ "readable-stream": "1.1.x"
+ }
+ },
+ "bytes": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/bytes/-/bytes-2.4.0.tgz",
+ "integrity": "sha1-fZcZb51br39pNeJZhVSe3SpsIzk="
+ },
+ "camelcase": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz",
+ "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo="
+ },
+ "caseless": {
+ "version": "0.11.0",
+ "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.11.0.tgz",
+ "integrity": "sha1-cVuW6phBWTzDMGeSP17GDr2k99c="
+ },
+ "chai": {
+ "version": "3.5.0",
+ "resolved": "https://registry.npmjs.org/chai/-/chai-3.5.0.tgz",
+ "integrity": "sha1-TQJjewZ/6Vi9v906QOxW/vc3Mkc=",
+ "requires": {
+ "assertion-error": "^1.0.1",
+ "deep-eql": "^0.1.3",
+ "type-detect": "^1.0.0"
+ }
+ },
+ "chalk": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
+ "requires": {
+ "ansi-styles": "^2.2.1",
+ "escape-string-regexp": "^1.0.2",
+ "has-ansi": "^2.0.0",
+ "strip-ansi": "^3.0.0",
+ "supports-color": "^2.0.0"
+ }
+ },
+ "clarinet": {
+ "version": "0.11.0",
+ "resolved": "https://registry.npmjs.org/clarinet/-/clarinet-0.11.0.tgz",
+ "integrity": "sha1-bMkSuTE43IZ/wnPNNOqQ6D4FRxk="
+ },
+ "cli": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/cli/-/cli-1.0.1.tgz",
+ "integrity": "sha1-IoF1NPJL+klQw01TLUjsvGIbjBQ=",
+ "requires": {
+ "exit": "0.1.2",
+ "glob": "^7.1.1"
+ },
+ "dependencies": {
+ "glob": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.1.tgz",
+ "integrity": "sha1-gFIR3wT6rxxjo2ADBs31reULLsg=",
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.2",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ }
+ }
+ },
+ "cli-table": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/cli-table/-/cli-table-0.3.1.tgz",
+ "integrity": "sha1-9TsFJmqLGguTSz0IIebi3FkUriM=",
+ "requires": {
+ "colors": "1.0.3"
+ },
+ "dependencies": {
+ "colors": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz",
+ "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs="
+ }
+ }
+ },
+ "cliui": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz",
+ "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=",
+ "requires": {
+ "string-width": "^1.0.1",
+ "strip-ansi": "^3.0.1",
+ "wrap-ansi": "^2.0.0"
+ }
+ },
+ "code-point-at": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
+ "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c="
+ },
+ "colors": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz",
+ "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM="
+ },
+ "combined-stream": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz",
+ "integrity": "sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk=",
+ "requires": {
+ "delayed-stream": "~1.0.0"
+ }
+ },
+ "commander": {
+ "version": "2.9.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz",
+ "integrity": "sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q=",
+ "requires": {
+ "graceful-readlink": ">= 1.0.0"
+ }
+ },
+ "comment-parser": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-0.3.1.tgz",
+ "integrity": "sha1-/WV6rIwUktMIyaYQD8m0nSQ1q6E=",
+ "requires": {
+ "readable-stream": "^2.0.4"
+ },
+ "dependencies": {
+ "isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
+ },
+ "readable-stream": {
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.2.tgz",
+ "integrity": "sha1-qeb+w8fdqF+LsbO6cChgRVb8gl4=",
+ "requires": {
+ "buffer-shims": "^1.0.0",
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.1",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~1.0.6",
+ "string_decoder": "~0.10.x",
+ "util-deprecate": "~1.0.1"
+ }
+ }
+ }
+ },
+ "component-emitter": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz",
+ "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY="
+ },
+ "compressible": {
+ "version": "2.0.9",
+ "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.9.tgz",
+ "integrity": "sha1-baq04rWZwncN2eIeeokbHFp1VCU=",
+ "requires": {
+ "mime-db": ">= 1.24.0 < 2"
+ }
+ },
+ "compression": {
+ "version": "1.6.2",
+ "resolved": "https://registry.npmjs.org/compression/-/compression-1.6.2.tgz",
+ "integrity": "sha1-zOsSHsydCcUtetDDNQ6pPd1AK8M=",
+ "requires": {
+ "accepts": "~1.3.3",
+ "bytes": "2.3.0",
+ "compressible": "~2.0.8",
+ "debug": "~2.2.0",
+ "on-headers": "~1.0.1",
+ "vary": "~1.1.0"
+ },
+ "dependencies": {
+ "bytes": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/bytes/-/bytes-2.3.0.tgz",
+ "integrity": "sha1-1baAoWW2IBc5rLYRVCqrwtjOsHA="
+ },
+ "debug": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz",
+ "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=",
+ "requires": {
+ "ms": "0.7.1"
+ }
+ },
+ "ms": {
+ "version": "0.7.1",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz",
+ "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg="
+ }
+ }
+ },
+ "concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
+ },
+ "connect-busboy": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/connect-busboy/-/connect-busboy-0.0.2.tgz",
+ "integrity": "sha1-rFyclmchcYheV2xmsr/ZXTuxEJc=",
+ "requires": {
+ "busboy": "*"
+ }
+ },
+ "console-browserify": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz",
+ "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=",
+ "requires": {
+ "date-now": "^0.1.4"
+ }
+ },
+ "content-disposition": {
+ "version": "0.5.1",
+ "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.1.tgz",
+ "integrity": "sha1-h0dsamfI2qh+Muh2Ft+IO6f7Bxs="
+ },
+ "content-type": {
+ "version": "git+https://github.com/wikimedia/content-type.git#47b2632d0a2ee79a7d67268e2f6621becd95d05b",
+ "from": "git+https://github.com/wikimedia/content-type.git#master"
+ },
+ "cookie": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz",
+ "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s="
+ },
+ "cookie-signature": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
+ "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
+ },
+ "cookiejar": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.0.6.tgz",
+ "integrity": "sha1-Cr81atANHFohnYjURRgEbdAmrP4="
+ },
+ "core-js": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.4.1.tgz",
+ "integrity": "sha1-TekR5mew6ukSTjQlS1OupvxhjT4="
+ },
+ "core-util-is": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
+ "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
+ },
+ "coveralls": {
+ "version": "2.11.15",
+ "resolved": "https://registry.npmjs.org/coveralls/-/coveralls-2.11.15.tgz",
+ "integrity": "sha1-N9NHQ2nWbBTzP6c6nSXO5uCZ/KA=",
+ "requires": {
+ "js-yaml": "3.6.1",
+ "lcov-parse": "0.0.10",
+ "log-driver": "1.2.5",
+ "minimist": "1.2.0",
+ "request": "2.75.0"
+ },
+ "dependencies": {
+ "bl": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/bl/-/bl-1.1.2.tgz",
+ "integrity": "sha1-/cqHGplxOqANGeO7ukHER4emU5g=",
+ "requires": {
+ "readable-stream": "~2.0.5"
+ }
+ },
+ "form-data": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.0.0.tgz",
+ "integrity": "sha1-bwrrrcxdoWwT4ezBETfYX5uIOyU=",
+ "requires": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.5",
+ "mime-types": "^2.1.11"
+ }
+ },
+ "isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
+ },
+ "js-yaml": {
+ "version": "3.6.1",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.6.1.tgz",
+ "integrity": "sha1-bl/mfYsgXOTSL60Ft3geja3MSzA=",
+ "requires": {
+ "argparse": "^1.0.7",
+ "esprima": "^2.6.0"
+ }
+ },
+ "minimist": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
+ "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ="
+ },
+ "readable-stream": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz",
+ "integrity": "sha1-j5A0HmilPMySh4jaz80Rs265t44=",
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.1",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~1.0.6",
+ "string_decoder": "~0.10.x",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "request": {
+ "version": "2.75.0",
+ "resolved": "https://registry.npmjs.org/request/-/request-2.75.0.tgz",
+ "integrity": "sha1-0rgmiihtoT6qXQGt9dGMyQ9lfZM=",
+ "requires": {
+ "aws-sign2": "~0.6.0",
+ "aws4": "^1.2.1",
+ "bl": "~1.1.2",
+ "caseless": "~0.11.0",
+ "combined-stream": "~1.0.5",
+ "extend": "~3.0.0",
+ "forever-agent": "~0.6.1",
+ "form-data": "~2.0.0",
+ "har-validator": "~2.0.6",
+ "hawk": "~3.1.3",
+ "http-signature": "~1.1.0",
+ "is-typedarray": "~1.0.0",
+ "isstream": "~0.1.2",
+ "json-stringify-safe": "~5.0.1",
+ "mime-types": "~2.1.7",
+ "node-uuid": "~1.4.7",
+ "oauth-sign": "~0.8.1",
+ "qs": "~6.2.0",
+ "stringstream": "~0.0.4",
+ "tough-cookie": "~2.3.0",
+ "tunnel-agent": "~0.4.1"
+ }
+ }
+ }
+ },
+ "cryptiles": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz",
+ "integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=",
+ "requires": {
+ "boom": "2.x.x"
+ }
+ },
+ "cst": {
+ "version": "0.4.9",
+ "resolved": "https://registry.npmjs.org/cst/-/cst-0.4.9.tgz",
+ "integrity": "sha1-Ua8UITv1+OjnFZZqxkXh4qVsaDQ=",
+ "requires": {
+ "babel-runtime": "^6.9.2",
+ "babylon": "^6.8.1",
+ "source-map-support": "^0.4.0"
+ }
+ },
+ "cycle": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/cycle/-/cycle-1.0.3.tgz",
+ "integrity": "sha1-IegLK+hYD5i0aPN5QwZisEbDStI="
+ },
+ "dashdash": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
+ "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
+ "requires": {
+ "assert-plus": "^1.0.0"
+ },
+ "dependencies": {
+ "assert-plus": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
+ "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU="
+ }
+ }
+ },
+ "date-now": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz",
+ "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs="
+ },
+ "debug": {
+ "version": "2.6.0",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.0.tgz",
+ "integrity": "sha1-vFlryr52F/Edn6FTYe3tVgi4SZs=",
+ "requires": {
+ "ms": "0.7.2"
+ }
+ },
+ "decamelize": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
+ "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA="
+ },
+ "deep-eql": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-0.1.3.tgz",
+ "integrity": "sha1-71WKyrjeJSBs1xOQbXTlaTDrafI=",
+ "requires": {
+ "type-detect": "0.1.1"
+ },
+ "dependencies": {
+ "type-detect": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-0.1.1.tgz",
+ "integrity": "sha1-C6XsKohWQORw6k6FBZcZANrFiCI="
+ }
+ }
+ },
+ "deep-equal": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz",
+ "integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU="
+ },
+ "deep-is": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz",
+ "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ="
+ },
+ "define-properties": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.2.tgz",
+ "integrity": "sha1-g6c/L+pWmJj7c3GTyPhzyvbUXJQ=",
+ "requires": {
+ "foreach": "^2.0.5",
+ "object-keys": "^1.0.8"
+ }
+ },
+ "delayed-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+ "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
+ },
+ "depd": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.0.tgz",
+ "integrity": "sha1-4b2Cxqq2ztlluXuIsX7T5SjKGMM="
+ },
+ "destroy": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
+ "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
+ },
+ "dicer": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/dicer/-/dicer-0.2.5.tgz",
+ "integrity": "sha1-WZbAhrszIYyBLAkL3cCc0S+stw8=",
+ "requires": {
+ "readable-stream": "1.1.x",
+ "streamsearch": "0.1.2"
+ }
+ },
+ "diff": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/diff/-/diff-1.4.0.tgz",
+ "integrity": "sha1-fyjS657nsVqX79ic5j3P2qPMur8="
+ },
+ "dom-serializer": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz",
+ "integrity": "sha1-BzxpdUbOB4DOI75KKOKT5AvDDII=",
+ "requires": {
+ "domelementtype": "~1.1.1",
+ "entities": "~1.1.1"
+ },
+ "dependencies": {
+ "domelementtype": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz",
+ "integrity": "sha1-vSh3PiZCiBrsUVRJJCmcXNgiGFs="
+ }
+ }
+ },
+ "dom-storage": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/dom-storage/-/dom-storage-2.0.2.tgz",
+ "integrity": "sha1-7RfL9oq9EOCu+BgnE+KXxeS1ALA="
+ },
+ "domelementtype": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.0.tgz",
+ "integrity": "sha1-sXrtguirWeUt2cGbF1bg/BhyBMI="
+ },
+ "domhandler": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.3.0.tgz",
+ "integrity": "sha1-LeWaCCLVAn+r/28DLCsloqir5zg=",
+ "requires": {
+ "domelementtype": "1"
+ }
+ },
+ "domino": {
+ "version": "1.0.28",
+ "resolved": "https://registry.npmjs.org/domino/-/domino-1.0.28.tgz",
+ "integrity": "sha1-nOP2qSIaLDKImEsU6hkc0ns5L4c="
+ },
+ "domutils": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz",
+ "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=",
+ "requires": {
+ "dom-serializer": "0",
+ "domelementtype": "1"
+ }
+ },
+ "ee-first": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
+ "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
+ },
+ "encodeurl": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.1.tgz",
+ "integrity": "sha1-eePVhlU0aQn+bw9Fpd5oEDspTSA="
+ },
+ "entities": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.1.tgz",
+ "integrity": "sha1-blwtClYhtdra7O+AuQ7ftc13cvA="
+ },
+ "error-ex": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.0.tgz",
+ "integrity": "sha1-5ntD8+gsluo6WE/+4Ln8MyXYAtk=",
+ "requires": {
+ "is-arrayish": "^0.2.1"
+ }
+ },
+ "escape-html": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+ "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
+ },
+ "escape-string-regexp": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
+ },
+ "escodegen": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.8.1.tgz",
+ "integrity": "sha1-WltTr0aTEQvrsIZ6o0MN07cKEBg=",
+ "requires": {
+ "esprima": "^2.7.1",
+ "estraverse": "^1.9.1",
+ "esutils": "^2.0.2",
+ "optionator": "^0.8.1",
+ "source-map": "~0.2.0"
+ }
+ },
+ "esprima": {
+ "version": "2.7.3",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz",
+ "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE="
+ },
+ "estraverse": {
+ "version": "1.9.3",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.9.3.tgz",
+ "integrity": "sha1-r2fy3JIlgkFZUJJgkaQAXSnJu0Q="
+ },
+ "esutils": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz",
+ "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs="
+ },
+ "etag": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/etag/-/etag-1.7.0.tgz",
+ "integrity": "sha1-A9MLX2fdbmMtKUXTDWZScxo01dg="
+ },
+ "exit": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz",
+ "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw="
+ },
+ "express": {
+ "version": "4.14.0",
+ "resolved": "https://registry.npmjs.org/express/-/express-4.14.0.tgz",
+ "integrity": "sha1-we4/Qs3Ikfs9xlCoki1R7IR9DWY=",
+ "requires": {
+ "accepts": "~1.3.3",
+ "array-flatten": "1.1.1",
+ "content-disposition": "0.5.1",
+ "content-type": "~1.0.2",
+ "cookie": "0.3.1",
+ "cookie-signature": "1.0.6",
+ "debug": "~2.2.0",
+ "depd": "~1.1.0",
+ "encodeurl": "~1.0.1",
+ "escape-html": "~1.0.3",
+ "etag": "~1.7.0",
+ "finalhandler": "0.5.0",
+ "fresh": "0.3.0",
+ "merge-descriptors": "1.0.1",
+ "methods": "~1.1.2",
+ "on-finished": "~2.3.0",
+ "parseurl": "~1.3.1",
+ "path-to-regexp": "0.1.7",
+ "proxy-addr": "~1.1.2",
+ "qs": "6.2.0",
+ "range-parser": "~1.2.0",
+ "send": "0.14.1",
+ "serve-static": "~1.11.1",
+ "type-is": "~1.6.13",
+ "utils-merge": "1.0.0",
+ "vary": "~1.1.0"
+ },
+ "dependencies": {
+ "content-type": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.2.tgz",
+ "integrity": "sha1-t9ETrueo3Se9IRM8TcJSnfFyHu0="
+ },
+ "debug": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz",
+ "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=",
+ "requires": {
+ "ms": "0.7.1"
+ }
+ },
+ "finalhandler": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-0.5.0.tgz",
+ "integrity": "sha1-6VCKvs6bbbqHGmlCodeRG5GRGsc=",
+ "requires": {
+ "debug": "~2.2.0",
+ "escape-html": "~1.0.3",
+ "on-finished": "~2.3.0",
+ "statuses": "~1.3.0",
+ "unpipe": "~1.0.0"
+ }
+ },
+ "ms": {
+ "version": "0.7.1",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz",
+ "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg="
+ },
+ "qs": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.2.0.tgz",
+ "integrity": "sha1-O3hIwDwt7OaalSKw+ujEEm10Xzs="
+ }
+ }
+ },
+ "express-handlebars": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/express-handlebars/-/express-handlebars-3.0.0.tgz",
+ "integrity": "sha1-gKBwu4GbCeSvLKbQeA91zgXnXC8=",
+ "requires": {
+ "glob": "^6.0.4",
+ "graceful-fs": "^4.1.2",
+ "handlebars": "^4.0.5",
+ "object.assign": "^4.0.3",
+ "promise": "^7.0.0"
+ }
+ },
+ "extend": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.0.tgz",
+ "integrity": "sha1-WkdDU7nzNT3dgXbf03uRyDpG8dQ="
+ },
+ "extsprintf": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.0.2.tgz",
+ "integrity": "sha1-4QgOBljjALBilJkMxw4VAiNf1VA="
+ },
+ "eyes": {
+ "version": "0.1.8",
+ "resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz",
+ "integrity": "sha1-Ys8SAjTGg3hdkCNIqADvPgzCC8A="
+ },
+ "fast-levenshtein": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+ "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc="
+ },
+ "finalhandler": {
+ "version": "0.5.1",
+ "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-0.5.1.tgz",
+ "integrity": "sha1-LEANjUUwk1vCMlScX6OF7Afeb80=",
+ "requires": {
+ "debug": "~2.2.0",
+ "escape-html": "~1.0.3",
+ "on-finished": "~2.3.0",
+ "statuses": "~1.3.1",
+ "unpipe": "~1.0.0"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz",
+ "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=",
+ "requires": {
+ "ms": "0.7.1"
+ }
+ },
+ "ms": {
+ "version": "0.7.1",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz",
+ "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg="
+ }
+ }
+ },
+ "find-up": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz",
+ "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=",
+ "requires": {
+ "path-exists": "^2.0.0",
+ "pinkie-promise": "^2.0.0"
+ }
+ },
+ "foreach": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz",
+ "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k="
+ },
+ "forever-agent": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
+ "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE="
+ },
+ "form-data": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.2.tgz",
+ "integrity": "sha1-icNTQAi5fq2ky7FX1Y9vXfAl6uQ=",
+ "requires": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.5",
+ "mime-types": "^2.1.12"
+ }
+ },
+ "formidable": {
+ "version": "1.0.17",
+ "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.0.17.tgz",
+ "integrity": "sha1-71SRSQ+UM7cF+qdyScmQKa40hVk="
+ },
+ "forwarded": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.0.tgz",
+ "integrity": "sha1-Ge+YdMSuHCl7zweP3mOgm2aoQ2M="
+ },
+ "fresh": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.3.0.tgz",
+ "integrity": "sha1-ZR+DjiJCTnVm3hYdg1jKoZn4PU8="
+ },
+ "fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
+ },
+ "function-bind": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.0.tgz",
+ "integrity": "sha1-FhdnFMgBeY5Ojyz391KUZ7tKV3E="
+ },
+ "gelf-stream": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/gelf-stream/-/gelf-stream-1.1.1.tgz",
+ "integrity": "sha1-nOqbY4asMBx0GDjKPLkeZtv79mk=",
+ "requires": {
+ "gelfling": "^0.3.0"
+ }
+ },
+ "gelfling": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/gelfling/-/gelfling-0.3.1.tgz",
+ "integrity": "sha1-M2qY+BUQ+a4K8qSU4XRooRap3AQ="
+ },
+ "generate-function": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.0.0.tgz",
+ "integrity": "sha1-aFj+fAlpt9TpCTM3ZHrHn2DfvnQ="
+ },
+ "generate-object-property": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz",
+ "integrity": "sha1-nA4cQDCM6AT0eDYYuTf6iPmdUNA=",
+ "requires": {
+ "is-property": "^1.0.0"
+ }
+ },
+ "get-caller-file": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.2.tgz",
+ "integrity": "sha1-9wLmMSfn4jHBYKgMFVSstw1QR+U="
+ },
+ "getpass": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.6.tgz",
+ "integrity": "sha1-KD/9n8ElaECHUxHBtg6MQBhxEOY=",
+ "requires": {
+ "assert-plus": "^1.0.0"
+ },
+ "dependencies": {
+ "assert-plus": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
+ "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU="
+ }
+ }
+ },
+ "glob": {
+ "version": "6.0.4",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz",
+ "integrity": "sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI=",
+ "requires": {
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "2 || 3",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "graceful-fs": {
+ "version": "4.1.11",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
+ "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg="
+ },
+ "graceful-readlink": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz",
+ "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU="
+ },
+ "growl": {
+ "version": "1.9.2",
+ "resolved": "https://registry.npmjs.org/growl/-/growl-1.9.2.tgz",
+ "integrity": "sha1-Dqd0NxXbjY3ixe3hd14bRayFwC8="
+ },
+ "handlebars": {
+ "version": "4.0.6",
+ "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.0.6.tgz",
+ "integrity": "sha1-LORISFBTf5yXqAJtU5m5NcTtTtc=",
+ "requires": {
+ "async": "^1.4.0",
+ "optimist": "^0.6.1",
+ "source-map": "^0.4.4",
+ "uglify-js": "^2.6"
+ },
+ "dependencies": {
+ "async": {
+ "version": "1.5.2",
+ "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz",
+ "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo="
+ }
+ }
+ },
+ "har-validator": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-2.0.6.tgz",
+ "integrity": "sha1-zcvAgYgmWtEZtqWnyKtw7s+10n0=",
+ "requires": {
+ "chalk": "^1.1.1",
+ "commander": "^2.9.0",
+ "is-my-json-valid": "^2.12.4",
+ "pinkie-promise": "^2.0.0"
+ }
+ },
+ "has-ansi": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
+ "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=",
+ "requires": {
+ "ansi-regex": "^2.0.0"
+ }
+ },
+ "has-color": {
+ "version": "0.1.7",
+ "resolved": "https://registry.npmjs.org/has-color/-/has-color-0.1.7.tgz",
+ "integrity": "sha1-ZxRKUmDDT8PMpnfQQdr1L+e3iy8="
+ },
+ "has-flag": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz",
+ "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo="
+ },
+ "hat": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/hat/-/hat-0.0.3.tgz",
+ "integrity": "sha1-uwFKnmSzeIrtgAWRdBPU/z1QLYo="
+ },
+ "hawk": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz",
+ "integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=",
+ "requires": {
+ "boom": "2.x.x",
+ "cryptiles": "2.x.x",
+ "hoek": "2.x.x",
+ "sntp": "1.x.x"
+ }
+ },
+ "hoek": {
+ "version": "2.16.3",
+ "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz",
+ "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0="
+ },
+ "hosted-git-info": {
+ "version": "2.1.5",
+ "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.1.5.tgz",
+ "integrity": "sha1-C6gdkNouJas0ozLm7HeTbhWYEYs="
+ },
+ "hot-shots": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/hot-shots/-/hot-shots-4.3.1.tgz",
+ "integrity": "sha1-WKbB/3F/JWc75NL3NtHJTV154jk="
+ },
+ "htmlparser2": {
+ "version": "3.8.3",
+ "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.8.3.tgz",
+ "integrity": "sha1-mWwosZFRaovoZQGn15dX5ccMEGg=",
+ "requires": {
+ "domelementtype": "1",
+ "domhandler": "2.3",
+ "domutils": "1.5",
+ "entities": "1.0",
+ "readable-stream": "1.1"
+ },
+ "dependencies": {
+ "entities": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-1.0.0.tgz",
+ "integrity": "sha1-sph6o4ITR/zeZCsk/fyeT7cSvyY="
+ }
+ }
+ },
+ "http-errors": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.5.1.tgz",
+ "integrity": "sha1-eIwNLB3iyBuebowBhDtrl+uSB1A=",
+ "requires": {
+ "inherits": "2.0.3",
+ "setprototypeof": "1.0.2",
+ "statuses": ">= 1.3.1 < 2"
+ }
+ },
+ "http-signature": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz",
+ "integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=",
+ "requires": {
+ "assert-plus": "^0.2.0",
+ "jsprim": "^1.2.2",
+ "sshpk": "^1.7.0"
+ }
+ },
+ "i": {
+ "version": "0.3.5",
+ "resolved": "https://registry.npmjs.org/i/-/i-0.3.5.tgz",
+ "integrity": "sha1-HSuFQVjsgWkRPGy39raAHpniEdU="
+ },
+ "iconv-lite": {
+ "version": "0.4.15",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.15.tgz",
+ "integrity": "sha1-/iZaIYrGpXz+hUkn6dBMGYJe3es="
+ },
+ "inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+ "requires": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "inherit": {
+ "version": "2.2.6",
+ "resolved": "https://registry.npmjs.org/inherit/-/inherit-2.2.6.tgz",
+ "integrity": "sha1-8WFLBshUToEo5CKchjR9tzrZeI0="
+ },
+ "inherits": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+ "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
+ },
+ "invert-kv": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz",
+ "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY="
+ },
+ "ipaddr.js": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.2.0.tgz",
+ "integrity": "sha1-irpJyRknmVhb3WQ+DMtQ6K53e6Q="
+ },
+ "is-arguments": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.0.2.tgz",
+ "integrity": "sha1-B+MK15UxhEF5tkLS2DmUNRgshyc="
+ },
+ "is-arrayish": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
+ "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0="
+ },
+ "is-builtin-module": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz",
+ "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=",
+ "requires": {
+ "builtin-modules": "^1.0.0"
+ }
+ },
+ "is-fullwidth-code-point": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
+ "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
+ "requires": {
+ "number-is-nan": "^1.0.0"
+ }
+ },
+ "is-my-json-valid": {
+ "version": "2.15.0",
+ "resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.15.0.tgz",
+ "integrity": "sha1-k27do8o8IR/ZjzstPgjaQ/eykVs=",
+ "requires": {
+ "generate-function": "^2.0.0",
+ "generate-object-property": "^1.1.0",
+ "jsonpointer": "^4.0.0",
+ "xtend": "^4.0.0"
+ }
+ },
+ "is-property": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz",
+ "integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ="
+ },
+ "is-typedarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
+ "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo="
+ },
+ "is-utf8": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz",
+ "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI="
+ },
+ "isarray": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+ "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
+ },
+ "isexe": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-1.1.2.tgz",
+ "integrity": "sha1-NvPiLmB1CSD15yQaR2qMakInWtA="
+ },
+ "isstream": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
+ "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo="
+ },
+ "istanbul": {
+ "version": "0.4.5",
+ "resolved": "https://registry.npmjs.org/istanbul/-/istanbul-0.4.5.tgz",
+ "integrity": "sha1-ZcfXPUxNqE1POsMQuRj7C4Azczs=",
+ "requires": {
+ "abbrev": "1.0.x",
+ "async": "1.x",
+ "escodegen": "1.8.x",
+ "esprima": "2.7.x",
+ "glob": "^5.0.15",
+ "handlebars": "^4.0.1",
+ "js-yaml": "3.x",
+ "mkdirp": "0.5.x",
+ "nopt": "3.x",
+ "once": "1.x",
+ "resolve": "1.1.x",
+ "supports-color": "^3.1.0",
+ "which": "^1.1.1",
+ "wordwrap": "^1.0.0"
+ },
+ "dependencies": {
+ "async": {
+ "version": "1.5.2",
+ "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz",
+ "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo="
+ },
+ "glob": {
+ "version": "5.0.15",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz",
+ "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=",
+ "requires": {
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "2 || 3",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "supports-color": {
+ "version": "3.2.3",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz",
+ "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=",
+ "requires": {
+ "has-flag": "^1.0.0"
+ }
+ },
+ "wordwrap": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
+ "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus="
+ }
+ }
+ },
+ "jade": {
+ "version": "0.26.3",
+ "resolved": "https://registry.npmjs.org/jade/-/jade-0.26.3.tgz",
+ "integrity": "sha1-jxDXl32NefL2/4YqgbBRPMslaGw=",
+ "requires": {
+ "commander": "0.6.1",
+ "mkdirp": "0.3.0"
+ },
+ "dependencies": {
+ "commander": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-0.6.1.tgz",
+ "integrity": "sha1-+mihT2qUXVTbvlDYzbMyDp47GgY="
+ },
+ "mkdirp": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz",
+ "integrity": "sha1-G79asbqCevI1dRQ0kEJkVfSB/h4="
+ }
+ }
+ },
+ "js-yaml": {
+ "version": "3.7.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.7.0.tgz",
+ "integrity": "sha1-XJZ93YN6m/3KXy3oQlOr6KHAO4A=",
+ "requires": {
+ "argparse": "^1.0.7",
+ "esprima": "^2.6.0"
+ }
+ },
+ "jscs": {
+ "version": "3.0.7",
+ "resolved": "https://registry.npmjs.org/jscs/-/jscs-3.0.7.tgz",
+ "integrity": "sha1-cUG03/W4bjLQ6Z12S4NnZ8MNIBo=",
+ "requires": {
+ "chalk": "~1.1.0",
+ "cli-table": "~0.3.1",
+ "commander": "~2.9.0",
+ "cst": "^0.4.3",
+ "estraverse": "^4.1.0",
+ "exit": "~0.1.2",
+ "glob": "^5.0.1",
+ "htmlparser2": "3.8.3",
+ "js-yaml": "~3.4.0",
+ "jscs-jsdoc": "^2.0.0",
+ "jscs-preset-wikimedia": "~1.0.0",
+ "jsonlint": "~1.6.2",
+ "lodash": "~3.10.0",
+ "minimatch": "~3.0.0",
+ "natural-compare": "~1.2.2",
+ "pathval": "~0.1.1",
+ "prompt": "~0.2.14",
+ "reserved-words": "^0.1.1",
+ "resolve": "^1.1.6",
+ "strip-bom": "^2.0.0",
+ "strip-json-comments": "~1.0.2",
+ "to-double-quotes": "^2.0.0",
+ "to-single-quotes": "^2.0.0",
+ "vow": "~0.4.8",
+ "vow-fs": "~0.3.4",
+ "xmlbuilder": "^3.1.0"
+ },
+ "dependencies": {
+ "estraverse": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz",
+ "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM="
+ },
+ "glob": {
+ "version": "5.0.15",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz",
+ "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=",
+ "requires": {
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "2 || 3",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "js-yaml": {
+ "version": "3.4.6",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.4.6.tgz",
+ "integrity": "sha1-a+GyP2JJ9T0pM3D9TRqqY84bTrA=",
+ "requires": {
+ "argparse": "^1.0.2",
+ "esprima": "^2.6.0",
+ "inherit": "^2.2.2"
+ }
+ }
+ }
+ },
+ "jscs-jsdoc": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/jscs-jsdoc/-/jscs-jsdoc-2.0.0.tgz",
+ "integrity": "sha1-9T684CmqMSW9iCkLpQ1k1FEKSHE=",
+ "requires": {
+ "comment-parser": "^0.3.1",
+ "jsdoctypeparser": "~1.2.0"
+ }
+ },
+ "jscs-preset-wikimedia": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/jscs-preset-wikimedia/-/jscs-preset-wikimedia-1.0.0.tgz",
+ "integrity": "sha1-//VjNCA4/C6IJre7cwnDrjQG/H4="
+ },
+ "jsdoctypeparser": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/jsdoctypeparser/-/jsdoctypeparser-1.2.0.tgz",
+ "integrity": "sha1-597cFToRhJ/8UUEUSuhqfvDCU5I=",
+ "requires": {
+ "lodash": "^3.7.0"
+ }
+ },
+ "jshint": {
+ "version": "2.9.4",
+ "resolved": "https://registry.npmjs.org/jshint/-/jshint-2.9.4.tgz",
+ "integrity": "sha1-XjupeEjVKQJz21FK7kf+JM9ZKTQ=",
+ "requires": {
+ "cli": "~1.0.0",
+ "console-browserify": "1.1.x",
+ "exit": "0.1.x",
+ "htmlparser2": "3.8.x",
+ "lodash": "3.7.x",
+ "minimatch": "~3.0.2",
+ "shelljs": "0.3.x",
+ "strip-json-comments": "1.0.x"
+ },
+ "dependencies": {
+ "lodash": {
+ "version": "3.7.0",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.7.0.tgz",
+ "integrity": "sha1-Nni9irmVBXwHreg27S7wh9qBHUU="
+ }
+ }
+ },
+ "json-schema": {
+ "version": "0.2.3",
+ "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz",
+ "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM="
+ },
+ "json-stringify-safe": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
+ "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus="
+ },
+ "jsonlint": {
+ "version": "1.6.2",
+ "resolved": "https://registry.npmjs.org/jsonlint/-/jsonlint-1.6.2.tgz",
+ "integrity": "sha1-VzcEUIX1XrRVxosf9OvAG9UOiDA=",
+ "requires": {
+ "JSV": ">= 4.0.x",
+ "nomnom": ">= 1.5.x"
+ }
+ },
+ "jsonpointer": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-4.0.1.tgz",
+ "integrity": "sha1-T9kss04OnbPInIYi7PUfm5eMbLk="
+ },
+ "jsprim": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.3.1.tgz",
+ "integrity": "sha1-KnJW9wQSop7jZwqspiWZTE3P8lI=",
+ "requires": {
+ "extsprintf": "1.0.2",
+ "json-schema": "0.2.3",
+ "verror": "1.3.6"
+ }
+ },
+ "kad": {
+ "version": "git+https://github.com/gwicke/kad.git#f35971036f43814043245da82b12d035b7bbfd16",
+ "from": "git+https://github.com/gwicke/kad.git#master"
+ },
+ "kad-fs": {
+ "version": "0.0.4",
+ "resolved": "https://registry.npmjs.org/kad-fs/-/kad-fs-0.0.4.tgz",
+ "integrity": "sha1-Aupapc8iIlclV5YnzP1tJmNyKJo=",
+ "requires": {
+ "readable-stream": "^2.0.4"
+ },
+ "dependencies": {
+ "isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
+ },
+ "readable-stream": {
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.2.tgz",
+ "integrity": "sha1-qeb+w8fdqF+LsbO6cChgRVb8gl4=",
+ "requires": {
+ "buffer-shims": "^1.0.0",
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.1",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~1.0.6",
+ "string_decoder": "~0.10.x",
+ "util-deprecate": "~1.0.1"
+ }
+ }
+ }
+ },
+ "kad-localstorage": {
+ "version": "0.0.7",
+ "resolved": "https://registry.npmjs.org/kad-localstorage/-/kad-localstorage-0.0.7.tgz",
+ "integrity": "sha1-96LngNpT+yi5Q8LFqJTCeaqBDxc=",
+ "requires": {
+ "dom-storage": "^2.0.1"
+ }
+ },
+ "kad-memstore": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/kad-memstore/-/kad-memstore-0.0.1.tgz",
+ "integrity": "sha1-g8t0hJasSRxxNRBMvla4jKc5JHc=",
+ "requires": {
+ "readable-stream": "^2.0.5"
+ },
+ "dependencies": {
+ "isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
+ },
+ "readable-stream": {
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.2.tgz",
+ "integrity": "sha1-qeb+w8fdqF+LsbO6cChgRVb8gl4=",
+ "requires": {
+ "buffer-shims": "^1.0.0",
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.1",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~1.0.6",
+ "string_decoder": "~0.10.x",
+ "util-deprecate": "~1.0.1"
+ }
+ }
+ }
+ },
+ "lcid": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz",
+ "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=",
+ "requires": {
+ "invert-kv": "^1.0.0"
+ }
+ },
+ "lcov-parse": {
+ "version": "0.0.10",
+ "resolved": "https://registry.npmjs.org/lcov-parse/-/lcov-parse-0.0.10.tgz",
+ "integrity": "sha1-GwuP+ayceIklBYK3C3ExXZ2m2aM="
+ },
+ "levn": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
+ "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=",
+ "requires": {
+ "prelude-ls": "~1.1.2",
+ "type-check": "~0.3.2"
+ }
+ },
+ "limitation": {
+ "version": "0.1.9",
+ "resolved": "https://registry.npmjs.org/limitation/-/limitation-0.1.9.tgz",
+ "integrity": "sha1-ugVf9906JnplzGvi3spOpr672wM=",
+ "requires": {
+ "bluebird": "^3.3.1",
+ "kad": "git+https://github.com/gwicke/kad.git#master",
+ "readable-stream": "^2.0.5"
+ },
+ "dependencies": {
+ "isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
+ },
+ "readable-stream": {
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.2.tgz",
+ "integrity": "sha1-qeb+w8fdqF+LsbO6cChgRVb8gl4=",
+ "requires": {
+ "buffer-shims": "^1.0.0",
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.1",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~1.0.6",
+ "string_decoder": "~0.10.x",
+ "util-deprecate": "~1.0.1"
+ }
+ }
+ }
+ },
+ "load-json-file": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz",
+ "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=",
+ "requires": {
+ "graceful-fs": "^4.1.2",
+ "parse-json": "^2.2.0",
+ "pify": "^2.0.0",
+ "pinkie-promise": "^2.0.0",
+ "strip-bom": "^2.0.0"
+ }
+ },
+ "lodash": {
+ "version": "3.10.1",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz",
+ "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y="
+ },
+ "lodash.assign": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz",
+ "integrity": "sha1-DZnzzNem0mHRm9rrkkUAXShYCOc="
+ },
+ "log-driver": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/log-driver/-/log-driver-1.2.5.tgz",
+ "integrity": "sha1-euTsJXMC/XkNVXyxDJcQDYV7AFY="
+ },
+ "lru-cache": {
+ "version": "2.7.3",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz",
+ "integrity": "sha1-bUUk6LlV+V1PW1iFHOId1y+06VI="
+ },
+ "media-typer": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
+ "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
+ },
+ "mediawiki-title": {
+ "version": "0.5.6",
+ "resolved": "https://registry.npmjs.org/mediawiki-title/-/mediawiki-title-0.5.6.tgz",
+ "integrity": "sha1-VJBpKU4ncoofE77T1wXWvuz06iQ="
+ },
+ "merge": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/merge/-/merge-1.2.0.tgz",
+ "integrity": "sha1-dTHjnUlJwoGma4xabgJl6LBYlNo="
+ },
+ "merge-descriptors": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
+ "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
+ },
+ "methods": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
+ "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4="
+ },
+ "mime": {
+ "version": "1.3.4",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-1.3.4.tgz",
+ "integrity": "sha1-EV+eO2s9rylZmDyzjxSaLUDrXVM="
+ },
+ "mime-db": {
+ "version": "1.26.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.26.0.tgz",
+ "integrity": "sha1-6v/NDk/Gk1z4E02iRuLmw1MFrf8="
+ },
+ "mime-types": {
+ "version": "2.1.14",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.14.tgz",
+ "integrity": "sha1-9+99l1g/yvO30oK2+LVnnaselO4=",
+ "requires": {
+ "mime-db": "~1.26.0"
+ }
+ },
+ "minimatch": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.3.tgz",
+ "integrity": "sha1-Kk5AkLlrLbBqnX3wEFWmKnfJt3Q=",
+ "requires": {
+ "brace-expansion": "^1.0.0"
+ }
+ },
+ "minimist": {
+ "version": "0.0.10",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz",
+ "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8="
+ },
+ "mkdirp": {
+ "version": "0.5.1",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
+ "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
+ "requires": {
+ "minimist": "0.0.8"
+ },
+ "dependencies": {
+ "minimist": {
+ "version": "0.0.8",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
+ "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
+ }
+ }
+ },
+ "mocha": {
+ "version": "2.5.3",
+ "resolved": "https://registry.npmjs.org/mocha/-/mocha-2.5.3.tgz",
+ "integrity": "sha1-FhvlvetJZ3HrmzV0UFC2IrWu/Fg=",
+ "requires": {
+ "commander": "2.3.0",
+ "debug": "2.2.0",
+ "diff": "1.4.0",
+ "escape-string-regexp": "1.0.2",
+ "glob": "3.2.11",
+ "growl": "1.9.2",
+ "jade": "0.26.3",
+ "mkdirp": "0.5.1",
+ "supports-color": "1.2.0",
+ "to-iso-string": "0.0.2"
+ },
+ "dependencies": {
+ "commander": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.3.0.tgz",
+ "integrity": "sha1-/UMOiJgy7DU7ms0d4hfBHLPu+HM="
+ },
+ "debug": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz",
+ "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=",
+ "requires": {
+ "ms": "0.7.1"
+ }
+ },
+ "escape-string-regexp": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.2.tgz",
+ "integrity": "sha1-Tbwv5nTnGUnK8/smlc5/LcHZqNE="
+ },
+ "glob": {
+ "version": "3.2.11",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz",
+ "integrity": "sha1-Spc/Y1uRkPcV0QmH1cAP0oFevj0=",
+ "requires": {
+ "inherits": "2",
+ "minimatch": "0.3"
+ }
+ },
+ "minimatch": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz",
+ "integrity": "sha1-J12O2qxPG7MyZHIInnlJyDlGmd0=",
+ "requires": {
+ "lru-cache": "2",
+ "sigmund": "~1.0.0"
+ }
+ },
+ "ms": {
+ "version": "0.7.1",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz",
+ "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg="
+ },
+ "supports-color": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-1.2.0.tgz",
+ "integrity": "sha1-/x7R5hFp0Gs88tWI4YixjYhH4X4="
+ }
+ }
+ },
+ "ms": {
+ "version": "0.7.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz",
+ "integrity": "sha1-riXPJRKziFodldfwN4aNhDESR2U="
+ },
+ "msgpack5": {
+ "version": "3.4.1",
+ "resolved": "https://registry.npmjs.org/msgpack5/-/msgpack5-3.4.1.tgz",
+ "integrity": "sha1-NQ7zWJnGyHc3EP2E2IHd0zQKgRQ=",
+ "requires": {
+ "bl": "^1.0.0",
+ "inherits": "^2.0.1",
+ "readable-stream": "^2.0.1"
+ },
+ "dependencies": {
+ "isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
+ },
+ "readable-stream": {
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.2.tgz",
+ "integrity": "sha1-qeb+w8fdqF+LsbO6cChgRVb8gl4=",
+ "requires": {
+ "buffer-shims": "^1.0.0",
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.1",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~1.0.6",
+ "string_decoder": "~0.10.x",
+ "util-deprecate": "~1.0.1"
+ }
+ }
+ }
+ },
+ "mute-stream": {
+ "version": "0.0.7",
+ "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz",
+ "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s="
+ },
+ "natural-compare": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.2.2.tgz",
+ "integrity": "sha1-H5bWDjFBysG20FZTzg2urHY69qo="
+ },
+ "ncp": {
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/ncp/-/ncp-0.4.2.tgz",
+ "integrity": "sha1-q8xsvT7C7Spyn/bnwfqPAXhKhXQ="
+ },
+ "negotiator": {
+ "version": "git+https://github.com/arlolra/negotiator.git#0418ab4e9a665772b7e233564a4525c9d9a8ec3a",
+ "from": "git+https://github.com/arlolra/negotiator.git#full-parse-access"
+ },
+ "nock": {
+ "version": "8.2.1",
+ "resolved": "https://registry.npmjs.org/nock/-/nock-8.2.1.tgz",
+ "integrity": "sha1-ZMxl4b3TiT9Yy6fhq/3Dj0DwNko=",
+ "requires": {
+ "chai": ">=1.9.2 <4.0.0",
+ "debug": "^2.2.0",
+ "deep-equal": "^1.0.0",
+ "json-stringify-safe": "^5.0.1",
+ "lodash": "~4.9.0",
+ "mkdirp": "^0.5.0",
+ "propagate": "0.4.0",
+ "qs": "^6.0.2"
+ },
+ "dependencies": {
+ "lodash": {
+ "version": "4.9.0",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.9.0.tgz",
+ "integrity": "sha1-TCDXQvA86F3HAODderm8q4Xm/BQ="
+ }
+ }
+ },
+ "node-uuid": {
+ "version": "1.4.7",
+ "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.7.tgz",
+ "integrity": "sha1-baWhdmjEs91ZYjvaEc9/pMH2Cm8="
+ },
+ "nomnom": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/nomnom/-/nomnom-1.8.1.tgz",
+ "integrity": "sha1-IVH3Ikcrp55Qp2/BJbuMjy5Nwqc=",
+ "requires": {
+ "chalk": "~0.4.0",
+ "underscore": "~1.6.0"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.0.0.tgz",
+ "integrity": "sha1-yxAt8cVvUSPquLZ817mAJ6AnkXg="
+ },
+ "chalk": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-0.4.0.tgz",
+ "integrity": "sha1-UZmj3c0MHv4jvAjBsCewYXbgxk8=",
+ "requires": {
+ "ansi-styles": "~1.0.0",
+ "has-color": "~0.1.0",
+ "strip-ansi": "~0.1.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.1.1.tgz",
+ "integrity": "sha1-OeipjQRNFQZgq+SmgIrPcLt7yZE="
+ }
+ }
+ },
+ "nopt": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz",
+ "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=",
+ "requires": {
+ "abbrev": "1"
+ }
+ },
+ "normalize-package-data": {
+ "version": "2.3.5",
+ "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.3.5.tgz",
+ "integrity": "sha1-jZJPFClg4Xd+f/4XBUNjHMfLAt8=",
+ "requires": {
+ "hosted-git-info": "^2.1.4",
+ "is-builtin-module": "^1.0.0",
+ "semver": "2 || 3 || 4 || 5",
+ "validate-npm-package-license": "^3.0.1"
+ }
+ },
+ "nsp": {
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/nsp/-/nsp-2.6.2.tgz",
+ "integrity": "sha1-k9+0xbKIXMNU2MoYtz8J5SucixY=",
+ "requires": {
+ "chalk": "^1.1.1",
+ "cli-table": "^0.3.1",
+ "https-proxy-agent": "^1.0.0",
+ "joi": "^6.9.1",
+ "nodesecurity-npm-utils": "^5.0.0",
+ "path-is-absolute": "^1.0.0",
+ "rc": "^1.1.2",
+ "semver": "^5.0.3",
+ "subcommand": "^2.0.3",
+ "wreck": "^6.3.0"
+ },
+ "dependencies": {
+ "chalk": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
+ "requires": {
+ "ansi-styles": "^2.2.1",
+ "escape-string-regexp": "^1.0.2",
+ "has-ansi": "^2.0.0",
+ "strip-ansi": "^3.0.0",
+ "supports-color": "^2.0.0"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
+ "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4="
+ },
+ "escape-string-regexp": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
+ },
+ "has-ansi": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
+ "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=",
+ "requires": {
+ "ansi-regex": "^2.0.0"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.0.0.tgz",
+ "integrity": "sha1-xQYbbg74qBd15Q9dZhUb9r83EQc="
+ }
+ }
+ },
+ "strip-ansi": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
+ "requires": {
+ "ansi-regex": "^2.0.0"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.0.0.tgz",
+ "integrity": "sha1-xQYbbg74qBd15Q9dZhUb9r83EQc="
+ }
+ }
+ },
+ "supports-color": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
+ "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc="
+ }
+ }
+ },
+ "cli-table": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/cli-table/-/cli-table-0.3.1.tgz",
+ "integrity": "sha1-9TsFJmqLGguTSz0IIebi3FkUriM=",
+ "requires": {
+ "colors": "1.0.3"
+ },
+ "dependencies": {
+ "colors": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz",
+ "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs="
+ }
+ }
+ },
+ "https-proxy-agent": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-1.0.0.tgz",
+ "integrity": "sha1-NffabEjOTdv6JkiRrFk+5f+GceY=",
+ "requires": {
+ "agent-base": "2",
+ "debug": "2",
+ "extend": "3"
+ },
+ "dependencies": {
+ "agent-base": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-2.0.1.tgz",
+ "integrity": "sha1-vY+ehqjrIh//oHvRS+/VXfFCgV4=",
+ "requires": {
+ "extend": "~3.0.0",
+ "semver": "~5.0.1"
+ },
+ "dependencies": {
+ "semver": {
+ "version": "5.0.3",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.0.3.tgz",
+ "integrity": "sha1-d0Zt5YnNXTyV8TiqeLxWmjy10no="
+ }
+ }
+ },
+ "debug": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz",
+ "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=",
+ "requires": {
+ "ms": "0.7.1"
+ },
+ "dependencies": {
+ "ms": {
+ "version": "0.7.1",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz",
+ "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg="
+ }
+ }
+ },
+ "extend": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.0.tgz",
+ "integrity": "sha1-WkdDU7nzNT3dgXbf03uRyDpG8dQ="
+ }
+ }
+ },
+ "joi": {
+ "version": "6.10.1",
+ "resolved": "https://registry.npmjs.org/joi/-/joi-6.10.1.tgz",
+ "integrity": "sha1-TVDDGAeRIgAP5fFq8f+OGRe3fgY=",
+ "requires": {
+ "hoek": "2.x.x",
+ "isemail": "1.x.x",
+ "moment": "2.x.x",
+ "topo": "1.x.x"
+ },
+ "dependencies": {
+ "hoek": {
+ "version": "2.16.3",
+ "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz",
+ "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0="
+ },
+ "isemail": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/isemail/-/isemail-1.2.0.tgz",
+ "integrity": "sha1-vgPfjMPineTSxd9lASY/H6RZXpo="
+ },
+ "moment": {
+ "version": "2.12.0",
+ "resolved": "https://registry.npmjs.org/moment/-/moment-2.12.0.tgz",
+ "integrity": "sha1-3CVg0Zg41sBzGxpq+gRnUmTTYNY="
+ },
+ "topo": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/topo/-/topo-1.1.0.tgz",
+ "integrity": "sha1-6ddRYV0buH3IZdsYL6HKCl71NtU=",
+ "requires": {
+ "hoek": "2.x.x"
+ }
+ }
+ }
+ },
+ "nodesecurity-npm-utils": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/nodesecurity-npm-utils/-/nodesecurity-npm-utils-5.0.0.tgz",
+ "integrity": "sha1-Baow3jDKjIRcQEjpT9eOXgi1Xtk="
+ },
+ "path-is-absolute": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.0.tgz",
+ "integrity": "sha1-Jj2tpmqz8vsQv3+dJN2PPlcO+RI="
+ },
+ "rc": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/rc/-/rc-1.1.6.tgz",
+ "integrity": "sha1-Q2UbdrauU7XIAvEVH6P8OwWZack=",
+ "requires": {
+ "deep-extend": "~0.4.0",
+ "ini": "~1.3.0",
+ "minimist": "^1.2.0",
+ "strip-json-comments": "~1.0.4"
+ },
+ "dependencies": {
+ "deep-extend": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.4.1.tgz",
+ "integrity": "sha1-7+QRPQgIX05vlod1mBD4B0aeIlM="
+ },
+ "ini": {
+ "version": "1.3.4",
+ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.4.tgz",
+ "integrity": "sha1-BTfLedr1m1mhpRff9wbIbsA5Fi4="
+ },
+ "minimist": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
+ "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ="
+ },
+ "strip-json-comments": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-1.0.4.tgz",
+ "integrity": "sha1-HhX7ysl9Pumb8tc7TGVrCCu6+5E="
+ }
+ }
+ },
+ "semver": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.1.0.tgz",
+ "integrity": "sha1-hfLPhVBGXE3wAM99hvawVBBqueU="
+ },
+ "subcommand": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/subcommand/-/subcommand-2.0.3.tgz",
+ "integrity": "sha1-mz/Rp1PjxEHwBBDLRBMdhlX1LDI=",
+ "requires": {
+ "cliclopts": "^1.1.0",
+ "debug": "^2.1.3",
+ "minimist": "^1.2.0",
+ "xtend": "^4.0.0"
+ },
+ "dependencies": {
+ "cliclopts": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/cliclopts/-/cliclopts-1.1.1.tgz",
+ "integrity": "sha1-aUMcfLWvcjd0sNORG0w3USQxkQ8="
+ },
+ "debug": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz",
+ "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=",
+ "requires": {
+ "ms": "0.7.1"
+ },
+ "dependencies": {
+ "ms": {
+ "version": "0.7.1",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz",
+ "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg="
+ }
+ }
+ },
+ "minimist": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
+ "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ="
+ },
+ "xtend": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz",
+ "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68="
+ }
+ }
+ },
+ "wreck": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/wreck/-/wreck-6.3.0.tgz",
+ "integrity": "sha1-oTaXafB7u2LWo3gzanhx/Hc8dAs=",
+ "requires": {
+ "boom": "2.x.x",
+ "hoek": "2.x.x"
+ },
+ "dependencies": {
+ "boom": {
+ "version": "2.10.1",
+ "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz",
+ "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=",
+ "requires": {
+ "hoek": "2.x.x"
+ }
+ },
+ "hoek": {
+ "version": "2.16.3",
+ "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz",
+ "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0="
+ }
+ }
+ }
+ }
+ },
+ "number-is-nan": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
+ "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0="
+ },
+ "oauth-sign": {
+ "version": "0.8.2",
+ "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz",
+ "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM="
+ },
+ "object-keys": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.11.tgz",
+ "integrity": "sha1-xUYBd4rVYPEULODgG8yotW0TQm0="
+ },
+ "object.assign": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.0.4.tgz",
+ "integrity": "sha1-scnMBE7xuf5jYG/BQau7MuFHMMw=",
+ "requires": {
+ "define-properties": "^1.1.2",
+ "function-bind": "^1.1.0",
+ "object-keys": "^1.0.10"
+ }
+ },
+ "on-finished": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
+ "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
+ "requires": {
+ "ee-first": "1.1.1"
+ }
+ },
+ "on-headers": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.1.tgz",
+ "integrity": "sha1-ko9dD0cNSTQmUepnlLCFfBAGk/c="
+ },
+ "once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+ "requires": {
+ "wrappy": "1"
+ }
+ },
+ "optimist": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz",
+ "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=",
+ "requires": {
+ "minimist": "~0.0.1",
+ "wordwrap": "~0.0.2"
+ }
+ },
+ "optionator": {
+ "version": "0.8.2",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz",
+ "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=",
+ "requires": {
+ "deep-is": "~0.1.3",
+ "fast-levenshtein": "~2.0.4",
+ "levn": "~0.3.0",
+ "prelude-ls": "~1.1.2",
+ "type-check": "~0.3.2",
+ "wordwrap": "~1.0.0"
+ },
+ "dependencies": {
+ "wordwrap": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
+ "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus="
+ }
+ }
+ },
+ "os-locale": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz",
+ "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=",
+ "requires": {
+ "lcid": "^1.0.0"
+ }
+ },
+ "parse-json": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz",
+ "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=",
+ "requires": {
+ "error-ex": "^1.2.0"
+ }
+ },
+ "parseurl": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.1.tgz",
+ "integrity": "sha1-yKuMkiO6NIiKpkopeyiFO+wY2lY="
+ },
+ "path-exists": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz",
+ "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=",
+ "requires": {
+ "pinkie-promise": "^2.0.0"
+ }
+ },
+ "path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
+ },
+ "path-to-regexp": {
+ "version": "0.1.7",
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
+ "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
+ },
+ "path-type": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz",
+ "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=",
+ "requires": {
+ "graceful-fs": "^4.1.2",
+ "pify": "^2.0.0",
+ "pinkie-promise": "^2.0.0"
+ }
+ },
+ "pathval": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/pathval/-/pathval-0.1.1.tgz",
+ "integrity": "sha1-CPkRzcqczllCiA2ngXvAtyO2bYI="
+ },
+ "pegjs": {
+ "version": "git+https://github.com/tstarling/pegjs.git#36d584bd7bbc564c86c058c5dfe8053b1fe1d584",
+ "from": "git+https://github.com/tstarling/pegjs.git#fork"
+ },
+ "pify": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+ "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw="
+ },
+ "pinkie": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz",
+ "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA="
+ },
+ "pinkie-promise": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz",
+ "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=",
+ "requires": {
+ "pinkie": "^2.0.0"
+ }
+ },
+ "pkginfo": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/pkginfo/-/pkginfo-0.4.0.tgz",
+ "integrity": "sha1-NJ27f/04CB/K3AhT32h/DHdEzWU="
+ },
+ "prelude-ls": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
+ "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ="
+ },
+ "prfun": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/prfun/-/prfun-2.1.4.tgz",
+ "integrity": "sha1-eHF9m3GM58q1XiC58kOI1fpR1cA=",
+ "requires": {
+ "core-js": "^2.3.0"
+ }
+ },
+ "process-nextick-args": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz",
+ "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M="
+ },
+ "promise": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/promise/-/promise-7.1.1.tgz",
+ "integrity": "sha1-SJZUxpJha4qlWwck+oCbt9tJxb8=",
+ "requires": {
+ "asap": "~2.0.3"
+ }
+ },
+ "prompt": {
+ "version": "0.2.14",
+ "resolved": "https://registry.npmjs.org/prompt/-/prompt-0.2.14.tgz",
+ "integrity": "sha1-V3VPZPVD/XsIRXB8gY7OYY8F/9w=",
+ "requires": {
+ "pkginfo": "0.x.x",
+ "read": "1.0.x",
+ "revalidator": "0.1.x",
+ "utile": "0.2.x",
+ "winston": "0.8.x"
+ }
+ },
+ "propagate": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/propagate/-/propagate-0.4.0.tgz",
+ "integrity": "sha1-8/zKCm/gZzanulcpZgaWF8EwtIE="
+ },
+ "proxy-addr": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-1.1.3.tgz",
+ "integrity": "sha1-3JdQL1ci6IhGez+iKXp7H/R98HQ=",
+ "requires": {
+ "forwarded": "~0.1.0",
+ "ipaddr.js": "1.2.0"
+ }
+ },
+ "punycode": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
+ "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4="
+ },
+ "qs": {
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.2.1.tgz",
+ "integrity": "sha1-zgPF/wk1vB2daanxTL0Y5WjWdiU="
+ },
+ "range-parser": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz",
+ "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4="
+ },
+ "raw-body": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.2.0.tgz",
+ "integrity": "sha1-mUl2z2pQlqQRYoQEkvC9xdbn+5Y=",
+ "requires": {
+ "bytes": "2.4.0",
+ "iconv-lite": "0.4.15",
+ "unpipe": "1.0.0"
+ }
+ },
+ "read": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz",
+ "integrity": "sha1-s9oZvQUkMal2cdRKQmNK33ELQMQ=",
+ "requires": {
+ "mute-stream": "~0.0.4"
+ }
+ },
+ "read-pkg": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz",
+ "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=",
+ "requires": {
+ "load-json-file": "^1.0.0",
+ "normalize-package-data": "^2.3.2",
+ "path-type": "^1.0.0"
+ }
+ },
+ "read-pkg-up": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz",
+ "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=",
+ "requires": {
+ "find-up": "^1.0.0",
+ "read-pkg": "^1.0.0"
+ }
+ },
+ "readable-stream": {
+ "version": "1.1.14",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
+ "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=",
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.1",
+ "isarray": "0.0.1",
+ "string_decoder": "~0.10.x"
+ }
+ },
+ "reduce-component": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/reduce-component/-/reduce-component-1.0.1.tgz",
+ "integrity": "sha1-4Mk1QsV0UhvqE98PlIjtgqt3xdo="
+ },
+ "regenerator-runtime": {
+ "version": "0.10.1",
+ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.10.1.tgz",
+ "integrity": "sha1-JX9BlhzkRVixj3gUr0jBdVn5+us="
+ },
+ "request": {
+ "version": "2.79.0",
+ "resolved": "https://registry.npmjs.org/request/-/request-2.79.0.tgz",
+ "integrity": "sha1-Tf5b9r6LjNw3/Pk+BLZVd3InEN4=",
+ "requires": {
+ "aws-sign2": "~0.6.0",
+ "aws4": "^1.2.1",
+ "caseless": "~0.11.0",
+ "combined-stream": "~1.0.5",
+ "extend": "~3.0.0",
+ "forever-agent": "~0.6.1",
+ "form-data": "~2.1.1",
+ "har-validator": "~2.0.6",
+ "hawk": "~3.1.3",
+ "http-signature": "~1.1.0",
+ "is-typedarray": "~1.0.0",
+ "isstream": "~0.1.2",
+ "json-stringify-safe": "~5.0.1",
+ "mime-types": "~2.1.7",
+ "oauth-sign": "~0.8.1",
+ "qs": "~6.3.0",
+ "stringstream": "~0.0.4",
+ "tough-cookie": "~2.3.0",
+ "tunnel-agent": "~0.4.1",
+ "uuid": "^3.0.0"
+ },
+ "dependencies": {
+ "qs": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.3.0.tgz",
+ "integrity": "sha1-9AOyZPI7wBIox0ExtAfxjV6l1EI="
+ },
+ "uuid": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.0.1.tgz",
+ "integrity": "sha1-ZUS7ot/ajBzxfmKaOjBeK7H+5sE="
+ }
+ }
+ },
+ "require-directory": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+ "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I="
+ },
+ "require-main-filename": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz",
+ "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE="
+ },
+ "reserved-words": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/reserved-words/-/reserved-words-0.1.1.tgz",
+ "integrity": "sha1-b3wV5eVhTFDalhYw2kat3IfAzvI="
+ },
+ "resolve": {
+ "version": "1.1.7",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz",
+ "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs="
+ },
+ "revalidator": {
+ "version": "0.1.8",
+ "resolved": "https://registry.npmjs.org/revalidator/-/revalidator-0.1.8.tgz",
+ "integrity": "sha1-/s5hv6DBtSoga9axgZgYS91SOjs="
+ },
+ "rimraf": {
+ "version": "2.5.4",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.5.4.tgz",
+ "integrity": "sha1-loAAk8vxoMhr2VtGJUZ1NcKd+gQ=",
+ "requires": {
+ "glob": "^7.0.5"
+ },
+ "dependencies": {
+ "glob": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.1.tgz",
+ "integrity": "sha1-gFIR3wT6rxxjo2ADBs31reULLsg=",
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.2",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ }
+ }
+ },
+ "semver": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz",
+ "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8="
+ },
+ "send": {
+ "version": "0.14.1",
+ "resolved": "https://registry.npmjs.org/send/-/send-0.14.1.tgz",
+ "integrity": "sha1-qVSYQyU5L1FTKndgdg5FlZjIn3o=",
+ "requires": {
+ "debug": "~2.2.0",
+ "depd": "~1.1.0",
+ "destroy": "~1.0.4",
+ "encodeurl": "~1.0.1",
+ "escape-html": "~1.0.3",
+ "etag": "~1.7.0",
+ "fresh": "0.3.0",
+ "http-errors": "~1.5.0",
+ "mime": "1.3.4",
+ "ms": "0.7.1",
+ "on-finished": "~2.3.0",
+ "range-parser": "~1.2.0",
+ "statuses": "~1.3.0"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz",
+ "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=",
+ "requires": {
+ "ms": "0.7.1"
+ }
+ },
+ "ms": {
+ "version": "0.7.1",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz",
+ "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg="
+ }
+ }
+ },
+ "serve-favicon": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/serve-favicon/-/serve-favicon-2.3.2.tgz",
+ "integrity": "sha1-3UGeJo3gEqtysxnTN/IQUBP5OB8=",
+ "requires": {
+ "etag": "~1.7.0",
+ "fresh": "0.3.0",
+ "ms": "0.7.2",
+ "parseurl": "~1.3.1"
+ }
+ },
+ "serve-static": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.11.1.tgz",
+ "integrity": "sha1-1sznaTUF9zPHWd5Xvvwa92wPCAU=",
+ "requires": {
+ "encodeurl": "~1.0.1",
+ "escape-html": "~1.0.3",
+ "parseurl": "~1.3.1",
+ "send": "0.14.1"
+ }
+ },
+ "service-runner": {
+ "version": "2.1.13",
+ "resolved": "https://registry.npmjs.org/service-runner/-/service-runner-2.1.13.tgz",
+ "integrity": "sha1-6P94uTIw19gx6j7VWHqiKSuCnOs=",
+ "requires": {
+ "bluebird": "^3.4.6",
+ "bunyan": "^1.8.1",
+ "bunyan-syslog-udp": "^0.1.0",
+ "extend": "^3.0.0",
+ "gelf-stream": "^1.1.1",
+ "hot-shots": "^4.2.0",
+ "js-yaml": "^3.6.1",
+ "limitation": "^0.1.9",
+ "semver": "^5.3.0",
+ "yargs": "^5.0.0"
+ }
+ },
+ "set-blocking": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
+ "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc="
+ },
+ "setprototypeof": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.2.tgz",
+ "integrity": "sha1-gaVSFB7BBLiOic44MQOtXGZWTQg="
+ },
+ "shelljs": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.3.0.tgz",
+ "integrity": "sha1-NZbmMHp4FUT1kfN9phg2DzHbV7E="
+ },
+ "sigmund": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz",
+ "integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA="
+ },
+ "simplediff": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/simplediff/-/simplediff-0.1.1.tgz",
+ "integrity": "sha1-sMrusJMiM3ADPGw6oRMNyGxqCHw="
+ },
+ "sntp": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz",
+ "integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=",
+ "requires": {
+ "hoek": "2.x.x"
+ }
+ },
+ "source-map": {
+ "version": "0.4.4",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz",
+ "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=",
+ "requires": {
+ "amdefine": ">=0.0.4"
+ }
+ },
+ "source-map-support": {
+ "version": "0.4.10",
+ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.10.tgz",
+ "integrity": "sha1-17GQOAQKFMCDehjmMKGWRTlSs3g=",
+ "requires": {
+ "source-map": "^0.5.3"
+ },
+ "dependencies": {
+ "source-map": {
+ "version": "0.5.6",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz",
+ "integrity": "sha1-dc449SvwczxafwwRjYEzSiu19BI="
+ }
+ }
+ },
+ "spdx-correct": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-1.0.2.tgz",
+ "integrity": "sha1-SzBz2TP/UfORLwOsVRlJikFQ20A=",
+ "requires": {
+ "spdx-license-ids": "^1.0.2"
+ }
+ },
+ "spdx-expression-parse": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-1.0.4.tgz",
+ "integrity": "sha1-m98vIOH0DtRH++JzJmGR/O1RYmw="
+ },
+ "spdx-license-ids": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-1.2.2.tgz",
+ "integrity": "sha1-yd96NCRZSt5r0RkA1ZZpbcBrrFc="
+ },
+ "sprintf-js": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+ "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw="
+ },
+ "sshpk": {
+ "version": "1.10.2",
+ "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.10.2.tgz",
+ "integrity": "sha1-1agEziJpVRVjjnmNviMnPeBwpfo=",
+ "requires": {
+ "asn1": "~0.2.3",
+ "assert-plus": "^1.0.0",
+ "bcrypt-pbkdf": "^1.0.0",
+ "dashdash": "^1.12.0",
+ "ecc-jsbn": "~0.1.1",
+ "getpass": "^0.1.1",
+ "jodid25519": "^1.0.0",
+ "jsbn": "~0.1.0",
+ "tweetnacl": "~0.14.0"
+ },
+ "dependencies": {
+ "assert-plus": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
+ "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU="
+ }
+ }
+ },
+ "stack-trace": {
+ "version": "0.0.9",
+ "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.9.tgz",
+ "integrity": "sha1-qPbq7KkGdMMz58Q5U/J1tFFRBpU="
+ },
+ "statuses": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz",
+ "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4="
+ },
+ "streamsearch": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz",
+ "integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo="
+ },
+ "string-width": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
+ "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
+ "requires": {
+ "code-point-at": "^1.0.0",
+ "is-fullwidth-code-point": "^1.0.0",
+ "strip-ansi": "^3.0.0"
+ }
+ },
+ "string_decoder": {
+ "version": "0.10.31",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
+ "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ="
+ },
+ "stringstream": {
+ "version": "0.0.5",
+ "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz",
+ "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg="
+ },
+ "strip-ansi": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
+ "requires": {
+ "ansi-regex": "^2.0.0"
+ }
+ },
+ "strip-bom": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz",
+ "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=",
+ "requires": {
+ "is-utf8": "^0.2.0"
+ }
+ },
+ "strip-json-comments": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-1.0.4.tgz",
+ "integrity": "sha1-HhX7ysl9Pumb8tc7TGVrCCu6+5E="
+ },
+ "superagent": {
+ "version": "1.8.5",
+ "resolved": "https://registry.npmjs.org/superagent/-/superagent-1.8.5.tgz",
+ "integrity": "sha1-HA3cOvMOgOuE68BcshItqP6UC1U=",
+ "requires": {
+ "component-emitter": "~1.2.0",
+ "cookiejar": "2.0.6",
+ "debug": "2",
+ "extend": "3.0.0",
+ "form-data": "1.0.0-rc3",
+ "formidable": "~1.0.14",
+ "methods": "~1.1.1",
+ "mime": "1.3.4",
+ "qs": "2.3.3",
+ "readable-stream": "1.0.27-1",
+ "reduce-component": "1.0.1"
+ },
+ "dependencies": {
+ "async": {
+ "version": "1.5.2",
+ "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz",
+ "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo="
+ },
+ "form-data": {
+ "version": "1.0.0-rc3",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-1.0.0-rc3.tgz",
+ "integrity": "sha1-01vGLn+8KTeuePlIqqDTjZBgdXc=",
+ "requires": {
+ "async": "^1.4.0",
+ "combined-stream": "^1.0.5",
+ "mime-types": "^2.1.3"
+ }
+ },
+ "qs": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-2.3.3.tgz",
+ "integrity": "sha1-6eha2+ddoLvkyOBHaghikPhjtAQ="
+ },
+ "readable-stream": {
+ "version": "1.0.27-1",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.27-1.tgz",
+ "integrity": "sha1-a2eYPCA1fO/QfwFlABoW1xDZEHg=",
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.1",
+ "isarray": "0.0.1",
+ "string_decoder": "~0.10.x"
+ }
+ }
+ }
+ },
+ "supertest": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/supertest/-/supertest-1.2.0.tgz",
+ "integrity": "sha1-hQp5X5Bo0vrxngF5n/CZYuDOQ74=",
+ "requires": {
+ "methods": "1.x",
+ "superagent": "^1.7.2"
+ }
+ },
+ "supports-color": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
+ "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc="
+ },
+ "to-double-quotes": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/to-double-quotes/-/to-double-quotes-2.0.0.tgz",
+ "integrity": "sha1-qvIx1vqUiUn4GTAburRITYWI5Kc="
+ },
+ "to-iso-string": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/to-iso-string/-/to-iso-string-0.0.2.tgz",
+ "integrity": "sha1-TcGeZk38y+Jb2NtQiwDG2hWCVdE="
+ },
+ "to-single-quotes": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/to-single-quotes/-/to-single-quotes-2.0.1.tgz",
+ "integrity": "sha1-fMKRUfD18sQZRvEZ9ZMv5VQXASU="
+ },
+ "tough-cookie": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.2.tgz",
+ "integrity": "sha1-8IH3bkyFcg5sN6X6ztc3FQ2EByo=",
+ "requires": {
+ "punycode": "^1.4.1"
+ }
+ },
+ "tunnel-agent": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.4.3.tgz",
+ "integrity": "sha1-Y3PbdpCf5XDgjXNYM2Xtgop07us="
+ },
+ "type-check": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",
+ "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=",
+ "requires": {
+ "prelude-ls": "~1.1.2"
+ }
+ },
+ "type-detect": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-1.0.0.tgz",
+ "integrity": "sha1-diIXzAbbJY7EiQihKY6LlRIejqI="
+ },
+ "type-is": {
+ "version": "1.6.14",
+ "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.14.tgz",
+ "integrity": "sha1-4hljnBfe0coHiQkt1UoDgmuBfLI=",
+ "requires": {
+ "media-typer": "0.3.0",
+ "mime-types": "~2.1.13"
+ }
+ },
+ "underscore": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz",
+ "integrity": "sha1-izixDKze9jM3uLJOT/htRa6lKag="
+ },
+ "unpipe": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
+ "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
+ },
+ "util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
+ },
+ "utile": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/utile/-/utile-0.2.1.tgz",
+ "integrity": "sha1-kwyI6ZCY1iIINMNWy9mncFItkNc=",
+ "requires": {
+ "async": "~0.2.9",
+ "deep-equal": "*",
+ "i": "0.3.x",
+ "mkdirp": "0.x.x",
+ "ncp": "0.4.x",
+ "rimraf": "2.x.x"
+ },
+ "dependencies": {
+ "async": {
+ "version": "0.2.10",
+ "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz",
+ "integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E="
+ }
+ }
+ },
+ "utils-merge": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.0.tgz",
+ "integrity": "sha1-ApT7kiu5N1FTVBxPcJYjHyh8ivg="
+ },
+ "uuid": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-2.0.3.tgz",
+ "integrity": "sha1-Z+LoY3lyFVMN/zGOW/nc6/1Hsho="
+ },
+ "validate-npm-package-license": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz",
+ "integrity": "sha1-KAS6vnEq0zeUWaz74kdGqywwP7w=",
+ "requires": {
+ "spdx-correct": "~1.0.0",
+ "spdx-expression-parse": "~1.0.0"
+ }
+ },
+ "vary": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.0.tgz",
+ "integrity": "sha1-4eWv+70WrnaN0mdDlLmtMCJlMUA="
+ },
+ "verror": {
+ "version": "1.3.6",
+ "resolved": "https://registry.npmjs.org/verror/-/verror-1.3.6.tgz",
+ "integrity": "sha1-z/XfEpRtKX0rqu+qJoniW+AcAFw=",
+ "requires": {
+ "extsprintf": "1.0.2"
+ }
+ },
+ "vow": {
+ "version": "0.4.13",
+ "resolved": "https://registry.npmjs.org/vow/-/vow-0.4.13.tgz",
+ "integrity": "sha1-58FPG9nIvg5zWaRZf+LR720afog="
+ },
+ "vow-fs": {
+ "version": "0.3.6",
+ "resolved": "https://registry.npmjs.org/vow-fs/-/vow-fs-0.3.6.tgz",
+ "integrity": "sha1-LUxZviLivyYY3fWXq0uqkjvnIA0=",
+ "requires": {
+ "glob": "^7.0.5",
+ "uuid": "^2.0.2",
+ "vow": "^0.4.7",
+ "vow-queue": "^0.4.1"
+ },
+ "dependencies": {
+ "glob": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.1.tgz",
+ "integrity": "sha1-gFIR3wT6rxxjo2ADBs31reULLsg=",
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.2",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ }
+ }
+ },
+ "vow-queue": {
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/vow-queue/-/vow-queue-0.4.2.tgz",
+ "integrity": "sha1-5/4XFg4Vx8QYTRtmapvGThjjAYQ=",
+ "requires": {
+ "vow": "~0.4.0"
+ }
+ },
+ "which": {
+ "version": "1.2.12",
+ "resolved": "https://registry.npmjs.org/which/-/which-1.2.12.tgz",
+ "integrity": "sha1-3me15FAmnxlJCe8j7OTr5Bb6EZI=",
+ "requires": {
+ "isexe": "^1.1.1"
+ }
+ },
+ "which-module": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz",
+ "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8="
+ },
+ "window-size": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.2.0.tgz",
+ "integrity": "sha1-tDFbtCFKPXBY6+7okuE/ok2YsHU="
+ },
+ "winston": {
+ "version": "0.8.3",
+ "resolved": "https://registry.npmjs.org/winston/-/winston-0.8.3.tgz",
+ "integrity": "sha1-ZLar9M0Brcrv1QCTk7HY6L7BnbA=",
+ "requires": {
+ "async": "0.2.x",
+ "colors": "0.6.x",
+ "cycle": "1.0.x",
+ "eyes": "0.1.x",
+ "isstream": "0.1.x",
+ "pkginfo": "0.3.x",
+ "stack-trace": "0.0.x"
+ },
+ "dependencies": {
+ "async": {
+ "version": "0.2.10",
+ "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz",
+ "integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E="
+ },
+ "colors": {
+ "version": "0.6.2",
+ "resolved": "https://registry.npmjs.org/colors/-/colors-0.6.2.tgz",
+ "integrity": "sha1-JCP+ZnisDF2uiFLl0OW+CMmXq8w="
+ },
+ "pkginfo": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/pkginfo/-/pkginfo-0.3.1.tgz",
+ "integrity": "sha1-Wyn2qB9wcXFC4J52W76rl7T4HiE="
+ }
+ }
+ },
+ "wordwrap": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz",
+ "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc="
+ },
+ "wrap-ansi": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",
+ "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=",
+ "requires": {
+ "string-width": "^1.0.1",
+ "strip-ansi": "^3.0.1"
+ }
+ },
+ "wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
+ },
+ "xmlbuilder": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-3.1.0.tgz",
+ "integrity": "sha1-LIaIjy1OrehQ+jjKf3Ij9yCVFuE=",
+ "requires": {
+ "lodash": "^3.5.0"
+ }
+ },
+ "xtend": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz",
+ "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68="
+ },
+ "y18n": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz",
+ "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE="
+ },
+ "yargs": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-5.0.0.tgz",
+ "integrity": "sha1-M1UUSXfQV1fbuG1uOOwFYSOzpm4=",
+ "requires": {
+ "cliui": "^3.2.0",
+ "decamelize": "^1.1.1",
+ "get-caller-file": "^1.0.1",
+ "lodash.assign": "^4.2.0",
+ "os-locale": "^1.4.0",
+ "read-pkg-up": "^1.0.1",
+ "require-directory": "^2.1.1",
+ "require-main-filename": "^1.0.1",
+ "set-blocking": "^2.0.0",
+ "string-width": "^1.0.2",
+ "which-module": "^1.0.0",
+ "window-size": "^0.2.0",
+ "y18n": "^3.2.1",
+ "yargs-parser": "^3.2.0"
+ }
+ },
+ "yargs-parser": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-3.2.0.tgz",
+ "integrity": "sha1-UIE1XRnZ0MjF2BrakIy05tGGZk8=",
+ "requires": {
+ "camelcase": "^3.0.0",
+ "lodash.assign": "^4.1.0"
+ }
+ }
+ }
+ },
+ "parsoid-jsapi": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/parsoid-jsapi/-/parsoid-jsapi-0.0.1.tgz",
+ "integrity": "sha1-AFiGuWCiP+0rcBogMH9rOr/6A7k=",
+ "requires": {
+ "domino": "^1.0.28",
+ "parsoid": "^0.7.1"
+ },
+ "dependencies": {
+ "domino": {
+ "version": "1.0.30",
+ "resolved": "https://registry.npmjs.org/domino/-/domino-1.0.30.tgz",
+ "integrity": "sha512-ikq8WiDSkICdkElud317F2Sigc6A3EDpWsxWBwIZqOl95km4p/Vc9Rj98id7qKgsjDmExj0AVM7JOd4bb647Xg=="
+ }
+ }
+ },
+ "path-exists": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
+ "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU="
+ },
+ "path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
+ },
+ "path-is-inside": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz",
+ "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=",
+ "dev": true
+ },
+ "path-key": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
+ "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=",
+ "dev": true
+ },
+ "path-parse": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
+ "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
+ "dev": true
+ },
+ "path-to-regexp": {
+ "version": "0.1.7",
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
+ "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
+ },
+ "path-type": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz",
+ "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==",
+ "dev": true,
+ "requires": {
+ "pify": "^3.0.0"
+ }
+ },
+ "performance-now": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
+ "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns="
+ },
+ "picomatch": {
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz",
+ "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==",
+ "dev": true
+ },
+ "pify": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
+ "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
+ "dev": true
+ },
+ "pkg-dir": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz",
+ "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==",
+ "dev": true,
+ "requires": {
+ "find-up": "^3.0.0"
+ }
+ },
+ "postcss": {
+ "version": "7.0.32",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.32.tgz",
+ "integrity": "sha512-03eXong5NLnNCD05xscnGKGDZ98CyzoqPSMjOe6SuoQY7Z2hIj0Ld1g/O/UQRuOle2aRtiIRDg9tDcTGAkLfKw==",
+ "requires": {
+ "chalk": "^2.4.2",
+ "source-map": "^0.6.1",
+ "supports-color": "^6.1.0"
+ },
+ "dependencies": {
+ "JSV": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/JSV/-/JSV-4.0.2.tgz",
+ "integrity": "sha1-0Hf2glVx+CEy+d/67Vh7QCn+/1c="
+ },
+ "abbrev": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.9.tgz",
+ "integrity": "sha1-kbR5JYinc4wl813W9jdSovh3YTU="
+ },
+ "accepts": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.3.tgz",
+ "integrity": "sha1-w8p0NJOGSMPg2cHjKN1otiLChMo=",
+ "requires": {
+ "mime-types": "~2.1.11"
+ }
+ },
+ "alea": {
+ "version": "0.0.9",
+ "resolved": "https://registry.npmjs.org/alea/-/alea-0.0.9.tgz",
+ "integrity": "sha1-9zjLRfg0MAafRc9pzL8xLdV6nho="
+ },
+ "amdefine": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz",
+ "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU="
+ },
+ "ansi-regex": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+ "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
+ },
+ "ansi-styles": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
+ "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4="
+ },
+ "argparse": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.9.tgz",
+ "integrity": "sha1-c9g7wmP4bpf4zE9rrhsOkKfSLIY=",
+ "requires": {
+ "sprintf-js": "~1.0.2"
+ }
+ },
+ "array-flatten": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
+ "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
+ },
+ "asap": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.5.tgz",
+ "integrity": "sha1-UidltQw1EEkOUtfc/ghe+bqWlY8="
+ },
+ "asn1": {
+ "version": "0.2.3",
+ "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz",
+ "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y="
+ },
+ "assert-plus": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz",
+ "integrity": "sha1-104bh+ev/A24qttwIfP+SBAasjQ="
+ },
+ "assertion-error": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.0.2.tgz",
+ "integrity": "sha1-E8pRXYYgbaC6xm6DTdOX2HWBCUw="
+ },
+ "async": {
+ "version": "0.9.2",
+ "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz",
+ "integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0="
+ },
+ "asynckit": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+ "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
+ },
+ "aws-sign2": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz",
+ "integrity": "sha1-FDQt0428yU0OW4fXY81jYSwOeU8="
+ },
+ "aws4": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.5.0.tgz",
+ "integrity": "sha1-Cin/t5wxyecS7rCH6OemS0pW11U="
+ },
+ "babel-runtime": {
+ "version": "6.20.0",
+ "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.20.0.tgz",
+ "integrity": "sha1-hzAL3PTNdw8JvwBIxkIE4XgG0W8=",
+ "requires": {
+ "core-js": "^2.4.0"
+ }
+ },
+ "supports-color": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz",
+ "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==",
+ "requires": {
+ "has-flag": "^3.0.0"
+ }
+ }
+ }
+ },
+ "pre-commit": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/pre-commit/-/pre-commit-1.2.2.tgz",
+ "integrity": "sha1-287g7p3nI15X95xW186UZBpp7sY=",
+ "dev": true,
+ "requires": {
+ "cross-spawn": "^5.0.1",
+ "spawn-sync": "^1.0.15",
+ "which": "1.2.x"
+ },
+ "dependencies": {
+ "cross-spawn": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
+ "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=",
+ "dev": true,
+ "requires": {
+ "lru-cache": "^4.0.1",
+ "shebang-command": "^1.2.0",
+ "which": "^1.2.9"
+ }
+ },
+ "which": {
+ "version": "1.2.14",
+ "resolved": "https://registry.npmjs.org/which/-/which-1.2.14.tgz",
+ "integrity": "sha1-mofEN48D6CfOyvGs31bHNsAcFOU=",
+ "dev": true,
+ "requires": {
+ "isexe": "^2.0.0"
+ }
+ }
+ }
+ },
+ "prelude-ls": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
+ "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=",
+ "dev": true
+ },
+ "prepend-http": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz",
+ "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=",
+ "dev": true
+ },
+ "preq": {
+ "version": "0.5.14",
+ "resolved": "https://registry.npmjs.org/preq/-/preq-0.5.14.tgz",
+ "integrity": "sha512-kuJ5ndEgjs27kTTQ/P2ipPQoHeCJcAI4i97mU3xSjkjx6CsuQOsCe2l5twTGC0SCB5UkzRpmrpXmvN0Ip4ZCxA==",
+ "requires": {
+ "bluebird": "^3.5.5",
+ "request": "^2.88.0",
+ "requestretry": "4.0.2"
+ }
+ },
+ "prfun": {
+ "version": "2.1.5",
+ "resolved": "https://registry.npmjs.org/prfun/-/prfun-2.1.5.tgz",
+ "integrity": "sha512-UCDQscAfQ1HArwvSUobJWbc3sTGLqGpYkRqXUpBZgf+zOWpOjz2dxnpRsOu+qxIj1K0n5UT1wgbCCgetsIwiug==",
+ "requires": {
+ "core-js": "^2.5.3"
+ }
+ },
+ "process-nextick-args": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
+ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
+ },
+ "progress": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
+ "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
+ "dev": true
+ },
+ "proto-list": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz",
+ "integrity": "sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk=",
+ "dev": true
+ },
+ "proxy-addr": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz",
+ "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==",
+ "requires": {
+ "forwarded": "~0.1.2",
+ "ipaddr.js": "1.9.1"
+ }
+ },
+ "prr": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz",
+ "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=",
+ "dev": true,
+ "optional": true
+ },
+ "pseudomap": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
+ "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=",
+ "dev": true
+ },
+ "psl": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/psl/-/psl-1.7.0.tgz",
+ "integrity": "sha512-5NsSEDv8zY70ScRnOTn7bK7eanl2MvFrOrS/R6x+dBt5g1ghnj9Zv90kO8GwT8gxcu2ANyFprnFYB85IogIJOQ=="
+ },
+ "pstree.remy": {
+ "version": "1.1.8",
+ "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz",
+ "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==",
+ "dev": true
+ },
+ "pump": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
+ "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
+ "dev": true,
+ "requires": {
+ "end-of-stream": "^1.1.0",
+ "once": "^1.3.1"
+ }
+ },
+ "punycode": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
+ "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
+ },
+ "pupa": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/pupa/-/pupa-2.0.1.tgz",
"integrity": "sha512-hEJH0s8PXLY/cdXh66tNEQGndDrIKNqNC5xmrysZy3i5C3oEoLna7YAOad+7u125+zH1HNXUmGEkrhb3c2VriA==",
@@ -4862,6 +8253,12 @@
"es6-error": "^4.0.1"
}
},
+ "repeat-string": {
+ "version": "1.6.1",
+ "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz",
+ "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=",
+ "optional": true
+ },
"request": {
"version": "2.88.2",
"resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz",
@@ -4950,6 +8347,15 @@
"signal-exit": "^3.0.2"
}
},
+ "right-align": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz",
+ "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=",
+ "optional": true,
+ "requires": {
+ "align-text": "^0.1.1"
+ }
+ },
"rimraf": {
"version": "2.4.5",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz",
@@ -5678,6 +9084,60 @@
"is-typedarray": "^1.0.0"
}
},
+ "uglify-js": {
+ "version": "2.8.29",
+ "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz",
+ "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=",
+ "optional": true,
+ "requires": {
+ "source-map": "~0.5.1",
+ "uglify-to-browserify": "~1.0.0",
+ "yargs": "~3.10.0"
+ },
+ "dependencies": {
+ "camelcase": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz",
+ "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=",
+ "optional": true
+ },
+ "cliui": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz",
+ "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=",
+ "optional": true,
+ "requires": {
+ "center-align": "^0.1.1",
+ "right-align": "^0.1.1",
+ "wordwrap": "0.0.2"
+ }
+ },
+ "source-map": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+ "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
+ "optional": true
+ },
+ "yargs": {
+ "version": "3.10.0",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz",
+ "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=",
+ "optional": true,
+ "requires": {
+ "camelcase": "^1.0.2",
+ "cliui": "^2.1.0",
+ "decamelize": "^1.0.0",
+ "window-size": "0.1.0"
+ }
+ }
+ }
+ },
+ "uglify-to-browserify": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz",
+ "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=",
+ "optional": true
+ },
"undefsafe": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.3.tgz",
@@ -5987,12 +9447,24 @@
}
}
},
+ "window-size": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz",
+ "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=",
+ "optional": true
+ },
"word-wrap": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
"integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
"dev": true
},
+ "wordwrap": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz",
+ "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=",
+ "optional": true
+ },
"wrap-ansi": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz",
diff --git a/package.json b/package.json
index 87d8451b..b957acd9 100644
--- a/package.json
+++ b/package.json
@@ -54,6 +54,9 @@
},
"homepage": "https://www.mediawiki.org/wiki/RESTBase_services_for_apps",
"dependencies": {
+ "@root/encoding": "^1.0.1",
+ "parsoid-jsapi": "^0.0.1",
+ "prfun": "^2.1.4",
"banana-i18n": "^1.3.1",
"bluebird": "^3.7.2",
"body-parser": "^1.19.0",
diff --git a/routes/page/significant-events.js b/routes/page/significant-events.js
new file mode 100644
index 00000000..1ec68e79
--- /dev/null
+++ b/routes/page/significant-events.js
@@ -0,0 +1,2234 @@
+const router = require('../../lib/util').router();
+const mUtil = require('../../lib/mobile-util');
+const mwapi = require('../../lib/mwapi');
+const mwrestapi = require('../../lib/mwrestapi');
+const BBPromise = require('bluebird');
+const api = require('../../lib/api-util');
+const express = require('express');
+const parsoidApi = require('../../lib/parsoid-access');
+const encoding = require('@root/encoding');
+const ParsoidJS = require('parsoid-jsapi');
+const PRFunPromise = require('prfun');
+const tUtil = require('../../lib/talk/TalkPageTopicUtilities');
+const NodeType = require('../../lib/nodeType');
+let app;
+
+const significantChangesCache = {};
+const maxAllowedCachedArticleSignificantEvents = 100;
+
+const tagReplacements = {
+ A: 'a',
+ B: 'b',
+ I: 'i',
+ SUP: 'sup',
+ SUB: 'sub',
+ DT: 'b',
+ CODE: 'b',
+ BIG: 'b',
+ LI: 'li',
+ OL: 'ol',
+ UL: 'ul',
+ DL: 'ul',
+ DD: 'li'
+};
+
+const tagsToRemoveEntirely = 'script,style';
+const tagsToRemoveButKeepContents = new Set(['P', 'DIV', 'SPAN']);
+const escapeRegex = /[<>]/g;
+const escapeMap = {
+ '<': '<',
+ '>': '>',
+ '&': '&'
+};
+const attributesToRemoveMap = {
+ style: true,
+ id: true,
+ class: true,
+ rel: true,
+ about: true,
+ 'data-mw': true,
+ typeof: true
+};
+
+const escape = text => {
+ return text.replace(escapeRegex, (match) => {
+ return escapeMap[match] || '';
+ });
+};
+
+class CharacterChangeWithSections {
+ constructor(counts, addedSections, deletedSections) {
+ this.counts = counts;
+ this.addedSections = addedSections;
+ this.deletedSections = deletedSections;
+ }
+}
+
+class CharacterChange {
+ constructor(addedCount, deletedCount) {
+ this.addedCount = addedCount;
+ this.deletedCount = deletedCount;
+ }
+
+ totalCount() {
+ return this.addedCount + this.deletedCount;
+ }
+}
+
+class SmallOutputExtended {
+ constructor(revid, parentid, timestamp, user, userid) {
+ this.revid = revid;
+ this.parentid = parentid;
+ this.timestamp = timestamp;
+ this.outputType = 'small-change';
+ this.user = user;
+ this.userid = userid;
+ }
+}
+
+class SmallOutput {
+ constructor(smallOutputExtended) {
+ this.revid = smallOutputExtended.revid;
+ this.parentid = smallOutputExtended.parentid;
+ this.timestamp = smallOutputExtended.timestamp;
+ this.outputType = 'small-change';
+ }
+}
+
+class VandalismOutput {
+ constructor(revid, parentid, timestamp, user, userid, sections) {
+ this.revid = revid;
+ this.parentid = parentid;
+ this.timestamp = timestamp;
+ this.outputType = 'vandalism-revert';
+ this.user = user;
+ this.userid = userid;
+ this.sections = sections;
+ }
+}
+
+class NewReferenceOutput {
+ constructor(sections, templates) {
+ this.outputType = 'new-template';
+ this.sections = sections;
+ this.templates = templates;
+ }
+}
+
+class AddedTextOutputExpanded {
+ constructor(characterChangeWithSections, snippet, snippetType, snippetHighlightRanges) {
+ this.outputType = 'added-text';
+ this.snippet = snippet;
+ this.snippetType = snippetType;
+ this.snippetHighlightRanges = snippetHighlightRanges;
+ this.characterCount = characterChangeWithSections.counts.addedCount;
+ this.sections = characterChangeWithSections.addedSections;
+ }
+}
+
+class AddedTextOutput {
+ constructor(addedTextOutputExpanded) {
+ this.outputType = addedTextOutputExpanded.outputType;
+ this.snippet = addedTextOutputExpanded.snippet;
+ this.snippetType = addedTextOutputExpanded.snippetType;
+ this.characterCount = addedTextOutputExpanded.characterCount;
+ this.sections = addedTextOutputExpanded.sections;
+ }
+}
+
+class DeletedTextOutput {
+ constructor(characterChangeWithSections) {
+ this.outputType = 'deleted-text';
+ this.characterCount = characterChangeWithSections.counts.deletedCount;
+ this.sections = characterChangeWithSections.deletedSections;
+ }
+}
+
+class LargeOutput {
+ constructor(largeOutputExpanded) {
+ this.revid = largeOutputExpanded.revid;
+ this.parentid = largeOutputExpanded.parentid;
+ this.timestamp = largeOutputExpanded.timestamp;
+ this.outputType = 'large-change';
+ this.user = largeOutputExpanded.user;
+ this.userid = largeOutputExpanded.userid;
+ this.significantChanges = largeOutputExpanded.significantChanges;
+ }
+}
+
+class LargeOutputExpanded {
+ constructor(revid, parentid, timestamp, user, userid, significantChanges) {
+ this.revid = revid;
+ this.parentid = parentid;
+ this.timestamp = timestamp;
+ this.outputType = 'large-change';
+ this.user = user;
+ this.userid = userid;
+ this.significantChanges = significantChanges;
+ }
+}
+
+class NewTalkPageTopicExtended {
+ constructor(revid, parentid, timestamp, user, userid, snippet, type, highlightRanges,
+ characterChange, section) {
+ this.revid = revid;
+ this.parentid = parentid;
+ this.timestamp = timestamp;
+ this.outputType = 'new-talk-page-topic';
+ this.snippet = snippet;
+ this.type = type;
+ this.highlightRanges = highlightRanges;
+ this.user = user;
+ this.userid = userid;
+ this.characterChange = characterChange;
+ this.section = section;
+ }
+}
+
+class NewTalkPageTopic {
+ constructor(newTalkPageTopicExpanded) {
+ this.revid = newTalkPageTopicExpanded.revid;
+ this.parentid = newTalkPageTopicExpanded.parentid;
+ this.timestamp = newTalkPageTopicExpanded.timestamp;
+ this.outputType = 'new-talk-page-topic';
+ this.snippet = newTalkPageTopicExpanded.snippet;
+ this.user = newTalkPageTopicExpanded.user;
+ this.userid = newTalkPageTopicExpanded.userid;
+ this.section = newTalkPageTopicExpanded.section;
+ }
+}
+
+class PreformattedSnippet {
+ constructor(revid, outputType, snippet, snippetType, snippetHighlightRanges,
+ indexOfSignificantChanges) {
+ this.revid = revid;
+ this.outputType = outputType;
+ this.snippet = snippet;
+ this.snippetType = snippetType;
+ this.snippetHighlightRanges = snippetHighlightRanges;
+ this.indexOfSignificantChanges = indexOfSignificantChanges;
+ }
+}
+
+function getThreshold(req) {
+ return 100;
+}
+
+function insertSubstringInString(originalString, substring, index) {
+ if (index > 0) {
+ return originalString.substring(0, index) + substring + originalString.substring(index,
+ originalString.length);
+ }
+
+ return substring + originalString;
+}
+
+function stringByRemovingSubstring(originalString, beginningIndex, endIndex) {
+ const firstSubstring = originalString.substring(0,beginningIndex);
+ const secondSubstring = originalString.substring(endIndex);
+ return firstSubstring + secondSubstring;
+}
+
+const diffAndRevisionPromise = (req, revision) => {
+ if (revision.parentid === null || revision.parentid === undefined || revision.parentid === 0) {
+ return Object.assign({
+ revision: revision,
+ body: null
+ });
+ }
+ return mwrestapi.queryForDiff(req, revision.parentid, revision.revid)
+ .then( (response) => {
+ return Object.assign({
+ revision: revision,
+ body: response.body
+ });
+ })
+ .catch(e => {
+ return Object.assign({
+ revision: revision,
+ body: null
+ });
+ });
+};
+
+function removeNodeButPreserveContents(node) {
+ while (node.childNodes.length > 0) {
+ const firstChild = node.firstChild;
+ if (firstChild) {
+ node.parentNode.insertBefore(firstChild, node);
+ }
+ }
+ node.parentNode.removeChild(node);
+}
+
+function renameNodeAndClearOutAttributes(doc, node, newNodeName) {
+
+ // first clear out attributes
+ for (var i = 0; i < node.attributes.length; i++) {
+ var attrib = node.attributes[i];
+ if (attributesToRemoveMap[attrib] === true) {
+ node.removeAttribute(attrib);
+ }
+ }
+
+ // then rename node if necessary
+ if (node.tagName.toLowerCase() === newNodeName.toLowerCase()) {
+ return;
+ }
+
+ var newNode = doc.createElement(newNodeName);
+ newNode.innerHTML = node.innerHTML;
+ node.parentNode.replaceChild(newNode, node);
+}
+
+function recursivelyEvaluateNode(doc, node) {
+
+ if (node.nodeType === NodeType.ELEMENT_NODE) {
+ const sub = tagReplacements[node.tagName];
+
+ if (!sub) {
+ if (tagsToRemoveButKeepContents.has(node.tagName)) {
+ removeNodeButPreserveContents(node);
+ } else {
+ if (node.tagName !== 'BODY') {
+ node.parentNode.removeChild(node);
+ }
+ }
+ } else {
+ renameNodeAndClearOutAttributes(doc, node, sub);
+ }
+ } else if (node.nodeType === NodeType.TEXT_NODE) {
+ node.textContent = escape(node.textContent);
+ }
+
+ if (node.childElementCount > 0) {
+ Array.from(node.children).forEach(child => {
+ recursivelyEvaluateNode(doc, child);
+ });
+ }
+}
+
+class UnexpectedSnippetFormatError extends Error {
+ constructor(message) {
+ super(message);
+ this.name = 'UnexpectedSnippetFormatError';
+ }
+}
+
+const formattedSnippetFromTextPromise = (req, text) => {
+
+ const headers = Object.assign( {
+ accept: 'text/html',
+ profile: 'https://www.mediawiki.org/wiki/Specs/Mobile-HTML/1.0.0',
+ 'content-type': 'multipart/form-data',
+ 'output-mode': 'editPreview'
+ });
+
+ const formData = Object.assign({
+ wikitext: text
+ });
+
+ const request = Object.assign({
+ method: 'post',
+ uri: `https://en.wikipedia.org/api/rest_v1/transform/wikitext/to/mobile-html/${req.params.title}`,
+ query: {},
+ headers: headers,
+ body: formData
+ });
+
+ return req.issueRequest(request)
+ .then( (response) => {
+ return mUtil.createDocument(response.body);
+ }).then( (response) => {
+
+ // removing tags here
+ const elementsToRemove = response.querySelectorAll(tagsToRemoveEntirely);
+ const elementsToRemoveList = Array.prototype.slice.call(elementsToRemove);
+ const references = response.body.getElementsByClassName('mw-references-wrap');
+ const referencesList = Array.prototype.slice.call(references);
+ const finalListToStrip = elementsToRemoveList.concat(referencesList);
+ finalListToStrip.forEach( (script) => {
+ script.parentNode.removeChild(script);
+ });
+
+ // mobile-html endpoint seems to return a wrapper pcs, section and paragraph element.
+ // strip these out as well.
+
+ const pcsElement = response.getElementById('pcs');
+ if (pcsElement) {
+ const section = pcsElement.firstChild;
+ if (section.tagName === 'SECTION') {
+ removeNodeButPreserveContents(section);
+ }
+
+ const paragraph = pcsElement.firstChild;
+ if (paragraph && paragraph.tagName === 'P') {
+ removeNodeButPreserveContents(paragraph);
+ }
+ } else {
+ throw new UnexpectedSnippetFormatError();
+ }
+
+ removeNodeButPreserveContents(pcsElement);
+
+ // now walk remaining doc elements and strip unsupported elements
+
+ recursivelyEvaluateNode(response, response.body);
+
+ return response;
+ })
+ .then((response) => {
+ return response.body.innerHTML;
+ })
+ .catch( (e) => {
+ return null;
+ });
+};
+
+const snippetPromise = (req, preformattedSnippet) => {
+
+ if (preformattedSnippet.snippet === null ||
+ preformattedSnippet.snippet === undefined ||
+ preformattedSnippet.snippetType === null ||
+ preformattedSnippet.snippetType === undefined ||
+ preformattedSnippet.snippetType === 2) {
+ // deleted complete line type.
+ // we shouldn't get here but gracefully return null for snippet without trying to
+ // format
+ preformattedSnippet.snippet = null;
+ return preformattedSnippet;
+ }
+
+ var strippedSnippet;
+ const addHighlightStart = 'ioshighlightstart';
+ const highlightEnd = 'ioshighlightend';
+
+ const addHighlightStartBin = encoding.strToBin(addHighlightStart);
+ const highlightEndBin = encoding.strToBin(highlightEnd);
+
+ // strip deleted ranges from snippet
+ // add added delimiters to type 3 & 5 snippet (Added and deleted words in line)
+ // delimiters are later used for truncation starting point
+ if (preformattedSnippet.outputType === 'large-change') {
+ var snippetBinary = encoding.strToBin(preformattedSnippet.snippet);
+ switch (preformattedSnippet.snippetType) {
+ case 1: // Added complete line
+ break;
+ case 2: // Deleted complete line
+ // should be caught earlier
+ break;
+ case 5:
+ case 3: // Added and deleted words in line
+
+ if (preformattedSnippet.snippetHighlightRanges === null ||
+ preformattedSnippet.snippetHighlightRanges === undefined) {
+ break;
+ }
+
+ // first strip deleted text
+ for (var d = preformattedSnippet.snippetHighlightRanges.length - 1; d >= 0; d-- ) {
+ const range = preformattedSnippet.snippetHighlightRanges[d];
+
+ switch (range.type) {
+ case 0: // Added
+ break;
+ case 1: // Deleted
+ snippetBinary = stringByRemovingSubstring(snippetBinary, range.start,
+ range.start + range.length);
+
+ for (var i = d; i < preformattedSnippet.snippetHighlightRanges.length;
+ i++) {
+ const iRange = preformattedSnippet.snippetHighlightRanges[i];
+ switch (iRange.type) {
+ case 0: // Added
+ iRange.start -= range.length;
+ preformattedSnippet.snippetHighlightRanges[i] = iRange;
+ break;
+ case 1: // Deleted
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ // filter out deleted ranges
+ // eslint-disable-next-line no-case-declarations
+ const addedHighlightRanges = preformattedSnippet.snippetHighlightRanges
+ .filter(range => range.type !== null && range.type !== undefined &&
+ range.type === 0);
+
+ // then add added text delimiters
+ var addOffset = 0;
+ addedHighlightRanges.forEach(function (range) {
+ switch (range.type) {
+ case 0: // Added
+ if (range.start === null || range.start === undefined) {
+ break;
+ }
+ snippetBinary = insertSubstringInString(snippetBinary,
+ addHighlightStartBin, range.start + addOffset);
+ addOffset += addHighlightStartBin.length;
+ break;
+ case 1: // Deleted
+ break;
+ default:
+ break;
+ }
+
+ snippetBinary = insertSubstringInString(snippetBinary, highlightEndBin,
+ range.start + addOffset + range.length);
+ addOffset += highlightEndBin.length;
+ });
+ break;
+ default:
+ break;
+ }
+
+ strippedSnippet = encoding.binToStr(snippetBinary);
+ } else { // new talk page topic
+ strippedSnippet = preformattedSnippet.snippet;
+ }
+
+ // make request to format to mobile-html, reassign result back to snippet
+ return formattedSnippetFromTextPromise(req, strippedSnippet)
+ .then( (response) => {
+ // truncate snippet if needed
+ // note: there's an interesting case where the existence of the delimiters changes the
+ // output. In this case being next to
+ // a wikitext link caused Parsoid to think that this was a new wikitext article
+ // rather than an existing one. I'm forging ahead as I think the worst that could
+ // happen is a missing link
+ // but if other issues occur, we should remove delimiters. With that we would lose
+ // all possibility of stretch goal highlighting
+ // and truncation
+
+ if (!response) {
+ preformattedSnippet.snippet = null;
+ return preformattedSnippet;
+ }
+
+ var truncatedSnippet = response;
+ if (preformattedSnippet.snippetType !== undefined &&
+ preformattedSnippet.snippetType !== null &&
+ preformattedSnippet.snippetType === 5 || preformattedSnippet.snippetType === 3) {
+ // try to trim the areas before the first ioshighlightstart and after the last
+ // ioshighlight start so snippet is
+ // focused on area that changed
+ const firstStart = response.indexOf('ioshighlightstart');
+ const lastStart = response.lastIndexOf('ioshighlightend');
+
+ // early exit here - some highlighting was inadvertantly pruned out,
+ // so don't attempt to truncate or preserve highlight delimiters
+ if (firstStart === -1 || lastStart === -1) {
+ if (firstStart === -1 && lastStart !== -1) {
+ truncatedSnippet = truncatedSnippet.replace(/ioshighlightend/g, '');
+ }
+
+ if (firstStart !== -1 && lastStart === -1) {
+ truncatedSnippet = truncatedSnippet.replace(/ioshighlightstart/g, '');
+ }
+
+ preformattedSnippet.snippet = truncatedSnippet;
+ return preformattedSnippet;
+ }
+
+ // If highlighting starts at the beginning of the line, don't
+ // truncate or prepend ...
+ // Otherwise truncate and prepend ...
+ // all lines truncate at the last highlight end line and suffix with ...
+ // regardless if
+ // it's the last thing in the snippet.
+ var newTruncatedSnippet = truncatedSnippet.slice(0, lastStart + 'ioshighlightend'.length);
+ newTruncatedSnippet = newTruncatedSnippet.concat('...');
+ if (firstStart > 0) {
+ newTruncatedSnippet = newTruncatedSnippet.slice(firstStart);
+ newTruncatedSnippet = '...'.concat(newTruncatedSnippet);
+ }
+
+ const minLengthForTruncation = 'ioshighlightstart'.length + 'ioshighlightend'.length + '...'.length + '...'.length + 20;
+ if (newTruncatedSnippet.length > minLengthForTruncation) {
+ truncatedSnippet = newTruncatedSnippet;
+ }
+
+ // UNCOMMENT THIS LINE IF HIGHLIGHTING IS NOT WORTH IT
+ // truncatedSnippet = truncatedSnippet.replace(/ioshighlightstart/g, '')
+ // .replace(/ioshighlightend/g, '');
+ }
+
+ truncatedSnippet = truncatedSnippet.replace(/ioshighlightstart/g, '').replace(/ioshighlightend/g, '');
+ preformattedSnippet.snippet = truncatedSnippet;
+ return preformattedSnippet;
+ });
+};
+
+const snippetPromises = (req, preformattedSnippets) => {
+ return BBPromise.map(preformattedSnippets, function(preformattedSnippet) {
+ return snippetPromise(req, preformattedSnippet);
+ });
+};
+
+const diffAndRevisionPromises = (req, revisions) => {
+ return BBPromise.map(revisions, function(revision) {
+ return diffAndRevisionPromise(req, revision);
+ });
+};
+
+function getTalkPageTitle(req) {
+ return `Talk:${req.params.title}`;
+}
+
+const talkPageRevisionsPromise = (req, rvStart, rvEnd) => {
+ return mwapi.queryForRevisions(req, getTalkPageTitle(req), 100, rvStart, rvEnd )
+ .catch(e => {
+ return null;
+ });
+};
+
+function keyTitleForCache(req, title) {
+ var keyTitle = title || req.params.title;
+ const threshold = getThreshold(req);
+ keyTitle = `${threshold}-${keyTitle}`;
+
+ return keyTitle;
+}
+
+function getCachedAndUncachedItems(revisions, req, title) {
+
+ // add cache to output and filter out of processing flow
+ const uncachedRevisions = [];
+ const cachedOutput = [];
+
+ if (!revisions) {
+ return Object.assign({
+ uncachedRevisions: [],
+ cachedOutput: []
+ });
+ }
+
+ const keyTitle = keyTitleForCache(req, title);
+ for (var i = 0; i < revisions.length; i++) {
+ const revision = revisions[i];
+ const domainDict = significantChangesCache[req.params.domain];
+ if (domainDict) {
+ const titleDict = domainDict[keyTitle];
+ if (titleDict) {
+ const cacheItem = titleDict[revision.revid];
+ if (cacheItem) {
+ cachedOutput.push(cacheItem);
+ continue;
+ }
+ }
+ }
+
+ uncachedRevisions.push(revision);
+ }
+
+ return Object.assign({
+ uncachedRevisions: uncachedRevisions,
+ cachedOutput: cachedOutput
+ });
+}
+
+function outputTypeCountsTowardsCache(outputType) {
+ return outputType === 'large-change' || outputType === 'vandalism-revert';
+}
+
+function calculateCacheForTitleIsMaxedOut(titleDict) {
+ // eslint-disable-next-line no-restricted-properties
+ var titleArray = Object.values(titleDict);
+ if (titleArray.length > 0) {
+ titleArray.pop(); // remove maxedOut element
+ const filteredObjects = titleArray.filter(outputObject =>
+ outputTypeCountsTowardsCache(outputObject.outputType));
+ return filteredObjects.length >= maxAllowedCachedArticleSignificantEvents;
+ }
+
+ return false;
+}
+
+function cacheForTitleIsMaxedOut(req, title) {
+ const keyTitle = keyTitleForCache(req, title);
+ var domainDict = significantChangesCache[req.params.domain];
+ if (domainDict) {
+ var titleDict = domainDict[keyTitle];
+ if (titleDict) {
+ if (titleDict.maxedOut) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+function latestAndEarliestCachedRevisionTimestamp(req, title) {
+ var domainDict = significantChangesCache[req.params.domain];
+ const keyTitle = keyTitleForCache(req, title);
+ if (domainDict) {
+ var titleDict = domainDict[keyTitle];
+ if (titleDict) {
+ // eslint-disable-next-line no-restricted-properties
+ var titleArray = Object.values(titleDict);
+ if (titleArray.length > 0) {
+ titleArray.pop(); // remove maxedOut element
+ const sortedTitleObjects = titleArray.sort(function (a, b) {
+ return new Date(b.timestamp) - new Date(a.timestamp);
+ });
+ var latestTimestamp = null;
+ var earliestTimestamp = null;
+ if (sortedTitleObjects.length > 0) {
+ latestTimestamp = sortedTitleObjects[0].timestamp;
+ earliestTimestamp = sortedTitleObjects[sortedTitleObjects.length - 1].timestamp;
+ }
+
+ return Object.assign( {
+ latestTimestamp: latestTimestamp,
+ earliestTimestamp: earliestTimestamp
+ });
+ }
+ }
+ }
+
+ return null;
+}
+
+function setSignificantChangesCache(req, title, item) {
+ const keyTitle = keyTitleForCache(req, title);
+ var domainDict = significantChangesCache[req.params.domain] || {};
+ var titleDict = domainDict[keyTitle] || {};
+ titleDict[item.revid] = item;
+ titleDict.maxedOut = calculateCacheForTitleIsMaxedOut(titleDict);
+ domainDict[keyTitle] = titleDict;
+ significantChangesCache[req.params.domain] = domainDict;
+}
+
+function cleanupCache(req) {
+ // cleanup article cache
+ var domainDict = significantChangesCache[req.params.domain];
+ const articleTitle = keyTitleForCache(req, null);
+ if (domainDict) {
+ var titleDict = domainDict[articleTitle];
+ if (titleDict) {
+ // eslint-disable-next-line no-restricted-properties
+ var titleArray = Object.values(titleDict);
+ if (titleArray.length > 0) {
+ titleArray.pop(); // remove maxedOut element
+
+ const sortedTitleArray = titleArray.sort(function(a, b) {
+ if (a.timestamp && b.timestamp) {
+ return new Date(b.timestamp) - new Date(a.timestamp);
+ }
+ return 0;
+ });
+ const significantCachedObjects = sortedTitleArray.filter(outputObject => {
+ if (outputObject.outputType) {
+ return outputTypeCountsTowardsCache(outputObject.outputType);
+ }
+
+ return false;
+ });
+
+ const delta = significantCachedObjects.length -
+ maxAllowedCachedArticleSignificantEvents;
+
+ var timestampCutoff = null;
+ if (delta > 0 && delta < significantCachedObjects.length &&
+ significantCachedObjects.length > 0) {
+ const significantCachedObject =
+ significantCachedObjects[significantCachedObjects.length - delta - 1];
+ timestampCutoff = significantCachedObject.timestamp;
+ }
+
+ // clean out all cached article and cached talk page revisions
+ // after the 100th significant event
+ if (timestampCutoff) {
+ const cutoffDate = new Date(timestampCutoff);
+
+ // clean out from article cache
+ for (var s = sortedTitleArray.length - 1; s >= 0; s--) {
+ const sortedObjectToConsider = sortedTitleArray[s];
+ if (sortedObjectToConsider.timestamp !== null &&
+ sortedObjectToConsider.timestamp !== undefined) {
+ const objectDate = new Date(sortedObjectToConsider.timestamp);
+ if (objectDate < cutoffDate) {
+ delete titleDict[sortedObjectToConsider.revid];
+ } else {
+ break;
+ }
+ }
+ }
+
+ // clean out from talk page cache
+ var talkPageTitle = getTalkPageTitle(req);
+ const keyTalkPageTitle = keyTitleForCache(req, talkPageTitle);
+ var talkPageTitleDict = domainDict[keyTalkPageTitle];
+ if (talkPageTitleDict) {
+ // eslint-disable-next-line no-restricted-properties
+ var talkPageTitleArray = Object.values(talkPageTitleDict);
+ if (talkPageTitleArray.length > 0) {
+ talkPageTitleArray.pop(); // remove maxedOut element
+ const sortedTalkPageTitleArray =
+ talkPageTitleArray.sort(function(a, b) {
+ return new Date(b.timestamp) - new Date(a.timestamp);
+ });
+
+ for (var t = sortedTalkPageTitleArray.length - 1; t >= 0; t--) {
+ const sortedTalkObjectToConsider = sortedTalkPageTitleArray[t];
+ const talkObjectDate = new
+ Date(sortedTalkObjectToConsider.timestamp);
+ if (talkObjectDate < cutoffDate) {
+ delete talkPageTitleDict[sortedTalkObjectToConsider.revid];
+ } else {
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ }
+ }
+}
+
+function getSummaryText(req) {
+ var domainDict = significantChangesCache[req.params.domain];
+ const articleTitle = keyTitleForCache(req, null);
+ if (domainDict) {
+ var titleDict = domainDict[articleTitle];
+ if (titleDict) {
+ // eslint-disable-next-line no-restricted-properties
+ var titleArray = Object.values(titleDict);
+ if (titleArray.length > 0) {
+ titleArray.pop(); // remove maxedOut element
+ const sortedTitleArray = titleArray.sort(function (a, b) {
+ return new Date(b.timestamp) - new Date(a.timestamp);
+ });
+ const earliestItem = sortedTitleArray[sortedTitleArray.length - 1];
+ if (earliestItem.timestamp !== undefined && earliestItem.timestamp !== null) {
+ const earliestTimestamp = earliestItem.timestamp;
+ var userids = [];
+ for (var i = 0; i <= titleArray.length - 1; i++) {
+ const object = titleArray[i];
+ userids.push(object.userid);
+ }
+ var dedupedUserIds = new Set(userids);
+ const numUsers = dedupedUserIds.size;
+
+ return Object.assign({
+ earliestTimestamp: earliestTimestamp,
+ numChanges: titleArray.length,
+ numUsers: numUsers
+ });
+ }
+ }
+ }
+ }
+
+ return Object.assign( {
+ earliestTimestamp: null,
+ dedupedUserIds: null,
+ numUsers: null
+ });
+}
+
+function textWithHtmlCommentsStripped(text) {
+
+ if (text === null || text === undefined) {
+ return null;
+ }
+
+ return text.replace(/)/g, '');
+}
+
+function getSectionForDiffLine(diffBody, diffLine) {
+
+ if (!diffBody || !diffLine) {
+ return null;
+ }
+
+ var fromSection = null;
+ var toSection = null;
+
+ // safety
+ if (!diffBody.from ||
+ !diffBody.from.sections ||
+ !diffBody.to ||
+ !diffBody.to.sections ||
+ !diffLine.offset) {
+ return null;
+ }
+
+ // capture intro
+ if ((!diffBody.from.sections ||
+ diffBody.from.sections.length === 0) &&
+ (!diffBody.to.sections ||
+ diffBody.to.sections.length === 0)) {
+ return null;
+ }
+
+ // diffLine.offset.from = 0 is still valid if it's at the very beginning of the article.
+ // In this case javascript evaluates diffLine.offset.from to false,
+ // hence the need for the separate check.
+ if ((diffLine.offset.from || diffLine.offset.from === 0) &&
+ diffBody.from.sections && diffBody.from.sections.length > 0 &&
+ diffLine.offset.from < diffBody.from.sections[0].offset) {
+ fromSection = 'Intro';
+ }
+
+ if ((diffLine.offset.to || diffLine.offset.to === 0) &&
+ diffBody.to.sections && diffBody.to.sections.length > 0 &&
+ diffLine.offset.to < diffBody.to.sections[0].offset) {
+ toSection = 'Intro';
+ }
+
+ if (fromSection && toSection) {
+ if (diffLine.offset.to && diffLine.offset.to.length > 0) {
+ return textWithHtmlCommentsStripped(toSection);
+ } else {
+ return textWithHtmlCommentsStripped(fromSection);
+ }
+ }
+
+ var prevSection = null;
+ if (!fromSection && diffLine.offset.from) {
+ for (let i = 0; i < diffBody.from.sections.length; i++) {
+ const section = diffBody.from.sections[i];
+
+ if (diffLine.offset.from < section.offset && prevSection) {
+ if (prevSection.heading) {
+ fromSection = prevSection.heading;
+ break;
+ }
+ }
+
+ prevSection = section;
+ }
+
+ if (!fromSection && diffLine.offset.from > 0 && diffBody.from.sections.length > 0) {
+ if (prevSection.heading) {
+ fromSection = prevSection.heading;
+ }
+
+ }
+ }
+
+ if (!toSection && diffLine.offset.to) {
+ prevSection = null;
+ for (let i = 0; i < diffBody.to.sections.length; i++) {
+
+ const section = diffBody.to.sections[i];
+ if (diffLine.offset.to < section.offset && prevSection) {
+ if (prevSection.heading) {
+ toSection = prevSection.heading;
+ break;
+ }
+ }
+
+ prevSection = section;
+ }
+
+ if (!toSection && diffLine.offset.to > 0 && diffBody.to.sections.length > 0) {
+ if (prevSection.heading) {
+ toSection = prevSection.heading;
+ }
+ }
+ }
+
+ if (diffLine.offset.to || diffLine.offset.to === 0) {
+ // 0 is a valid value, but without explicit check JS sees it as invalid
+ return textWithHtmlCommentsStripped(toSection);
+ } else {
+ return textWithHtmlCommentsStripped(fromSection);
+ }
+}
+
+function updateDiffAndRevisionsWithCharacterCount(diffAndRevisions) {
+
+ if (!diffAndRevisions) {
+ return;
+ }
+
+ // Loop through diffs, filter out type 0 (context type) and assign byte change properties
+ // to the remaining
+
+ for (var i = 0; i < diffAndRevisions.length; i++) {
+ const diffAndRevision = diffAndRevisions[i];
+
+ if (!diffAndRevision.body || !diffAndRevision.body.diff) {
+ continue;
+ }
+
+ var filteredDiffs = [];
+
+ var aggregateAddedCount = 0;
+ var aggregateDeletedCount = 0;
+ var aggregateAddedSections = new Set();
+ var aggregateDeletedSections = new Set();
+
+ for (var d = 0; d < diffAndRevision.body.diff.length; d++) {
+ const diff = diffAndRevision.body.diff[d];
+
+ if (diff.type === undefined ||
+ diff.type === null ||
+ diff.text === undefined ||
+ diff.text === null) {
+ continue;
+ }
+
+ var lineAddedCount = 0;
+ var lineDeletedCount = 0;
+ switch (diff.type) {
+ case 0: // Context line type
+ continue;
+ case 1: // Add complete line type
+ lineAddedCount = diff.text.length;
+ break;
+ case 2: // Delete complete line type
+ lineDeletedCount = diff.text.length;
+ break;
+ case 5: // Move paragraph destination type (also has added and deleted
+ // ranges in line)
+ // eslint-disable-next-line no-fallthrough
+ case 3: // Change line type (add and deleted ranges in line)
+
+ if (diff.highlightRanges === undefined ||
+ diff.highlightRanges === null) {
+ break;
+ }
+
+ for (var h = 0; h < diff.highlightRanges.length; h++) {
+ const range = diff.highlightRanges[h];
+
+ if (range.start === null ||
+ range.start === undefined ||
+ range.length === null ||
+ range.length === undefined) {
+ continue;
+ }
+
+ const binaryText = encoding.strToBin(diff.text);
+ const binaryRangeText = binaryText.substring(range.start,
+ range.start + range.length);
+ const rangeText = encoding.binToStr(binaryRangeText);
+ const lengthToCalculate = rangeText.length;
+
+ switch (range.type) {
+ case 0: // Add range type
+ lineAddedCount += lengthToCalculate;
+ break;
+ case 1: // Delete range type
+ lineDeletedCount += lengthToCalculate;
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ aggregateAddedCount += lineAddedCount;
+ aggregateDeletedCount += lineDeletedCount;
+
+ if (lineAddedCount > 0 ) {
+ const section = getSectionForDiffLine(diffAndRevision.body, diff);
+ if (section) {
+ aggregateAddedSections.add(section);
+ }
+ }
+
+ if (lineDeletedCount > 0) {
+ const section = getSectionForDiffLine(diffAndRevision.body, diff);
+ if (section) {
+ aggregateDeletedSections.add(section);
+ }
+ }
+
+ diff.characterChange = new CharacterChange(lineAddedCount, lineDeletedCount);
+ filteredDiffs.push(diff);
+ }
+
+ const aggregateCounts = new CharacterChange(aggregateAddedCount, aggregateDeletedCount);
+ diffAndRevision.characterChangeWithSections = new CharacterChangeWithSections(
+ aggregateCounts, Array.from(aggregateAddedSections),
+ Array.from(aggregateDeletedSections));
+ diffAndRevision.body.diff = filteredDiffs;
+ }
+}
+
+function templateNamesToCallOut() {
+ return ['cite', 'citation', 'short description'];
+}
+
+function needsToParseForAddedTemplates(text, includeOpeningBraces) {
+
+ if (text === undefined ||
+ text === null ||
+ includeOpeningBraces === undefined ||
+ includeOpeningBraces === null) {
+ return false;
+ }
+
+ const names = templateNamesToCallOut();
+
+ for (var n = 0; n < names.length; n++) {
+ const name = names[n];
+
+ if ((text.includes(`{{${name}`) && includeOpeningBraces) || (text.includes(`${name}`) &&
+ !includeOpeningBraces)) {
+
+ if (!text.includes('citation needed')) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+// BUG: https://en.wikipedia.org/w/index.php?title=United_States&type=revision
+// &diff=965295364&oldid=965071033
+// Should we be catching added [ tags?
+// ANOTHER BUG:
+// Sometimes new citations have spaces within deemed the same, so only part of it
+// is sent in as text whereas we need all of cite.
+// See here
+// https://en.wikipedia.org/w/index.php?title=United_States&type=revision&diff=971349395&oldid=971332725
+// Also this one
+// https://en.wikipedia.org/w/index.php?title=United_States&type=revision&diff=971260075&oldid=971259267
+// Definitely some mangled 'added-text' snippets going on here.
+// https://en.wikipedia.org/w/index.php?title=United_States&type=revision&diff=970665187&oldid=970577931
+function structuredTemplatePromise(text, diffItem, revision) {
+ return new BBPromise((resolve) => {
+ var main = PRFunPromise.async(function*() {
+ var pdoc = yield ParsoidJS.parse(text, { pdoc: true });
+ const splitTemplates = yield PRFunPromise.map(pdoc.filterTemplates(),
+ ParsoidJS.toWikitext);
+ var templateObjects = [];
+
+ // Note: for now just grabbing the first split template
+ // (which includes only parent templates and not nested)
+ // to avoid duplicate template problem. If we need nested support we can loop through
+ // all split templates, then uniquely identify and dedupe templates later.
+ if (splitTemplates.length === 0) {
+ // bail early
+ const result = Object.assign( {
+ revision: revision,
+ diffItem: diffItem,
+ templates: templateObjects
+ });
+ resolve(result);
+ return;
+ }
+ for (var s = 0; s < 1; s++) {
+ const splitTemplateText = splitTemplates[s];
+
+ const innerPdoc = yield ParsoidJS.parse(splitTemplateText, { pdoc: true });
+ const individualTemplates = innerPdoc.filterTemplates();
+ if (individualTemplates !== undefined && individualTemplates !== null) {
+ for (var i = 0; i < individualTemplates.length; i++) {
+ const template = individualTemplates[i];
+
+ if (!needsToParseForAddedTemplates(template.name, false)) {
+ continue;
+ }
+
+ if (template.name === undefined ||
+ template.name === null ||
+ template.params === undefined ||
+ template.params === null) {
+ continue;
+ }
+
+ var dict = {};
+ dict.name = template.name;
+ for (var p = 0; p < template.params.length; p++) {
+ const param = template.params[p].name;
+ if (param !== undefined && param !== null) {
+ const value = yield template.get(param).value.toWikitext();
+ dict[param] = value;
+ }
+ }
+ templateObjects.push(dict);
+ }
+ }
+ }
+ const result = Object.assign( {
+ revision: revision,
+ diffItem: diffItem,
+ templates: templateObjects
+ });
+ resolve(result);
+ });
+
+ main().done();
+ });
+}
+
+function addStructuredTemplates(diffAndRevisions) {
+
+ var promises = [];
+
+ if (!diffAndRevisions) {
+ return Promise.all(promises)
+ .then( (response) => {
+ return diffAndRevisions;
+ });
+ }
+
+ for (var i = 0; i < diffAndRevisions.length; i++) {
+ const diffAndRevision = diffAndRevisions[i];
+
+ if (diffAndRevision.body === null ||
+ diffAndRevision.body === undefined ||
+ diffAndRevision.body.diff === null ||
+ diffAndRevision.body.diff === undefined ||
+ diffAndRevision.revision === null ||
+ diffAndRevision.revision === undefined) {
+ continue;
+ }
+
+ const addedLinesCount = diffAndRevision.body.diff.filter(diffItem => diffItem.type === 1)
+ .length;
+ if (addedLinesCount > 500) {
+ // This suggests some sort of mass change that would be too much work on the server
+ // to parse for templates.
+ // bypassing template detection in this case.
+ // this may be a good metric to tweak to boost performance
+ continue;
+ }
+
+ for (var d = 0; d < diffAndRevision.body.diff.length; d++) {
+ const diffItem = diffAndRevision.body.diff[d];
+
+ if (diffItem.text === undefined ||
+ diffItem.text === null) {
+ continue;
+ }
+
+ switch (diffItem.type) {
+ case 1: // Add complete line type
+ if (needsToParseForAddedTemplates(diffItem.text, true)) {
+ promises.push(structuredTemplatePromise(diffItem.text, diffItem,
+ diffAndRevision.revision));
+ }
+ break;
+ case 5:
+ case 3: {
+
+ const binaryText = encoding.strToBin(diffItem.text);
+ var previousBinaryRangeText = null;
+ var previousRangeEndIndex = null;
+ for (var h = 0; h < diffItem.highlightRanges.length; h++) {
+ const range = diffItem.highlightRanges[h];
+
+ if (range.start === undefined ||
+ range.start === null ||
+ range.length === undefined ||
+ range.length === null) {
+ continue;
+ }
+
+ switch (range.type) {
+ case 0: { // Add range type
+
+ const binaryRangeText = binaryText.substring(range.start,
+ range.start + range.length);
+ if (previousBinaryRangeText !== null &&
+ previousRangeEndIndex !== null) {
+
+ const inBetweenText = binaryText.substring(
+ previousRangeEndIndex,
+ range.start);
+ if (inBetweenText === ' ') {
+ previousBinaryRangeText += inBetweenText;
+ previousBinaryRangeText += binaryRangeText;
+ previousRangeEndIndex = range.start + range.length;
+ continue;
+ } else {
+ // before attempting to send off for template parsing,
+ // grab following '}}' to capture edge case templates
+ const nextTwoCharacters = binaryText
+ .substring(previousRangeEndIndex,
+ previousRangeEndIndex + 2);
+ if (nextTwoCharacters === '}}') {
+ previousBinaryRangeText += '}}';
+ }
+ const rangeText = encoding
+ .binToStr(previousBinaryRangeText);
+ if (needsToParseForAddedTemplates(rangeText,
+ true)) {
+ promises.push(structuredTemplatePromise(rangeText,
+ diffItem,
+ diffAndRevision.revision));
+ }
+
+ previousBinaryRangeText = binaryRangeText;
+ previousRangeEndIndex = range.start + range.length;
+ }
+
+ } else {
+ previousBinaryRangeText = binaryRangeText;
+ previousRangeEndIndex = range.start + range.length;
+ continue;
+ }
+ }
+ break;
+ case 1: // Deleted
+ previousRangeEndIndex = range.start + range.length;
+
+ }
+ }
+
+ // there's probably one last straggler range that we haven't checked
+ // due to how we're doing things
+
+ if (previousBinaryRangeText !== null && previousRangeEndIndex !== null) {
+
+ // before attempting to send off for template parsing,
+ // grab following '}}' to capture edge case templates
+ const nextTwoCharacters = binaryText.substring(previousRangeEndIndex,
+ previousRangeEndIndex + 2);
+ if (nextTwoCharacters === '}}') {
+ previousBinaryRangeText += '}}';
+ }
+
+ // also if first 2 characters start with '}}', it might throw things off
+ const firstTwoCharacters = previousBinaryRangeText.substring(0,
+ 2);
+ if (firstTwoCharacters === '}}') {
+ previousBinaryRangeText = previousBinaryRangeText.substring(2,
+ previousBinaryRangeText.length);
+ }
+
+ const rangeText = encoding.binToStr(previousBinaryRangeText);
+ if (needsToParseForAddedTemplates(rangeText, true)) {
+ promises.push(structuredTemplatePromise(rangeText, diffItem,
+ diffAndRevision.revision));
+ }
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ }
+
+ return Promise.all(promises)
+ .then( (response) => {
+
+ // loop through responses, add to revision.
+
+ diffAndRevisions.forEach( (diffAndRevision) => {
+
+ var templatesAndDiffItems = [];
+ response.forEach( (item) => {
+
+ if (item.revision !== null &&
+ item.revision !== undefined &&
+ diffAndRevision.revision !== null &&
+ diffAndRevision.revision !== undefined &&
+ item.templates !== null &&
+ item.templates !== undefined &&
+ item.templates.length > 0) {
+ if (item.revision.revid === diffAndRevision.revision.revid) {
+ const templateAndDiffItem = Object.assign({
+ templates: item.templates,
+ diffItem: item.diffItem
+ });
+
+ templatesAndDiffItems.push(templateAndDiffItem);
+ }
+ }
+ });
+
+ diffAndRevision.templatesAndDiffItems = templatesAndDiffItems;
+ });
+
+ return diffAndRevisions;
+ });
+}
+
+function getNewTopicDiffAndRevisions(talkDiffAndRevisions) {
+
+ if (talkDiffAndRevisions === null ||
+ talkDiffAndRevisions === undefined) {
+ return null;
+ }
+
+ const newSectionTalkPageDiffAndRevisions = [];
+ for (var index = 0; index < talkDiffAndRevisions.length; index++) {
+ const diffAndRevision = talkDiffAndRevisions[index];
+
+ if (diffAndRevision.revision === null ||
+ diffAndRevision.revision === undefined ||
+ diffAndRevision.revision.comment === null ||
+ diffAndRevision.revision.comment === undefined ||
+ diffAndRevision.revision.userid === null ||
+ diffAndRevision.revision.userid === undefined ||
+ diffAndRevision.revision.tags === null ||
+ diffAndRevision.revision.tags === undefined) {
+ continue;
+ }
+
+ if (diffAndRevision.revision.comment.toLowerCase().includes('new section')
+ && !diffAndRevision.revision.comment.toLowerCase()
+ .includes('semi-protected edit request') // don't show semi-protected edit requests
+ && diffAndRevision.revision.userid !== 4936590 // don't show signbot topics
+ && !diffAndRevision.revision.tags.includes('mw-reverted')) { // don't show mw-reverted posts
+ // see if this section was reverted in previous iterations
+ var wasUndoneOrRolledBackLater = false;
+ if (index - 1 > 0) {
+ var nextDiffAndRevision = talkDiffAndRevisions[index - 1];
+ if (nextDiffAndRevision.revision.userid === 4936590) { // signbot,
+ // look for next revision
+ if (index - 2 > 0) {
+ nextDiffAndRevision = talkDiffAndRevisions[index - 2];
+ }
+ }
+ if (nextDiffAndRevision.revision.tags.includes('mw-undo') ||
+ nextDiffAndRevision.revision.tags.includes('mw-rollback')) {
+ wasUndoneOrRolledBackLater = true;
+ }
+ }
+ if (wasUndoneOrRolledBackLater === false) {
+ newSectionTalkPageDiffAndRevisions.push(diffAndRevision);
+ }
+ }
+ }
+
+ return newSectionTalkPageDiffAndRevisions;
+}
+
+function getAllChangedDiffLines(diffBody) {
+ const allChangedDiffLines = diffBody.diff.filter((item) => {
+ return item.type !== 0;
+ });
+ return allChangedDiffLines;
+}
+
+function getLargestDiffLine(diffBody) {
+
+ if (diffBody === null ||
+ diffBody === undefined ||
+ diffBody.diff === null ||
+ diffBody.diff === undefined) {
+ return null;
+ }
+
+ diffBody.diff.sort(function(a, b) {
+
+ if (b.characterChange === null ||
+ b.characterChange === undefined ||
+ a.characterChange === null ||
+ a.characterChange === undefined) {
+ return 0;
+ }
+
+ return b.characterChange.totalCount() - a.characterChange.totalCount();
+ });
+
+ if (diffBody.diff.length > 0) {
+ const largestDiffLine = diffBody.diff[0];
+ return largestDiffLine;
+ }
+
+ return null;
+}
+
+function getLargestDiffLineOfAdded(diffBody) {
+
+ if (diffBody === null ||
+ diffBody === undefined ||
+ diffBody.diff === null ||
+ diffBody.diff === undefined) {
+ return null;
+ }
+
+ diffBody.diff.sort(function(a, b) {
+
+ if (b.characterChange === null ||
+ b.characterChange === undefined ||
+ a.characterChange === null ||
+ a.characterChange === undefined) {
+ return 0;
+ }
+
+ return b.characterChange.addedCount - a.characterChange.addedCount;
+ });
+
+ if (diffBody.diff.length > 0) {
+ const largestDiffLine = diffBody.diff[0];
+ return largestDiffLine;
+ }
+
+ return null;
+}
+
+function textContainsEmptyLineOrSection(text) {
+ if (text === null ||
+ text === undefined) {
+ return false;
+ }
+
+ const trimmedText = text.trim();
+ return (trimmedText.length === 0 || text.includes('=='));
+}
+
+function textContainsTemplate(text) {
+
+ if (text === null ||
+ text === undefined) {
+ return false;
+ }
+
+ return text.includes('{{');
+}
+
+function getFirstDiffLineWithContent(diffBody) {
+
+ if (diffBody === null ||
+ diffBody === undefined ||
+ diffBody.diff === null ||
+ diffBody.diff === undefined) {
+ return null;
+ }
+
+ const nonContextDiffLines = diffBody.diff.filter(diff => (diff.type !== null &&
+ diff.type !== undefined && diff.text !== null && diff.text !== undefined
+ && diff.type > 0));
+ const nonContextDiffLinesWithoutEmptyLineOrSectionsOrTemplates =
+ nonContextDiffLines.filter(diff =>
+ !textContainsEmptyLineOrSection(diff.text) && !textContainsTemplate(diff.text));
+ const nonContextDiffLinesWithoutEmptyLineOrSections =
+ nonContextDiffLines.filter(diff => !textContainsEmptyLineOrSection(diff.text));
+
+ if (nonContextDiffLines.length === 0) {
+ return null;
+ }
+
+ // prefer to return a line without templates, otherwise try line with templates
+ if (nonContextDiffLinesWithoutEmptyLineOrSectionsOrTemplates.length > 0) {
+ return nonContextDiffLinesWithoutEmptyLineOrSectionsOrTemplates[0];
+ } else if (nonContextDiffLinesWithoutEmptyLineOrSections.length > 0) {
+ return nonContextDiffLinesWithoutEmptyLineOrSections[0];
+ } else {
+ return null;
+ }
+}
+
+function sortOutput(output) {
+
+ if (output === null ||
+ output === undefined) {
+ return null;
+ }
+
+ // sort by date
+ return output.sort(function(a, b) {
+
+ if (b.timestamp === null ||
+ b.timestamp === undefined ||
+ a.timestamp === null ||
+ a.timestamp === undefined) {
+ return 0;
+ }
+
+ return new Date(b.timestamp) - new Date(a.timestamp);
+ });
+}
+
+function cleanOutput(output) {
+
+ if (output === null ||
+ output === undefined) {
+ return null;
+ }
+
+ // convert large and small changes only to info needed
+
+ const cleanedOutput = [];
+ for (var i = 0; i < output.length; i++) {
+ const item = output[i];
+
+ if (item.outputType === null ||
+ item.outputType === undefined) {
+ continue;
+ }
+
+ if (item.outputType === 'large-change') {
+ cleanedOutput.push(new LargeOutput(item));
+ } else if (item.outputType === 'new-talk-page-topic') {
+ cleanedOutput.push(new NewTalkPageTopic(item));
+ } else if (item.outputType === 'small-change') {
+ cleanedOutput.push(new SmallOutput(item));
+ } else {
+ cleanedOutput.push(item);
+ }
+ }
+
+ return cleanedOutput;
+}
+
+function editCountsAndGroupsPromise(req, cleanedOutput) {
+
+ if (cleanedOutput === undefined ||
+ cleanedOutput === null) {
+ return null;
+ }
+
+ // gather unique userids
+ var userids = [];
+ cleanedOutput.forEach( (outputItem) => {
+ if (outputItem.userid !== undefined && outputItem.userid !== null &&
+ outputItem.userid !== 0) {
+ userids.push(outputItem.userid);
+ }
+ });
+ var dedupedIdsSet = new Set(userids);
+ var dedupedIds = Array.from(dedupedIdsSet);
+ // fetch counts and groups
+ return mwapi.queryForUsers(req, dedupedIds)
+ .then( (response) => {
+ // distribute results back into cleanedOutput
+
+ if (response.body === null ||
+ response.body === undefined ||
+ response.body.query === null ||
+ response.body.query === undefined ||
+ response.body.query.users === null ||
+ response.body.query.users === undefined) {
+ cleanedOutput.forEach( (outputItem) => {
+ if (outputItem.outputType !== 'small-change') {
+ outputItem.userGroups = null;
+ outputItem.userEditCount = null;
+ }
+ });
+ return cleanedOutput;
+ }
+
+ const users = response.body.query.users;
+ users.forEach( (user) => {
+ cleanedOutput.forEach( (outputItem) => {
+
+ if (outputItem.userid !== null &&
+ outputItem.userid !== undefined &&
+ user.userid !== null &&
+ user.userid !== undefined) {
+ if (outputItem.userid === user.userid) {
+ outputItem.userGroups = user.groups;
+ outputItem.userEditCount = user.editcount;
+ }
+ }
+ });
+ });
+
+ return cleanedOutput;
+ })
+ .catch( (e) => {
+ return cleanedOutput;
+ });
+}
+
+function isRequestingFirstPage(req) {
+ return req.query.rvstartid === undefined || req.query.rvstartid === null;
+}
+
+function shaFromSortedOutput(req, sortedOutput) {
+
+ if (sortedOutput === null ||
+ sortedOutput === undefined) {
+ return null;
+ }
+
+ if (!isRequestingFirstPage(req)) {
+ return null;
+ }
+
+ var shaTitle;
+ var shaRevID;
+ for (var i = 0; i < sortedOutput.length; i++) {
+ const output = sortedOutput[i];
+
+ if (output.outputType === undefined ||
+ output.outputType === null ||
+ output.revid === undefined ||
+ output.revid === null) {
+ continue;
+ }
+
+ if (output.outputType === 'large-change') {
+ shaTitle = req.params.title;
+ shaRevID = output.revid;
+ break;
+ }
+
+ if (output.outputType === 'new-talk-page-topic') {
+ shaTitle = getTalkPageTitle(req);
+ shaRevID = output.revid;
+ break;
+ }
+ }
+
+ if (shaTitle === undefined || shaTitle === null || shaRevID === undefined ||
+ shaRevID === null) {
+ // grab first item, even if it's a small type
+ for (var s = 0; s < sortedOutput.length; s++) {
+ const output = sortedOutput[s];
+ if (output.outputType === undefined ||
+ output.outputType === null ||
+ output.revid === undefined ||
+ output.revid === null) {
+ continue;
+ }
+
+ if (output.outputType === 'small-change') {
+ shaTitle = req.params.title;
+ shaRevID = output.revid;
+ break;
+ }
+ }
+ }
+
+ if (shaTitle === undefined || shaTitle === null || shaRevID === undefined ||
+ shaRevID === null) {
+ return null;
+ }
+
+ return tUtil.createSha256(`${shaTitle}${shaRevID}`);
+}
+
+class MalformedArticleRevisionResponse extends Error {
+ constructor(message) {
+ super(message);
+ this.name = 'MalformedArticleRevisionResponse';
+ }
+}
+
+class AllArticleDiffCallsFailed extends Error {
+ constructor(message) {
+ super(message);
+ this.name = 'AllArticleDiffCallsFailed';
+ }
+}
+
+function getSignificantEvents(req, res) {
+
+ // STEP 1: Gather list of article revisions
+ return mwapi.queryForRevisions(req)
+ .then( (response) => {
+
+ // STEP 2: All at once gather diffs for each uncached revision and list of
+ // talk page revisions
+
+ if (response.body === undefined ||
+ response.body === null ||
+ response.body.query === undefined ||
+ response.body.query === null ||
+ response.body.query.pages === undefined ||
+ response.body.query.pages === null ||
+ response.body.query.pages[0] === undefined ||
+ response.body.query.pages[0] === null ||
+ response.body.query.pages[0].revisions === undefined ||
+ response.body.query.pages[0].revisions === null) {
+ throw new MalformedArticleRevisionResponse();
+ }
+
+ const revisions = response.body.query.pages[0].revisions;
+
+ // BEGIN: PAGE CUTOFF LOGIC
+ // page cutoff: if cache is maxed out, filter out any revisions
+ // that occur before the cutoff
+ const cutoff = latestAndEarliestCachedRevisionTimestamp(req);
+ var filteredRevisions;
+ const isMaxedOut = cacheForTitleIsMaxedOut(req);
+ if (isMaxedOut && cutoff && cutoff.earliestTimestamp) {
+ filteredRevisions = revisions.filter((revision) => {
+ if (revision.timestamp === undefined || revision.timestamp === null ||
+ cutoff.earliestTimestamp === undefined ||
+ cutoff.earliestTimestamp === null) {
+ return 0;
+ }
+ return new Date(revision.timestamp) > new Date(cutoff.earliestTimestamp);
+ });
+ } else {
+ filteredRevisions = revisions;
+ }
+
+ // if cache is maxed out and filteredRevisions contains
+ // revisions from after the latest cached revision,
+ // propagate flag for needing cleanup later
+ var needsCacheCleanup = false;
+ if (isMaxedOut) {
+ for (var i = 0; i < filteredRevisions.length; i++) {
+
+ const revision = filteredRevisions[i];
+
+ if (revision.timestamp === undefined || revision.timestamp === null ||
+ cutoff.earliestTimestamp === undefined ||
+ cutoff.earliestTimestamp === null) {
+ continue;
+ }
+
+ const timestamp = new Date(revision.timestamp);
+ const latestCacheTimestamp = new Date(cutoff.latestTimestamp);
+ if (timestamp > latestCacheTimestamp) {
+ needsCacheCleanup = true;
+ break;
+ }
+ }
+ }
+ // END: PAGE CUTOFF LOGIC
+
+ var nextRvStartId = null;
+ var talkPageRvStartId = null;
+ var talkPageRvEndId = null;
+ if (filteredRevisions && filteredRevisions.length > 0) {
+ const earliestRevision = filteredRevisions[filteredRevisions.length - 1];
+ const latestRevision = filteredRevisions[0];
+ if (earliestRevision.parentid !== null && earliestRevision.parentid !== undefined
+ && latestRevision.timestamp !== null &&
+ latestRevision.timestamp !== undefined) {
+ nextRvStartId = earliestRevision.parentid;
+ // if rvstartid is missing from query, they are fetching the first page
+ // if they are fetching the first page, we don't want to block of
+ // talk page revision fetching at the start, in case talk page topics came in
+ // after the latest article revision
+ talkPageRvStartId = isRequestingFirstPage(req) ? null :
+ latestRevision.timestamp;
+ talkPageRvEndId = earliestRevision.timestamp;
+ }
+ }
+
+ const articleEvalResults = getCachedAndUncachedItems(filteredRevisions, req, null);
+
+ // save cached article revisions to finalOutput
+ const finalOutput = articleEvalResults.cachedOutput;
+
+ const talkPageRevisionPromise = (talkPageRvEndId !== null) ?
+ talkPageRevisionsPromise(req, talkPageRvStartId, talkPageRvEndId)
+ : null;
+
+ return BBPromise.props({
+ articleDiffAndRevisions: diffAndRevisionPromises(req,
+ articleEvalResults.uncachedRevisions),
+ talkPageRevisions: talkPageRevisionPromise,
+ nextRvStartId: nextRvStartId,
+ needsCacheCleanup: needsCacheCleanup,
+ finalOutput: finalOutput
+ });
+ })
+ .then( (response) => {
+
+ // if all articleDiffAndRevisions fail, return error
+ if (response.articleDiffAndRevisions !== undefined &&
+ response.articleDiffAndRevisions !== null) {
+ const articleDiffAndRevisionsNullDiff =
+ response.articleDiffAndRevisions.filter(diffAndRevision => {
+ return diffAndRevision.body === undefined || diffAndRevision.body === null;
+ });
+
+ if (articleDiffAndRevisionsNullDiff.length ===
+ response.articleDiffAndRevisions.length &&
+ articleDiffAndRevisionsNullDiff.length > 0 &&
+ response.articleDiffAndRevisions.length > 0) { // all diff calls failed
+ throw new AllArticleDiffCallsFailed();
+ }
+ }
+
+ // STEP 3: All at once gather diffs for uncached talk page revisions
+
+ const articleDiffAndRevisions = response.articleDiffAndRevisions
+ .filter(x => x !== null);
+ const nextRvStartId = response.nextRvStartId;
+ const needsCacheCleanup = response.needsCacheCleanup;
+
+ if (response.talkPageRevisions !== undefined && response.talkPageRevisions !== null &&
+ response.talkPageRevisions.body !== undefined &&
+ response.talkPageRevisions.body !== null &&
+ response.talkPageRevisions.body.query !== undefined &&
+ response.talkPageRevisions.body.query !== null &&
+ response.talkPageRevisions.body.query.pages[0] !== undefined &&
+ response.talkPageRevisions.body.query.pages[0] !== null &&
+ response.talkPageRevisions.body.query.pages[0].revisions !== undefined &&
+ response.talkPageRevisions.body.query.pages[0].revisions !== null) {
+ const talkPageRevisions = response.talkPageRevisions.body.query.pages[0].revisions;
+ const talkPageEvalResults = getCachedAndUncachedItems(talkPageRevisions,
+ req, getTalkPageTitle(req));
+
+ // save cached talk page revisions to finalOutput
+ const finalOutput = response.finalOutput.concat(talkPageEvalResults.cachedOutput);
+
+ // for each uncached talk page revision, gather diffs
+ return diffAndRevisionPromises(req, talkPageEvalResults.uncachedRevisions)
+ .then( (response) => {
+ return Object.assign({
+ articleDiffAndRevisions: articleDiffAndRevisions,
+ talkDiffAndRevisions: response,
+ nextRvStartId: nextRvStartId,
+ needsCacheCleanup: needsCacheCleanup,
+ finalOutput: finalOutput
+ });
+ });
+ } else {
+ return Object.assign({
+ articleDiffAndRevisions: articleDiffAndRevisions,
+ talkDiffAndRevisions: null,
+ nextRvStartId: nextRvStartId,
+ needsCacheCleanup: needsCacheCleanup,
+ finalOutput: response.finalOutput
+ });
+ }
+ })
+ .then( (response) => {
+
+ // Determine character size of change for every diff line and aggregate
+ // for every revision
+ updateDiffAndRevisionsWithCharacterCount(response.articleDiffAndRevisions);
+ if (response.talkDiffAndRevisions) {
+ updateDiffAndRevisionsWithCharacterCount(response.talkDiffAndRevisions);
+ }
+
+ // Flag added template types
+ return addStructuredTemplates(response.articleDiffAndRevisions, false)
+ .then( (articleDiffAndRevisions) => {
+ response.articleDiffAndRevisions = articleDiffAndRevisions;
+ return response;
+ });
+ })
+ .then( (response) => {
+
+ const threshold = getThreshold(req);
+
+ // segment off into types
+ var uncachedOutput = [];
+
+ for (var i = 0; i < response.articleDiffAndRevisions.length; i++) {
+ const diffAndRevision = response.articleDiffAndRevisions[i];
+
+ if (diffAndRevision.revision === undefined || diffAndRevision.revision === null) {
+ continue;
+ }
+
+ const revision = diffAndRevision.revision;
+
+ // edge case in case one of the diff endpoints fail...fallback to small type
+ if (diffAndRevision.body === undefined || diffAndRevision.body === null) {
+ const smallOutputObject = new SmallOutputExtended(revision.revid,
+ revision.parentid, revision.timestamp, revision.user, revision.userid);
+ uncachedOutput.push(smallOutputObject);
+ continue;
+ }
+
+ if (revision.tags !== undefined && revision.tags !== null &&
+ revision.tags.includes('mw-rollback') &&
+ (revision.comment !== undefined && revision.comment !== null) &&
+ (revision.comment.toLowerCase().includes('revert') ||
+ revision.comment.toLowerCase().includes('vandalism')) &&
+ diffAndRevision.body !== null && diffAndRevision.body !== undefined) {
+ const sections = diffAndRevision.characterChangeWithSections.addedSections
+ .concat(diffAndRevision.characterChangeWithSections.deletedSections);
+ const dedupedSections = new Set(sections);
+ const vandalismRevertOutputObject = new VandalismOutput(revision.revid,
+ revision.parentid, revision.timestamp, revision.user, revision.userid,
+ Array.from(dedupedSections));
+ uncachedOutput.push(vandalismRevertOutputObject);
+ } else {
+
+ var significantChanges = [];
+ if (diffAndRevision.templatesAndDiffItems !== undefined &&
+ diffAndRevision.templatesAndDiffItems !== null &&
+ diffAndRevision.templatesAndDiffItems.length > 0) {
+
+ var sections = [];
+ var combinedTemplates = [];
+
+ for (var t = 0; t < diffAndRevision.templatesAndDiffItems.length; t++) {
+ const templatesAndDiffItem = diffAndRevision.templatesAndDiffItems[t];
+ const templates = templatesAndDiffItem.templates;
+ const diffItem = templatesAndDiffItem.diffItem;
+ const section = getSectionForDiffLine(diffAndRevision.body,
+ diffItem);
+ if (section) {
+ sections.push(section);
+ }
+ if (templates) {
+ combinedTemplates = combinedTemplates.concat(templates);
+ }
+ }
+
+ const dedupedSections = new Set(sections);
+ const newReferenceOutputObject =
+ new NewReferenceOutput(Array.from(dedupedSections),
+ combinedTemplates);
+ significantChanges.push(newReferenceOutputObject);
+ }
+
+ if (diffAndRevision.characterChangeWithSections !== undefined &&
+ diffAndRevision.characterChangeWithSections !== null &&
+ diffAndRevision.characterChangeWithSections.counts !== undefined &&
+ diffAndRevision.characterChangeWithSections.counts !== null &&
+ diffAndRevision.characterChangeWithSections.counts.totalCount() >
+ threshold) {
+
+ if (diffAndRevision.characterChangeWithSections.counts.addedCount > 0) {
+ const largestDiffLine = getLargestDiffLineOfAdded(diffAndRevision.body);
+ if (largestDiffLine !== undefined && largestDiffLine !== null) {
+ const addedTextOutputObject = new AddedTextOutputExpanded(
+ diffAndRevision.characterChangeWithSections,
+ largestDiffLine.text,
+ largestDiffLine.type, largestDiffLine.highlightRanges);
+ significantChanges.push(addedTextOutputObject);
+ }
+ }
+
+ if (diffAndRevision.characterChangeWithSections.counts.deletedCount > 0) {
+ const deletedTextOutputObject = new DeletedTextOutput(
+ diffAndRevision.characterChangeWithSections);
+ significantChanges.push(deletedTextOutputObject);
+ }
+ }
+
+ if (significantChanges.length > 0) {
+ const largeOutputObject = new LargeOutputExpanded(revision.revid,
+ revision.parentid, revision.timestamp, revision.user,
+ revision.userid, significantChanges);
+ uncachedOutput.push(largeOutputObject);
+ } else {
+ const smallOutputObject = new SmallOutputExtended(revision.revid,
+ revision.parentid, revision.timestamp, revision.user,
+ revision.userid);
+ uncachedOutput.push(smallOutputObject);
+ }
+ }
+ }
+
+ // get new talk page revisions, add to uncachedOutput. it will be sorted later.
+ if (response.talkDiffAndRevisions !== undefined &&
+ response.talkDiffAndRevisions !== null) {
+ const newTopicDiffAndRevisions = getNewTopicDiffAndRevisions(
+ response.talkDiffAndRevisions);
+ if (newTopicDiffAndRevisions !== undefined && newTopicDiffAndRevisions !== null) {
+ newTopicDiffAndRevisions.forEach(function (diffAndRevision) {
+ const revision = diffAndRevision.revision;
+
+ const nullSnippetTalkPageObject = new NewTalkPageTopicExtended(
+ revision.revid, revision.parentid, revision.timestamp,
+ revision.user, revision.userid,null,null,
+ null, null, null);
+ if (diffAndRevision.body !== undefined && diffAndRevision.body !== null) {
+ const firstDiffLine = getFirstDiffLineWithContent(diffAndRevision.body);
+ if (firstDiffLine !== undefined && firstDiffLine !== null) {
+ const section = getSectionForDiffLine(diffAndRevision.body,
+ firstDiffLine);
+ const newTalkPageTopicOutputObject = new NewTalkPageTopicExtended(
+ revision.revid, revision.parentid,
+ revision.timestamp, revision.user,
+ revision.userid,
+ firstDiffLine.text,firstDiffLine.type,
+ firstDiffLine.highlightRanges,
+ diffAndRevision.characterChangeWithSections.counts, section);
+ uncachedOutput.push(newTalkPageTopicOutputObject);
+ } else {
+ uncachedOutput.push(nullSnippetTalkPageObject);
+ }
+ } else {
+ const newTalkPageTopicOutputObject = new NewTalkPageTopicExtended(
+ revision.revid, revision.parentid,
+ revision.timestamp, revision.user, revision.userid,
+ null,null, null, null);
+ uncachedOutput.push(nullSnippetTalkPageObject);
+ }
+ });
+ }
+ }
+
+ return Object.assign({ nextRvStartId: response.nextRvStartId,
+ needsCacheCleanup: response.needsCacheCleanup,
+ uncachedOutput: uncachedOutput,
+ finalOutput: response.finalOutput } );
+ })
+ .then( (response) => {
+
+ var preformattedSnippets = [];
+ for (var o = 0; o < response.uncachedOutput.length; o++) {
+ const item = response.uncachedOutput[o];
+
+ if (item.outputType === undefined || item.outputType === null) {
+ continue;
+ }
+
+ if (item.outputType === 'new-talk-page-topic' && item.snippet !== null) {
+ const snippet = new PreformattedSnippet(item.revid, item.outputType,
+ item.snippet,1, null,
+ null);
+ preformattedSnippets.push(snippet);
+ } else if (item.outputType === 'large-change') {
+ for (let i = 0; i < item.significantChanges.length; i++) {
+ const significantChange = item.significantChanges[i];
+
+ if (significantChange.outputType === undefined ||
+ significantChange.outputType === null ||
+ significantChange.snippet === undefined ||
+ significantChange.snippet === null ||
+ significantChange.snippetType === undefined ||
+ significantChange.snippetType === null) {
+ continue;
+ }
+
+ if (significantChange.outputType === 'added-text') {
+ const snippet = new PreformattedSnippet(item.revid, item.outputType,
+ significantChange.snippet, significantChange.snippetType,
+ significantChange.snippetHighlightRanges, i);
+ preformattedSnippets.push(snippet);
+ }
+ }
+ }
+ }
+
+ // convert large snippets from wikitext to mobile-html
+ return snippetPromises(req, preformattedSnippets)
+ .then( (formattedSnippets) => {
+
+ // reassign formattedSnippets to snippet output
+ for (var fs = 0; fs < formattedSnippets.length; fs++) {
+ const formattedSnippet = formattedSnippets[fs];
+
+ for (var i = 0; i < response.uncachedOutput.length; i++) {
+ const item = response.uncachedOutput[i];
+
+ if (item.revid === undefined || item.revid === null ||
+ formattedSnippet.revid === undefined ||
+ formattedSnippet.revid === null ||
+ item.outputType === undefined || item.outputType === null ||
+ formattedSnippet.outputType === undefined ||
+ formattedSnippet.outputType === null) {
+ continue;
+ }
+
+ if (item.revid === formattedSnippet.revid &&
+ item.outputType === formattedSnippet.outputType) {
+ if (formattedSnippet.outputType === 'new-talk-page-topic') {
+ item.snippet = formattedSnippet.snippet;
+ } else if (formattedSnippet.outputType === 'large-change') {
+
+ if (item.significantChanges === undefined ||
+ item.significantChanges === null ||
+ formattedSnippet.indexOfSignificantChanges === undefined ||
+ formattedSnippet.indexOfSignificantChanges === null) {
+ continue;
+ }
+
+ if (item.significantChanges.length >
+ formattedSnippet.indexOfSignificantChanges) {
+ var significantChange =
+ item.significantChanges[
+ formattedSnippet.indexOfSignificantChanges
+ ];
+ significantChange.snippet = formattedSnippet.snippet;
+ if (significantChange.outputType === 'added-text') {
+ item.significantChanges[
+ formattedSnippet.indexOfSignificantChanges
+ ] = new AddedTextOutput(significantChange);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // push to final output and cache
+ // note we are using original response list, not snippet response
+ // (snippet only contains large)
+ response.uncachedOutput.forEach((item) => {
+ response.finalOutput.push(item);
+ var cacheKey;
+ if (item.outputType === 'new-talk-page-topic') {
+ const title = getTalkPageTitle(req);
+ setSignificantChangesCache(req, title, item);
+ } else {
+ setSignificantChangesCache(req, null, item);
+ }
+ });
+
+ return Object.assign({ nextRvStartId: response.nextRvStartId,
+ needsCacheCleanup: response.needsCacheCleanup,
+ finalOutput: response.finalOutput } );
+ });
+ })
+ .then( (response) => {
+
+ const sortedOutput = sortOutput((response.finalOutput));
+ const cleanedOutput = cleanOutput(sortedOutput);
+ const sha = shaFromSortedOutput(req, sortedOutput);
+
+ return editCountsAndGroupsPromise(req, cleanedOutput)
+ .then( (editCountsAndGroupsResponse) => {
+ return Object.assign({ nextRvStartId: response.nextRvStartId,
+ needsCacheCleanup: response.needsCacheCleanup,
+ cleanedOutput: editCountsAndGroupsResponse, sha: sha });
+ });
+ })
+ .then( (response) => {
+
+ if (response.needsCacheCleanup) {
+ cleanupCache(req);
+ }
+
+ const summary = getSummaryText(req);
+
+ const result = Object.assign({ nextRvStartId: response.nextRvStartId,
+ sha: response.sha,
+ timeline: response.cleanedOutput,
+ summary: summary });
+ return result;
+ });
+}
+
+router.get('/page/significant-events/:title', (req, res) => {
+ return getSignificantEvents(req, res).then( (response) => {
+ res.send(response).end();
+ });
+});
+
+var countCacheWarmupPages = 0;
+
+function recursiveGetSignificantEvents(req, res) {
+ countCacheWarmupPages++;
+ return getSignificantEvents(req, res).then( (response) => {
+
+ if (!response.nextRvStartId) {
+ const finalWarmupCount = countCacheWarmupPages;
+ countCacheWarmupPages = 0;
+ return Object.assign({ warmedPages: finalWarmupCount,
+ significantChangesCache: significantChangesCache });
+ }
+
+ req.query.rvstartid = response.nextRvStartId;
+ return recursiveGetSignificantEvents(req, res);
+ });
+}
+
+router.get('/page/significant-events-warmup/:title', (req, res) => {
+ return recursiveGetSignificantEvents(req, res)
+ .then( (response) => {
+ res.send(response).end();
+ });
+});
+
+module.exports = function(appObj) {
+ app = appObj;
+ return {
+ path: '/',
+ api_version: 1,
+ router
+ };
+};
]