From 02854f546800d53acf13a0c75d5cb68bd5d81b35 Mon Sep 17 00:00:00 2001 From: "J.M" Date: Sat, 15 Sep 2018 13:41:41 +0200 Subject: [PATCH 1/8] Reformat README, add wrong callback URL detection sample --- README.md | 501 +++++++++++++++++++++++++----------------------------- 1 file changed, 231 insertions(+), 270 deletions(-) diff --git a/README.md b/README.md index 0f05c2d..5278c15 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -codebird-js -=========== -*A Twitter library in JavaScript.* +# codebird-js + +_A Twitter library in JavaScript._ Copyright (C) 2010-2018 Jublo Limited @@ -11,17 +11,15 @@ the Free Software Foundation, either version 3 of the License, or This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License -along with this program. If not, see . +along with this program. If not, see . [![Travis Status](https://img.shields.io/travis/jublonet/codebird-js/develop.svg)](https://travis-ci.org/jublonet/codebird-js/branches) - -Including Codebird ------------------- +## Including Codebird To include Codebird in your code, add its scripts to your markup: @@ -36,20 +34,18 @@ cb.setConsumerKey("YOURKEY", "YOURSECRET"); You may also use a JavaScript module loader of your choice (such as [RequireJS](http://requirejs.org/) or the one bundled in Node.js) -to load Codebird unobtrusively. In Node.js, loading Codebird looks like this: +to load Codebird unobtrusively. In Node.js, loading Codebird looks like this: ```javascript var Codebird = require("codebird"); // or with leading "./", if the codebird.js file is in your main folder: // var Codebird = require("./codebird"); -var cb = new Codebird; +var cb = new Codebird(); cb.setConsumerKey("YOURKEY", "YOURSECRET"); ``` - -Authentication --------------- +## Authentication To authenticate your API requests on behalf of a certain Twitter user (following OAuth 1.0a), take a look at these steps: @@ -64,6 +60,7 @@ cb.setConsumerKey("YOURKEY", "YOURSECRET"); ``` You may either set the OAuth token and secret, if you already have them: + ```javascript cb.setToken("YOURTOKEN", "YOURTOKENSECRET"); ``` @@ -72,28 +69,30 @@ Or you authenticate, like this: ```javascript // gets a request token -cb.__call( - "oauth_requestToken", - {oauth_callback: "oob"}, - function (reply,rate,err) { - if (err) { - console.log("error response or timeout exceeded" + err.error); - } - if (reply) { - // stores it - cb.setToken(reply.oauth_token, reply.oauth_token_secret); - - // gets the authorize screen URL - cb.__call( - "oauth_authorize", - {}, - function (auth_url) { - window.codebird_auth = window.open(auth_url); - } - ); - } +cb.__call("oauth_requestToken", { oauth_callback: "oob" }, function( + reply, + rate, + err +) { + if (err) { + console.log("error response or timeout exceeded" + err.error); + } + if (reply) { + if (reply.errors && reply.errors["415"]) { + // check your callback URL + console.log(reply.errors["415"]); + return; } -); + + // stores the token + cb.setToken(reply.oauth_token, reply.oauth_token_secret); + + // gets the authorize screen URL + cb.__call("oauth_authorize", {}, function(auth_url) { + window.codebird_auth = window.open(auth_url); + }); + } +}); ``` :warning: Codebird server calls do not always go through when @@ -115,20 +114,20 @@ After the user enters the PIN, complete the authentication: ```javascript cb.__call( - "oauth_accessToken", - {oauth_verifier: document.getElementById("PINFIELD").value}, - function (reply,rate,err) { - if (err) { - console.log("error response or timeout exceeded" + err.error); - } - if (reply) { - // store the authenticated token, which may be different from the request token (!) - cb.setToken(reply.oauth_token, reply.oauth_token_secret); - } - - // if you need to persist the login after page reload, - // consider storing the token in a cookie or HTML5 local storage + "oauth_accessToken", + { oauth_verifier: document.getElementById("PINFIELD").value }, + function(reply, rate, err) { + if (err) { + console.log("error response or timeout exceeded" + err.error); } + if (reply) { + // store the authenticated token, which may be different from the request token (!) + cb.setToken(reply.oauth_token, reply.oauth_token_secret); + } + + // if you need to persist the login after page reload, + // consider storing the token in a cookie or HTML5 local storage + } ); ``` @@ -145,34 +144,32 @@ cb.logout(); Some API methods also support authenticating on a per-application level. This is useful for getting data that are not directly related to a specific -Twitter user, but generic to the Twitter ecosystem (such as ```search/tweets```). +Twitter user, but generic to the Twitter ecosystem (such as `search/tweets`). To obtain an app-only bearer token, call the appropriate API: ```javascript -cb.__call( - "oauth2_token", - {}, - function (reply, err) { - var bearer_token; - if (err) { - console.log("error response or timeout exceeded" + err.error); - } - if (reply) { - bearer_token = reply.access_token; - } - } -); +cb.__call("oauth2_token", {}, function(reply, err) { + var bearer_token; + if (err) { + console.log("error response or timeout exceeded" + err.error); + } + if (reply) { + bearer_token = reply.access_token; + } +}); ``` I strongly recommend that you store the obtained bearer token in your database. There is no need to re-obtain the token with each page load, as it becomes invalid -only when you call the ```oauth2/invalidate_token``` method. +only when you call the `oauth2/invalidate_token` method. If you already have your token, tell Codebird to use it: + ```javascript cb.setBearerToken("YOURBEARERTOKEN"); ``` + In this case, you don't need to set the consumer key and secret. For sending an API request with app-only auth, see the ‘Usage examples’ section. @@ -180,100 +177,95 @@ For sending an API request with app-only auth, see the ‘Usage examples’ sect 1. Before sending your user off to Twitter, you have to store the request token and its secret, for example in a cookie. 2. In the callback URL, extract those values and assign them to the Codebird object. -3. Extract the ```oauth_verifier``` field from the request URI. +3. Extract the `oauth_verifier` field from the request URI. In Javascript, try extracting the URL parameter like this: ```javascript -var cb = new Codebird; +var cb = new Codebird(); var current_url = location.toString(); -var query = current_url.match(/\?(.+)$/).split("&"); -var parameters = {}; +var query = current_url.match(/\?(.+)$/).split("&"); +var parameters = {}; var parameter; cb.setConsumerKey("STUFF", "HERE"); for (var i = 0; i < query.length; i++) { - parameter = query[i].split("="); - if (parameter.length === 1) { - parameter[1] = ""; - } - parameters[decodeURIComponent(parameter[0])] = decodeURIComponent(parameter[1]); + parameter = query[i].split("="); + if (parameter.length === 1) { + parameter[1] = ""; + } + parameters[decodeURIComponent(parameter[0])] = decodeURIComponent( + parameter[1] + ); } // check if oauth_verifier is set if (typeof parameters.oauth_verifier !== "undefined") { - // assign stored request token parameters to codebird here - // ... - cb.setToken(stored_somewhere.oauth_token, stored_somewhere.oauth_token_secret); - - cb.__call( - "oauth_accessToken", - { - oauth_verifier: parameters.oauth_verifier - }, - function (reply, rate, err) { - if (err) { - console.log("error response or timeout exceeded" + err.error); - } - if (reply) { - cb.setToken(reply.oauth_token, reply.oauth_token_secret); - } - - // if you need to persist the login after page reload, - // consider storing the token in a cookie or HTML5 local storage - } - ); + // assign stored request token parameters to codebird here + // ... + cb.setToken( + stored_somewhere.oauth_token, + stored_somewhere.oauth_token_secret + ); + + cb.__call( + "oauth_accessToken", + { + oauth_verifier: parameters.oauth_verifier + }, + function(reply, rate, err) { + if (err) { + console.log("error response or timeout exceeded" + err.error); + } + if (reply) { + cb.setToken(reply.oauth_token, reply.oauth_token_secret); + } + + // if you need to persist the login after page reload, + // consider storing the token in a cookie or HTML5 local storage + } + ); } ``` +## Usage examples -Usage examples --------------- - -:warning: *Because the Consumer Key and Token Secret are available in the code, +:warning: _Because the Consumer Key and Token Secret are available in the code, it is important that you configure your app as read-only at Twitter, -unless you are sure to know what you are doing.* +unless you are sure to know what you are doing._ When you have an access token, calling the API is simple: ```javascript cb.setToken("YOURTOKEN", "YOURTOKENSECRET"); // see above -cb.__call( - "statuses_homeTimeline", - {}, - function (reply, rate, err) { - console.log(reply); - console.log(err); - } -); +cb.__call("statuses_homeTimeline", {}, function(reply, rate, err) { + console.log(reply); + console.log(err); +}); ``` Tweeting is as easy as this: ```javascript -cb.__call( - "statuses_update", - {"status": "Whohoo, I just tweeted!"}, - function (reply, rate, err) { - // ... - } -); +cb.__call("statuses_update", { status: "Whohoo, I just tweeted!" }, function( + reply, + rate, + err +) { + // ... +}); ``` -:warning: *Make sure to urlencode any parameter values that contain -query-reserved characters, like tweeting the `&` sign:* +:warning: _Make sure to urlencode any parameter values that contain +query-reserved characters, like tweeting the `&` sign:_ ```javascript var params = "status=" + encodeURIComponent("Fish & chips"); -cb.__call( - "statuses_update", - params, - function (reply, rate, err) { - // ... - } -); +cb.__call("statuses_update", params, function(reply, rate, err) { + // ... +}); ``` In most cases, giving all parameters in an array is easier, @@ -281,41 +273,29 @@ because no encoding is needed: ```javascript var params = { - status: "Fish & chips" + status: "Fish & chips" }; -cb.__call( - "statuses_update", - params, - function (reply, rate, err) { - // ... - } -); +cb.__call("statuses_update", params, function(reply, rate, err) { + // ... +}); ``` ```javascript var params = { - screen_name: "jublonet" + screen_name: "jublonet" }; -cb.__call( - "users_show", - params, - function (reply, rate, err) { - // ... - } -); +cb.__call("users_show", params, function(reply, rate, err) { + // ... +}); ``` ```javascript var params = { - q: "NYC" + q: "NYC" }; -cb.__call( - "search_tweets", - params, - function (reply) { - // ... - } -); +cb.__call("search_tweets", params, function(reply) { + // ... +}); ``` ### Uploading media to Twitter @@ -340,7 +320,7 @@ cb.__call( ``` **Second,** you attach the collected media ids for all images to your call -to ```statuses/update```, like this: +to `statuses/update`, like this: ```javascript cb.__call( @@ -364,57 +344,55 @@ add another parameter to your method call, like this: ```javascript cb.__call( - "search_tweets", - "q=Twitter", - function (reply) { - // ... - }, - true // this parameter required + "search_tweets", + "q=Twitter", + function(reply) { + // ... + }, + true // this parameter required ); ``` Bear in mind that not all API methods support application-only auth. -Mapping API methods to Codebird function calls ----------------------------------------------- +## Mapping API methods to Codebird function calls As you can see from the last example, there is a general way how Twitter’s API methods map to Codebird function calls. The general rules are: 1. For each slash in a Twitter API method, use an underscore in the Codebird function. - Example: ```statuses/update``` maps to ```cb.__call("statuses_update", ...)```. + Example: `statuses/update` maps to `cb.__call("statuses_update", ...)`. 2. For each underscore in a Twitter API method, use camelCase in the Codebird function. - Example: ```statuses/home_timeline``` maps to ```cb.__call("statuses_homeTimeline", ...)```. + Example: `statuses/home_timeline` maps to `cb.__call("statuses_homeTimeline", ...)`. 3. For each parameter template in method, use UPPERCASE in the Codebird function. - Also don’t forget to include the parameter in your parameter list. + Also don’t forget to include the parameter in your parameter list. - Examples: - - ```statuses/show/:id``` maps to ```cb.__call("statuses_show_ID", 'id=12345', ...)```. - - ```users/profile_image/:screen_name``` maps to - ```cb.__call("users_profileImage_SCREEN_NAME", "screen_name=jublonet", ...)```. + Examples: -HTTP methods (GET, POST, DELETE etc.) -------------------------------------- + - `statuses/show/:id` maps to `cb.__call("statuses_show_ID", 'id=12345', ...)`. + - `users/profile_image/:screen_name` maps to + `cb.__call("users_profileImage_SCREEN_NAME", "screen_name=jublonet", ...)`. + +## HTTP methods (GET, POST, DELETE etc.) Never care about which HTTP method (verb) to use when calling a Twitter API. Codebird is intelligent enough to find out on its own. -Response codes --------------- +## Response codes The HTTP response code that the API gave is included in any return values. -You can find it within the return object’s ```httpstatus``` property. +You can find it within the return object’s `httpstatus` property. ### Dealing with rate-limits Basically, Codebird leaves it up to you to handle Twitter’s rate limit. The library returns the response HTTP status code, so you can detect rate limits. -I suggest you to check if the ```reply.httpstatus``` property is ```400``` +I suggest you to check if the `reply.httpstatus` property is `400` and check with the Twitter API to find out if you are currently being rate-limited. See the [Rate Limiting FAQ](https://developer.twitter.com/en/docs/basics/rate-limiting) @@ -425,33 +403,28 @@ you will receive rate-limiting details in this parameter, if the Twitter API responds with rate-limiting HTTP headers. ```javascript -cb.__call( - "search_tweets", - "q=Twitter", - function (reply, rate_limit_status) { - console.log(rate_limit_status); - // ... - } -); +cb.__call("search_tweets", "q=Twitter", function(reply, rate_limit_status) { + console.log(rate_limit_status); + // ... +}); ``` -API calls and the same-origin policy ------------------------------------- +## API calls and the same-origin policy Normally, browsers only allow requests being sent to addresses that are on -the same base domain. This is a security feature called the “same-origin -policy.” However, this policy is in your way when you try to access the +the same base domain. This is a security feature called the “same-origin +policy.” However, this policy is in your way when you try to access the (remote) Twitter API domain and its methods. ### Cross-domain requests -With Codebird, don’t worry about this. We automatically send cross-domain +With Codebird, don’t worry about this. We automatically send cross-domain requests using a secured proxy that sends back the required headers to the user’s browser. This CORS proxy is using an encrypted SSL connection. -*We do not record data sent to or from the Twitter API. -Using Codebird’s CORS proxy is subject to the Acceptable use policy.* +_We do not record data sent to or from the Twitter API. +Using Codebird’s CORS proxy is subject to the Acceptable use policy._ If your JavaScript environment is not restricted under the same-origin policy (for example in node.js), direct connections to the Twitter API are established @@ -466,7 +439,7 @@ cb.setUseProxy(false); ### Support for Internet Explorer 7 to 9 Cross-domain requests work well in any browser except for -Internet Explorer 7-9. Codebird cannot send POST requests in these browsers. +Internet Explorer 7-9. Codebird cannot send POST requests in these browsers. For IE7-9, Codebird works in limited operation mode: - Calls to GET methods work fine, @@ -475,18 +448,17 @@ For IE7-9, Codebird works in limited operation mode: ### Using your own proxy server -The source code of the CORS proxy is publicly available. If you want to, -set up your own instance on your server. Afterwards, tell Codebird the +The source code of the CORS proxy is publicly available. If you want to, +set up your own instance on your server. Afterwards, tell Codebird the address: ```javascript cb.setProxy("https://example.com/codebird-cors-proxy/"); ``` -Heads up! Follow the notes in the [codebird-cors-proxy README](https://github.com/jublonet/codebird-cors-proxy/#readme) for details. +Heads up! Follow the notes in the [codebird-cors-proxy README](https://github.com/jublonet/codebird-cors-proxy/#readme) for details. -Using multiple Codebird instances ---------------------------------- +## Using multiple Codebird instances By default, each Codebird instance works on its own. @@ -494,23 +466,22 @@ If you need to run requests to the Twitter API for multiple users at once, Codebird supports this automatically. Just create a new object: ```javascript -var cb1 = new Codebird; -var cb2 = new Codebird; +var cb1 = new Codebird(); +var cb2 = new Codebird(); ``` Please note that your OAuth consumer key and secret is shared within multiple Codebird instances, while the OAuth request and access tokens with their -secrets are *not* shared. +secrets are _not_ shared. -How Do I…? ----------- +## How Do I…? ### …get user ID, screen name and more details about the current user? When the user returns from the authentication screen, you need to trade the obtained request token for an access token, using the OAuth verifier. As discussed in the section ‘Usage example,’ you use a call to -```oauth/access_token``` to do that. +`oauth/access_token` to do that. The API reply to this method call tells you details about the user that just logged in. These details contain the **user ID** and the **screen name.** @@ -528,22 +499,18 @@ Take a look at the returned data as follows: ``` If you need to get more details, such as the user’s latest tweet, -you should fetch the complete User Entity. The simplest way to get the +you should fetch the complete User Entity. The simplest way to get the user entity of the currently authenticated user is to use the -```account/verify_credentials``` API method. In Codebird, it works like this: +`account/verify_credentials` API method. In Codebird, it works like this: ```javascript -cb.__call( - "account_verifyCredentials", - {}, - function (reply) { - console.log(reply); - } -); +cb.__call("account_verifyCredentials", {}, function(reply) { + console.log(reply); +}); ``` I suggest to cache the User Entity after obtaining it, as the -```account/verify_credentials``` method is rate-limited by 15 calls per 15 minutes. +`account/verify_credentials` method is rate-limited by 15 calls per 15 minutes. ### …walk through cursored results? @@ -555,39 +522,34 @@ forwards through these pages. Here is how you can walk through cursored results with Codebird. 1. Get the first result set of a cursored method: + ```javascript -cb.__call( - "followers_list", - {}, - function (result1) { - // ... - } -); +cb.__call("followers_list", {}, function(result1) { + // ... +}); ``` -2. To navigate forth, take the ```next_cursor_str```: +2. To navigate forth, take the `next_cursor_str`: + ```javascript var nextCursor = result1.next_cursor_str; ``` -3. If ```nextCursor``` is not 0, use this cursor to request the next result page: +3. If `nextCursor` is not 0, use this cursor to request the next result page: + ```javascript - if (nextCursor > 0) { - cb.__call( - "followers_list", - {cursor: nextCursor}, - function (result2) { - // ... - } - ); - } +if (nextCursor > 0) { + cb.__call("followers_list", { cursor: nextCursor }, function(result2) { + // ... + }); +} ``` -To navigate back instead of forth, use the field ```resultX.previous_cursor_str``` -instead of ```next_cursor_str```. +To navigate back instead of forth, use the field `resultX.previous_cursor_str` +instead of `next_cursor_str`. -It might make sense to use the cursors in a loop. Watch out, though, -not to send more than the allowed number of requests to ```followers/list``` +It might make sense to use the cursors in a loop. Watch out, though, +not to send more than the allowed number of requests to `followers/list` per rate-limit timeframe, or else you will hit your rate-limit. ### …use xAuth with Codebird? @@ -596,18 +558,19 @@ Codebird supports xAuth just like every other authentication used at Twitter. Remember that your application needs to be whitelisted to be able to use xAuth. Here’s an example: + ```javascript cb.__call( - "oauth_accessToken", - { - "x_auth_username": "username", - "x_auth_password": "4h3_p4$$w0rd", - "x_auth_mode" : "client_auth" - }, - function (reply) { - console.log(reply); - // ... - } + "oauth_accessToken", + { + x_auth_username: "username", + x_auth_password: "4h3_p4$$w0rd", + x_auth_mode: "client_auth" + }, + function(reply) { + console.log(reply); + // ... + } ); ``` @@ -654,16 +617,14 @@ Here’s a sample for adding a Tweet using the Collections API: ```javascript cb.__call( - "collections_entries_curate", - { - "id": "custom-672852634622144512", - "changes": [ - {"op": "add", "tweet_id": "672727928262828032"} - ] - }, - function (reply, rate) { - document.body.innerText = JSON.stringify(reply); - } + "collections_entries_curate", + { + id: "custom-672852634622144512", + changes: [{ op: "add", tweet_id: "672727928262828032" }] + }, + function(reply, rate) { + document.body.innerText = JSON.stringify(reply); + } ); ``` @@ -688,17 +649,16 @@ Codebird will auto-detect and use any of the following: Here’s a usage sample for promises: ```javascript -cb.__call( - "statuses_update", - {"status": "Whohoo, I just tweeted!"} -).then(function (data) { - var reply = data.reply, - rate = data.rate; - // ... - }, - function (err) { - // ... - }); +cb.__call("statuses_update", { status: "Whohoo, I just tweeted!" }).then( + function(data) { + var reply = data.reply, + rate = data.rate; + // ... + }, + function(err) { + // ... + } +); ``` Since the app-only flag is the fourth parameter for `__call`, @@ -706,19 +666,20 @@ you’ll have to provide a callback stub nonetheless even with promises: ```javascript cb.__call( - "search_tweets", - {"q": "#PHP7"}, - null, // no callback needed, we have the promise - true // app-only auth - -).then(function (data) { - var reply = data.reply, - rate = data.rate; - // ... - }, - function (err) { - // ... - }); + "search_tweets", + { q: "#PHP7" }, + null, // no callback needed, we have the promise + true // app-only auth +).then( + function(data) { + var reply = data.reply, + rate = data.rate; + // ... + }, + function(err) { + // ... + } +); ``` **Tips:** From 4e79fb08776e20f37040a24e70142d3b184f11fd Mon Sep 17 00:00:00 2001 From: "J.M" Date: Sat, 15 Sep 2018 13:42:03 +0200 Subject: [PATCH 2/8] OAuth tests: Increase Event Emitter max listeners --- test/oauth_tests.js | 101 +++++++++++++++++++++++--------------------- 1 file changed, 53 insertions(+), 48 deletions(-) diff --git a/test/oauth_tests.js b/test/oauth_tests.js index 7c611b1..7e48e5d 100644 --- a/test/oauth_tests.js +++ b/test/oauth_tests.js @@ -1,99 +1,104 @@ +require("events").EventEmitter.defaultMaxListeners = 15; + const tape = require("tape"), _test = require("tape-promise"), test = _test(tape), // decorate tape CodebirdM = require("./codebirdm"); function getCB() { - var cb = new CodebirdM.default; + var cb = new CodebirdM.default(); cb.setConsumerKey("123", "456"); return cb; } -test("Tests oauth_authenticate Promise", function (t) { +test("Tests oauth_authenticate Promise", function(t) { const cb = getCB(); t.plan(1); cb.setToken("123", "456"); - return cb.oauth_authenticate().then(function (a) { - t.deepEqual( - a, - {reply: "https://api.twitter.com/oauth/authenticate?oauth_token=123"} - ) + return cb.oauth_authenticate().then(function(a) { + t.deepEqual(a, { + reply: "https://api.twitter.com/oauth/authenticate?oauth_token=123" + }); }); }); -test("Tests oauth_authenticate callback", function (t) { +test("Tests oauth_authenticate callback", function(t) { const cb = getCB(); t.plan(4); cb.setToken("123", "456"); - cb.oauth_authenticate({}, function (a) { - t.equal( - a, "https://api.twitter.com/oauth/authenticate?oauth_token=123" - ) + cb.oauth_authenticate({}, function(a) { + t.equal(a, "https://api.twitter.com/oauth/authenticate?oauth_token=123"); }); - cb.oauth_authenticate({force_login: true}, function (a) { + cb.oauth_authenticate({ force_login: true }, function(a) { t.equal( - a, "https://api.twitter.com/oauth/authenticate?oauth_token=123&force_login=1" - ) - }); - cb.oauth_authenticate({force_login: true, screen_name: "TwitterAPI"}, function (a) { - t.equal( - a, "https://api.twitter.com/oauth/authenticate?oauth_token=123&force_login=1&screen_name=TwitterAPI" - ) + a, + "https://api.twitter.com/oauth/authenticate?oauth_token=123&force_login=1" + ); }); - cb.oauth_authenticate({screen_name: "TwitterAPI"}, function (a) { + cb.oauth_authenticate( + { force_login: true, screen_name: "TwitterAPI" }, + function(a) { + t.equal( + a, + "https://api.twitter.com/oauth/authenticate?oauth_token=123&force_login=1&screen_name=TwitterAPI" + ); + } + ); + cb.oauth_authenticate({ screen_name: "TwitterAPI" }, function(a) { t.equal( - a, "https://api.twitter.com/oauth/authenticate?oauth_token=123&screen_name=TwitterAPI" - ) + a, + "https://api.twitter.com/oauth/authenticate?oauth_token=123&screen_name=TwitterAPI" + ); }); }); -test("Tests oauth_authorize callback", function (t) { +test("Tests oauth_authorize callback", function(t) { const cb = getCB(); t.plan(4); cb.setToken("123", "456"); - cb.oauth_authorize({}, function (a) { - t.equal( - a, "https://api.twitter.com/oauth/authorize?oauth_token=123" - ) + cb.oauth_authorize({}, function(a) { + t.equal(a, "https://api.twitter.com/oauth/authorize?oauth_token=123"); }); - cb.oauth_authorize({force_login: true}, function (a) { + cb.oauth_authorize({ force_login: true }, function(a) { t.equal( - a, "https://api.twitter.com/oauth/authorize?oauth_token=123&force_login=1" - ) + a, + "https://api.twitter.com/oauth/authorize?oauth_token=123&force_login=1" + ); }); - cb.oauth_authorize({force_login: true, screen_name: "TwitterAPI"}, function (a) { + cb.oauth_authorize({ force_login: true, screen_name: "TwitterAPI" }, function( + a + ) { t.equal( - a, "https://api.twitter.com/oauth/authorize?oauth_token=123&force_login=1&screen_name=TwitterAPI" - ) + a, + "https://api.twitter.com/oauth/authorize?oauth_token=123&force_login=1&screen_name=TwitterAPI" + ); }); - cb.oauth_authorize({screen_name: "TwitterAPI"}, function (a) { + cb.oauth_authorize({ screen_name: "TwitterAPI" }, function(a) { t.equal( - a, "https://api.twitter.com/oauth/authorize?oauth_token=123&screen_name=TwitterAPI" - ) + a, + "https://api.twitter.com/oauth/authorize?oauth_token=123&screen_name=TwitterAPI" + ); }); }); -test("Tests oauth2_token", function (t) { +test("Tests oauth2_token", function(t) { const cb = getCB(); t.plan(1); - cb.oauth2_token( - function (a) { - t.deepEqual( - a, { - token_type: "bearer", - access_token: "VqiO0n2HrKE", - httpstatus: 200 - } - ); + cb.oauth2_token(function(a) { + t.deepEqual(a, { + token_type: "bearer", + access_token: "VqiO0n2HrKE", + httpstatus: 200 }); + }); }); -test("Tests signing of boolean parameters", function (t) { +test("Tests signing of boolean parameters", function(t) { const cb = getCB(); t.equal( From ecf24a34541f8c027129bd957465adeab6028143 Mon Sep 17 00:00:00 2001 From: "J.M" Date: Sat, 15 Sep 2018 13:42:19 +0200 Subject: [PATCH 3/8] Add reply XML detection --- test/functional_tests.js | 59 ++++++++++++++++++++++++++++------------ 1 file changed, 41 insertions(+), 18 deletions(-) diff --git a/test/functional_tests.js b/test/functional_tests.js index aa717aa..4ec66a7 100644 --- a/test/functional_tests.js +++ b/test/functional_tests.js @@ -1,37 +1,60 @@ const tape = require("tape"), _test = require("tape-promise"), test = _test(tape), // decorate tape - CodebirdT = require("./codebirdt"); + CodebirdT = require("./codebirdt"), + CodebirdM = require("./codebirdm"); -function getCB() { - var cb = new CodebirdT.default; +function getCB(mock) { + if (typeof mock === "undefined") { + var mock = false; + } + var cb = mock ? new CodebirdM.default() : new CodebirdT.default(); cb.setConsumerKey("123", "456"); return cb; } -test("Tests statuses/update", function (t) { +test("Tests statuses/update", function(t) { const cb = getCB(); t.plan(1); cb.setToken("123", "456"); - return cb.__call( - "statuses_update", - {"status": "Whohoo, I just tweeted!"} - ).then(function (reply, rate, err) { - t.deepEqual( - reply, - { + return cb + .__call("statuses_update", { status: "Whohoo, I just tweeted!" }) + .then(function(reply, rate, err) { + t.deepEqual(reply, { rate: { - limit: null, remaining: null, reset: null + limit: null, + remaining: null, + reset: null }, reply: { - errors: [ - { code: 89, message: "Invalid or expired token." } - ], + errors: [{ code: 89, message: "Invalid or expired token." }], httpstatus: 401 } - } - ) - }); + }); + }); +}); + +test("Tests reply XML detection", function(t) { + const cb = getCB(true); + t.plan(1); + + cb.setToken("123", "456"); + return cb + .__call("oauth_requestToken", { + oauth_callback: "http://www.example.com/#xml_detection" + }) + .then(function(reply, rate, err) { + t.deepEqual(reply, { + reply: { + errors: { + "415": + "Callback URL not approved for this client application. Approved callback URLs can be adjusted in your application settings" + }, + httpstatus: 401 + }, + rate: null + }); + }); }); From 268f5eeb460a96b71dddb942b1161193e8b64f04 Mon Sep 17 00:00:00 2001 From: "J.M" Date: Sat, 15 Sep 2018 13:42:52 +0200 Subject: [PATCH 4/8] Add Mock reply for XML detection --- test/codebirdm.es7.js | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/test/codebirdm.es7.js b/test/codebirdm.es7.js index 766ad4b..fdb98c5 100644 --- a/test/codebirdm.es7.js +++ b/test/codebirdm.es7.js @@ -17,7 +17,6 @@ import CodebirdT from "./codebirdt"; * @package codebird-test */ export default class CodebirdM extends CodebirdT { - constructor() { super(); /** @@ -26,15 +25,22 @@ export default class CodebirdM extends CodebirdT { this._mock_replies = { default: { httpstatus: 404, - reply: "{\"errors\":[{\"message\":\"Sorry, that page does not exist\",\"code\":34}]}" + reply: + '{"errors":[{"message":"Sorry, that page does not exist","code":34}]}' }, "GET https://api.twitter.com/1.1/users/show.json?screen_name=TwitterAPI": { httpstatus: 200, - reply: "{\"id\":6253282,\"id_str\":\"6253282\",\"name\":\"Twitter API\",\"screen_name\":\"twitterapi\",\"location\":\"San Francisco, CA\",\"profile_location\":null,\"description\":\"The Real Twitter API. I tweet about API changes, service issues and happily answer questions about Twitter and our API. Don't get an answer? It's on my website.\",\"url\":\"http:\/\/t.co\/78pYTvWfJd\",\"entities\":{\"url\":{\"urls\":[{\"url\":\"http:\/\/t.co\/78pYTvWfJd\",\"expanded_url\":\"http:\/\/dev.twitter.com\",\"display_url\":\"dev.twitter.com\",\"indices\":[0,22]}]},\"description\":{\"urls\":[]}},\"protected\":false,\"followers_count\":4993679,\"friends_count\":48,\"listed_count\":13001,\"created_at\":\"Wed May 23 06:01:13 +0000 2007\",\"favourites_count\":27,\"utc_offset\":-28800,\"time_zone\":\"Pacific Time (US & Canada)\",\"geo_enabled\":true,\"verified\":true,\"statuses_count\":3553,\"lang\":\"en\",\"status\":{\"created_at\":\"Tue Nov 24 08:56:07 +0000 2015\",\"id\":669077021138493440,\"id_str\":\"669077021138493440\",\"text\":\"Additional 64-bit entity ID migration coming in Feb 2016 https:\/\/t.co\/eQIGvw1rsJ\",\"source\":\"\u003ca href=\\\"https:\/\/about.twitter.com\/products\/tweetdeck\\\" rel=\\\"nofollow\\\"\u003eTweetDeck\u003c\/a\u003e\",\"truncated\":false,\"in_reply_to_status_id\":null,\"in_reply_to_status_id_str\":null,\"in_reply_to_user_id\":null,\"in_reply_to_user_id_str\":null,\"in_reply_to_screen_name\":null,\"geo\":null,\"coordinates\":null,\"place\":null,\"contributors\":null,\"retweet_count\":67,\"favorite_count\":79,\"entities\":{\"hashtags\":[],\"symbols\":[],\"user_mentions\":[],\"urls\":[{\"url\":\"https:\/\/t.co\/eQIGvw1rsJ\",\"expanded_url\":\"https:\/\/twittercommunity.com\/t\/migration-of-twitter-core-entities-to-64-bit-ids\/56881\",\"display_url\":\"twittercommunity.com\/t\/migration-of\u2026\",\"indices\":[57,80]}]},\"favorited\":false,\"retweeted\":false,\"possibly_sensitive\":false,\"lang\":\"en\"},\"contributors_enabled\":false,\"is_translator\":false,\"is_translation_enabled\":false,\"profile_background_color\":\"C0DEED\",\"profile_background_image_url\":\"http:\/\/pbs.twimg.com\/profile_background_images\/656927849\/miyt9dpjz77sc0w3d4vj.png\",\"profile_background_image_url_https\":\"https:\/\/pbs.twimg.com\/profile_background_images\/656927849\/miyt9dpjz77sc0w3d4vj.png\",\"profile_background_tile\":true,\"profile_image_url\":\"http:\/\/pbs.twimg.com\/profile_images\/2284174872\/7df3h38zabcvjylnyfe3_normal.png\",\"profile_image_url_https\":\"https:\/\/pbs.twimg.com\/profile_images\/2284174872\/7df3h38zabcvjylnyfe3_normal.png\",\"profile_banner_url\":\"https:\/\/pbs.twimg.com\/profile_banners\/6253282\/1431474710\",\"profile_link_color\":\"0084B4\",\"profile_sidebar_border_color\":\"C0DEED\",\"profile_sidebar_fill_color\":\"DDEEF6\",\"profile_text_color\":\"333333\",\"profile_use_background_image\":true,\"has_extended_profile\":false,\"default_profile\":false,\"default_profile_image\":false,\"following\":true,\"follow_request_sent\":false,\"notifications\":false}" + reply: + '{"id":6253282,"id_str":"6253282","name":"Twitter API","screen_name":"twitterapi","location":"San Francisco, CA","profile_location":null,"description":"The Real Twitter API. I tweet about API changes, service issues and happily answer questions about Twitter and our API. Don\'t get an answer? It\'s on my website.","url":"http://t.co/78pYTvWfJd","entities":{"url":{"urls":[{"url":"http://t.co/78pYTvWfJd","expanded_url":"http://dev.twitter.com","display_url":"dev.twitter.com","indices":[0,22]}]},"description":{"urls":[]}},"protected":false,"followers_count":4993679,"friends_count":48,"listed_count":13001,"created_at":"Wed May 23 06:01:13 +0000 2007","favourites_count":27,"utc_offset":-28800,"time_zone":"Pacific Time (US & Canada)","geo_enabled":true,"verified":true,"statuses_count":3553,"lang":"en","status":{"created_at":"Tue Nov 24 08:56:07 +0000 2015","id":669077021138493440,"id_str":"669077021138493440","text":"Additional 64-bit entity ID migration coming in Feb 2016 https://t.co/eQIGvw1rsJ","source":"\u003ca href=\\"https://about.twitter.com/products/tweetdeck\\" rel=\\"nofollow\\"\u003eTweetDeck\u003c/a\u003e","truncated":false,"in_reply_to_status_id":null,"in_reply_to_status_id_str":null,"in_reply_to_user_id":null,"in_reply_to_user_id_str":null,"in_reply_to_screen_name":null,"geo":null,"coordinates":null,"place":null,"contributors":null,"retweet_count":67,"favorite_count":79,"entities":{"hashtags":[],"symbols":[],"user_mentions":[],"urls":[{"url":"https://t.co/eQIGvw1rsJ","expanded_url":"https://twittercommunity.com/t/migration-of-twitter-core-entities-to-64-bit-ids/56881","display_url":"twittercommunity.com/t/migration-of\u2026","indices":[57,80]}]},"favorited":false,"retweeted":false,"possibly_sensitive":false,"lang":"en"},"contributors_enabled":false,"is_translator":false,"is_translation_enabled":false,"profile_background_color":"C0DEED","profile_background_image_url":"http://pbs.twimg.com/profile_background_images/656927849/miyt9dpjz77sc0w3d4vj.png","profile_background_image_url_https":"https://pbs.twimg.com/profile_background_images/656927849/miyt9dpjz77sc0w3d4vj.png","profile_background_tile":true,"profile_image_url":"http://pbs.twimg.com/profile_images/2284174872/7df3h38zabcvjylnyfe3_normal.png","profile_image_url_https":"https://pbs.twimg.com/profile_images/2284174872/7df3h38zabcvjylnyfe3_normal.png","profile_banner_url":"https://pbs.twimg.com/profile_banners/6253282/1431474710","profile_link_color":"0084B4","profile_sidebar_border_color":"C0DEED","profile_sidebar_fill_color":"DDEEF6","profile_text_color":"333333","profile_use_background_image":true,"has_extended_profile":false,"default_profile":false,"default_profile_image":false,"following":true,"follow_request_sent":false,"notifications":false}' }, "POST https://api.twitter.com/oauth2/token": { httpstatus: 200, - reply: "{\"token_type\":\"bearer\",\"access_token\":\"VqiO0n2HrKE\"}" + reply: '{"token_type":"bearer","access_token":"VqiO0n2HrKE"}' + }, + "POST https://api.twitter.com/oauth/request_token": { + httpstatus: 401, + reply: + 'Callback URL not approved for this client application. Approved callback URLs can be adjusted in your application settings' } }; @@ -45,16 +51,16 @@ export default class CodebirdM extends CodebirdT { this.xml.url = url; const key = `${httpmethod} ${url}`; if (this._mock_replies.hasOwnProperty(key)) { - this.xml.status = this._mock_replies[key].httpstatus; + this.xml.status = this._mock_replies[key].httpstatus; this.xml.responseText = this._mock_replies[key].reply; } else { - this.xml.status = this._mock_replies.default.httpstatus; + this.xml.status = this._mock_replies.default.httpstatus; this.xml.responseText = this._mock_replies.default.reply; } }, setRequestHeader: () => true, onreadystatechange: () => false, - send: function () { + send: function() { setTimeout(this.onreadystatechange, 200); } }; @@ -62,5 +68,5 @@ export default class CodebirdM extends CodebirdT { _getXmlRequestObject() { return this.xml; - }; + } } From 405139e21ccee15777654eb0258159135ddda9aa Mon Sep 17 00:00:00 2001 From: "J.M" Date: Sat, 15 Sep 2018 13:43:19 +0200 Subject: [PATCH 5/8] Add XML error detection. Fix #156. --- codebird.es7.js | 432 ++++++++++++++++++++++++++++++------------------ codebird.js | 19 ++- 2 files changed, 288 insertions(+), 163 deletions(-) diff --git a/codebird.es7.js b/codebird.es7.js index b35bcf6..5c54edb 100644 --- a/codebird.es7.js +++ b/codebird.es7.js @@ -26,7 +26,6 @@ * @subpackage codebird-js */ class Codebird { - constructor() { /** * The OAuth consumer key of your registered app @@ -82,9 +81,9 @@ * Whether to access the API via a proxy that is allowed by CORS * Assume that CORS is only necessary in browsers */ - this._use_proxy = (typeof navigator !== "undefined" - && typeof navigator.userAgent !== "undefined" - ); + this._use_proxy = + typeof navigator !== "undefined" && + typeof navigator.userAgent !== "undefined"; /** * The Request or access token. Used to sign requests @@ -101,7 +100,8 @@ */ this._version = "3.0.0-dev"; - this.b64_alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; + this.b64_alphabet = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; } /** @@ -156,8 +156,7 @@ * @return bool */ logout() { - this._oauth_token = - this._oauth_token_secret = null; + this._oauth_token = this._oauth_token_secret = null; return true; } @@ -200,8 +199,13 @@ * @return mixed The encoded data */ _url(data) { - if ((/boolean|number|string/).test(typeof data)) { - return encodeURIComponent(data).replace(/!/g, "%21").replace(/'/g, "%27").replace(/\(/g, "%28").replace(/\)/g, "%29").replace(/\*/g, "%2A"); + if (/boolean|number|string/.test(typeof data)) { + return encodeURIComponent(data) + .replace(/!/g, "%21") + .replace(/'/g, "%27") + .replace(/\(/g, "%28") + .replace(/\)/g, "%29") + .replace(/\*/g, "%2A"); } else { return ""; } @@ -222,11 +226,20 @@ * @return string The hash */ _sha1(e) { - function n (e, b) { - e[b >> 5] |= 128 << 24 - b % 32; - e[(b + 64 >> 9 << 4) + 15] = b; - for (var c = new Array(80), a = 1732584193, d = -271733879, h = -1732584194, - k = 271733878, g = -1009589776, p = 0; p < e.length; p += 16) { + function n(e, b) { + e[b >> 5] |= 128 << (24 - (b % 32)); + e[(((b + 64) >> 9) << 4) + 15] = b; + for ( + var c = new Array(80), + a = 1732584193, + d = -271733879, + h = -1732584194, + k = 271733878, + g = -1009589776, + p = 0; + p < e.length; + p += 16 + ) { for (var o = a, q = d, r = h, s = k, t = g, f = 0; 80 > f; f++) { let m; @@ -234,17 +247,35 @@ m = e[p + f]; } else { m = c[f - 3] ^ c[f - 8] ^ c[f - 14] ^ c[f - 16]; - m = m << 1 | m >>> 31; + m = (m << 1) | (m >>> 31); } c[f] = m; - m = l(l(a << 5 | a >>> 27, 20 > f ? d & h | ~d & k : 40 > f ? d ^ - h ^ k : 60 > f ? d & h | d & k | h & k : d ^ h ^ k), l( - l(g, c[f]), 20 > f ? 1518500249 : 40 > f ? 1859775393 : - 60 > f ? -1894007588 : -899497514)); + m = l( + l( + (a << 5) | (a >>> 27), + 20 > f + ? (d & h) | (~d & k) + : 40 > f + ? d ^ h ^ k + : 60 > f + ? (d & h) | (d & k) | (h & k) + : d ^ h ^ k + ), + l( + l(g, c[f]), + 20 > f + ? 1518500249 + : 40 > f + ? 1859775393 + : 60 > f + ? -1894007588 + : -899497514 + ) + ); g = k; k = h; - h = d << 30 | d >>> 2; + h = (d << 30) | (d >>> 2); d = a; a = m; } @@ -259,19 +290,20 @@ function l(e, b) { var c = (e & 65535) + (b & 65535); - return (e >> 16) + (b >> 16) + (c >> 16) << 16 | c & 65535; + return (((e >> 16) + (b >> 16) + (c >> 16)) << 16) | (c & 65535); } function q(e) { for (var b = [], c = (1 << g) - 1, a = 0; a < e.length * g; a += g) { - b[a >> 5] |= (e.charCodeAt(a / g) & c) << 24 - a % 32; + b[a >> 5] |= (e.charCodeAt(a / g) & c) << (24 - (a % 32)); } return b; } var g = 8; - let b = `${this._oauth_consumer_secret}&${null !== this._oauth_token_secret ? - this._oauth_token_secret : ""}`; + let b = `${this._oauth_consumer_secret}&${ + null !== this._oauth_token_secret ? this._oauth_token_secret : "" + }`; if (this._oauth_consumer_secret === null) { throw "To generate a hash, the consumer secret must be set."; } @@ -288,11 +320,19 @@ bb = n(bb.concat(c), 672); b = ""; for (g = 0; g < 4 * bb.length; g += 3) { - for (d = (bb[g >> 2] >> 8 * (3 - g % 4) & 255) << 16 | (bb[g + 1 >> 2] >> - 8 * (3 - (g + 1) % 4) & 255) << 8 | bb[g + 2 >> 2] >> 8 * (3 - - (g + 2) % 4) & 255, e = 0; 4 > e; e++) { - b = 8 * g + 6 * e > 32 * bb.length ? b + "=" : b + - this.b64_alphabet.charAt(d >> 6 * (3 - e) & 63); + for ( + d = + (((bb[g >> 2] >> (8 * (3 - (g % 4)))) & 255) << 16) | + (((bb[(g + 1) >> 2] >> (8 * (3 - ((g + 1) % 4)))) & 255) << 8) | + ((bb[(g + 2) >> 2] >> (8 * (3 - ((g + 2) % 4)))) & 255), + e = 0; + 4 > e; + e++ + ) { + b = + 8 * g + 6 * e > 32 * bb.length + ? b + "=" + : b + this.b64_alphabet.charAt((d >> (6 * (3 - e))) & 63); } } return b; @@ -315,7 +355,11 @@ * @return string The base64 representation */ _base64_encode(a) { - let d, e, f, b, g = 0, + let d, + e, + f, + b, + g = 0, h = 0, i = this.b64_alphabet, c = []; @@ -326,10 +370,10 @@ d = a.charCodeAt(g++); e = a.charCodeAt(g++); f = a.charCodeAt(g++); - b = d << 16 | e << 8 | f; - d = b >> 18 & 63; - e = b >> 12 & 63; - f = b >> 6 & 63; + b = (d << 16) | (e << 8) | f; + d = (b >> 18) & 63; + e = (b >> 12) & 63; + f = (b >> 6) & 63; b &= 63; c[h++] = i.charAt(d) + i.charAt(e) + i.charAt(f) + i.charAt(b); } while (g < a.length); @@ -358,7 +402,8 @@ */ _http_build_query(e, f, b) { function g(c, a, d) { - let b, e = []; + let b, + e = []; if (a === true) { a = "1"; } else if (a === false) { @@ -381,7 +426,9 @@ return ""; } } - var d, c, h = []; + var d, + c, + h = []; if (!b) { b = "&"; } @@ -428,7 +475,9 @@ * @return array The sorted keys */ _ksort(input_arr) { - let keys = [], sorter, k; + let keys = [], + sorter, + k; sorter = (a, b) => { let a_float = parseFloat(a), @@ -465,7 +514,7 @@ _clone(obj) { let clone = {}; for (let i in obj) { - if (typeof (obj[i]) === "object") { + if (typeof obj[i] === "object") { clone[i] = this._clone(obj[i]); } else { clone[i] = obj[i]; @@ -482,20 +531,21 @@ _getXmlRequestObject() { let xml = null; // first, try the W3-standard object - if (typeof window === "object" - && window - && typeof window.XMLHttpRequest !== "undefined" - ) { + if ( + typeof window === "object" && + window && + typeof window.XMLHttpRequest !== "undefined" + ) { xml = new window.XMLHttpRequest(); // then, try Titanium framework object - } else if (typeof Ti === "object" - && Ti - && typeof Ti.Network.createHTTPClient !== "undefined" - ) { + } else if ( + typeof Ti === "object" && + Ti && + typeof Ti.Network.createHTTPClient !== "undefined" + ) { xml = Ti.Network.createHTTPClient(); // are we in an old Internet Explorer? - } else if (typeof ActiveXObject !== "undefined" - ) { + } else if (typeof ActiveXObject !== "undefined") { try { xml = new ActiveXObject("Microsoft.XMLHTTP"); } catch (e) { @@ -546,10 +596,23 @@ _parse_str(str, array) { var glue1 = "=", glue2 = "&", - array2 = String(str).replace(/^&?([\s\S]*?)&?$/, "$1").split(glue2), - i, j, chr, tmp, key, value, bracket, keys, evalStr, + array2 = String(str) + .replace(/^&?([\s\S]*?)&?$/, "$1") + .split(glue2), + i, + j, + chr, + tmp, + key, + value, + bracket, + keys, + evalStr, fixStr = str => { - return decodeURIComponent(str).replace(/([\\"'])/g, "\\$1").replace(/\n/g, "\\n").replace(/\r/g, "\\r"); + return decodeURIComponent(str) + .replace(/([\\"'])/g, "\\$1") + .replace(/\n/g, "\\n") + .replace(/\r/g, "\\r"); }; if (!array) { array = this.window; @@ -608,7 +671,10 @@ key = eval(evalStr + ".push([]);") - 1; } evalStr += `[${key}]`; - if (j !== keys.length - 1 && eval("typeof " + evalStr) === "undefined") { + if ( + j !== keys.length - 1 && + eval("typeof " + evalStr) === "undefined" + ) { eval(evalStr + " = [];"); } } @@ -796,27 +862,27 @@ let promise_class = false; try { promise_class = require("jquery"); - } catch (e) { } + } catch (e) {} if (promise_class) { return promise_class.Deferred(); } try { promise_class = require("q"); - } catch (e) { } + } catch (e) {} if (!promise_class) { try { promise_class = require("rsvp"); - } catch (e) { } + } catch (e) {} } if (!promise_class) { try { promise_class = require("when"); - } catch (e) { } + } catch (e) {} } if (promise_class) { try { return promise_class.defer(); - } catch (e) { } + } catch (e) {} } } return false; @@ -907,7 +973,6 @@ return method; } - /** * Maps called PHP magic method name to Twitter API method * @@ -918,7 +983,9 @@ */ _mapFnToApiMethod(fn, apiparams) { let method = "", - param, i, j; + param, + i, + j; // replace _ by / method = this._mapFnInsertSlashes(fn); @@ -936,7 +1003,9 @@ method_template = method_template.split(param).join(":" + param_l); if (typeof apiparams[param_l] === "undefined") { for (j = 0; j < 26; j++) { - method_template = method_template.split(String.fromCharCode(65 + j)).join("_" + String.fromCharCode(97 + j)); + method_template = method_template + .split(String.fromCharCode(65 + j)) + .join("_" + String.fromCharCode(97 + j)); } throw `To call the templated method "${method_template}", specify the parameter value for "${param_l}".`; } @@ -947,14 +1016,17 @@ // replace A-Z by _a-z for (i = 0; i < 26; i++) { - method = method.split(String.fromCharCode(65 + i)).join("_" + String.fromCharCode(97 + i)); - method_template = method_template.split(String.fromCharCode(65 + i)).join("_" + String.fromCharCode(97 + i)); + method = method + .split(String.fromCharCode(65 + i)) + .join("_" + String.fromCharCode(97 + i)); + method_template = method_template + .split(String.fromCharCode(65 + i)) + .join("_" + String.fromCharCode(97 + i)); } return [method, method_template]; } - /** * Detects HTTP method to use for API call * @@ -981,9 +1053,10 @@ const apimethods = this.getApiMethods(); for (let httpmethod in apimethods) { - if (apimethods.hasOwnProperty(httpmethod) - && apimethods[httpmethod].indexOf(method) > -1 - ) { + if ( + apimethods.hasOwnProperty(httpmethod) && + apimethods[httpmethod].indexOf(method) > -1 + ) { return httpmethod; } } @@ -999,10 +1072,10 @@ */ _detectMultipart(method) { const multiparts = [ - // Tweets + // Tweets "media/upload", - // Users + // Users "account/update_profile_image", "account/update_profile_banner" ]; @@ -1020,14 +1093,18 @@ */ _getSignature(httpmethod, method, keys, base_params) { // convert params to string - let base_string = "", key, value; + let base_string = "", + key, + value; for (let i = 0; i < keys.length; i++) { key = keys[i]; value = base_params[key]; base_string += `${key}=${this._url(value)}&`; } base_string = base_string.substring(0, base_string.length - 1); - return this._sha1(`${httpmethod}&${this._url(method)}&${this._url(base_string)}`); + return this._sha1( + `${httpmethod}&${this._url(method)}&${this._url(base_string)}` + ); } /** @@ -1077,7 +1154,12 @@ } let keys = this._ksort(sign_base_params); - const signature = this._getSignature(httpmethod, method, keys, sign_base_params); + const signature = this._getSignature( + httpmethod, + method, + keys, + sign_base_params + ); params = oauth_params; params.oauth_signature = signature; @@ -1106,19 +1188,19 @@ // only check specific parameters const possible_methods = [ - // Tweets - "media/upload", - // Accounts - "account/update_profile_image", - "account/update_profile_banner" - ]; + // Tweets + "media/upload", + // Accounts + "account/update_profile_image", + "account/update_profile_banner" + ]; let possible_files = { - // Tweets - "media/upload": "media", - // Accounts - "account/update_profile_image": "image", - "account/update_profile_banner": "banner" - }; + // Tweets + "media/upload": "media", + // Accounts + "account/update_profile_image": "image", + "account/update_profile_banner": "banner" + }; // method might have files? if (possible_methods.indexOf(method) === -1) { return; @@ -1133,8 +1215,7 @@ if (!params.hasOwnProperty(key)) { continue; } - multipart_request += - `--${multipart_border}\r\nContent-Disposition: form-data; name="${key}"`; + multipart_request += `--${multipart_border}\r\nContent-Disposition: form-data; name="${key}"`; if (possible_files.indexOf(key) === -1) { multipart_request += "\r\nContent-Transfer-Encoding: base64"; } @@ -1152,10 +1233,7 @@ * @return bool Whether the method is defined in media API */ _detectMedia(method) { - const medias = [ - "media/metadata/create", - "media/upload" - ]; + const medias = ["media/metadata/create", "media/upload"]; return medias.indexOf(method) > -1; } @@ -1221,21 +1299,33 @@ parsed = JSON.parse(reply); } catch (e) { parsed = {}; + // assume XML + if (reply.match(/^<\?xml/)) { + let errors; + if ((errors = reply.match(/(.+)<\/errors>/))) { + parsed.errors = {}; + errors = errors[1].match(/(.+)/); + parsed.errors[error[1]] = error[2]; + } + } + return parsed; + } // assume query format let elements = reply.split("&"); for (let i = 0; i < elements.length; i++) { - let element = elements[i].split("=", 2); - if (element.length > 1) { + let element = elements[i].split("=", 2); + if (element.length > 1) { parsed[element[0]] = decodeURIComponent(element[1]); - } else { + } else { parsed[element[0]] = null; - } + } } } return parsed; } - /** * Uncommon API methods */ @@ -1245,7 +1335,11 @@ * * @return object Promise */ - oauth_authenticate(params = {}, callback = undefined, type = "authenticate") { + oauth_authenticate( + params = {}, + callback = undefined, + type = "authenticate" + ) { const dfd = this._getDfd(); if (typeof params.force_login === "undefined") { params.force_login = null; @@ -1264,7 +1358,9 @@ } throw error; } - let url = `${this._endpoint_oauth}oauth/${type}?oauth_token=${this._url(this._oauth_token)}`; + let url = `${this._endpoint_oauth}oauth/${type}?oauth_token=${this._url( + this._oauth_token + )}`; if (params.force_login === true) { url += "&force_login=1"; } @@ -1316,10 +1412,7 @@ let url = this._endpoint_oauth + "oauth2/token"; if (this._use_proxy) { - url = url.replace( - this._endpoint_base, - this._endpoint_proxy - ); + url = url.replace(this._endpoint_base, this._endpoint_proxy); } const xml = this._getXmlRequestObject(); @@ -1330,19 +1423,22 @@ xml.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); xml.setRequestHeader( `${this._use_proxy ? "X-" : ""}Authorization`, - "Basic " + this._base64_encode(`${this._oauth_consumer_key}:${this._oauth_consumer_secret}`) - ); + "Basic " + + this._base64_encode( + `${this._oauth_consumer_key}:${this._oauth_consumer_secret}` + ) + ); xml.onreadystatechange = () => { if (xml.readyState >= 4) { let httpstatus = 12027; try { httpstatus = xml.status; - } catch (e) { } + } catch (e) {} let response = ""; try { response = xml.responseText; - } catch (e) { } + } catch (e) {} let reply = this._parseApiReply(response); reply.httpstatus = httpstatus; if (httpstatus === 200) { @@ -1386,16 +1482,16 @@ * @return mixed The API reply, encoded in the set return_format */ _callApi( - httpmethod, - method, - params = {}, - multipart = false, - app_only_auth = false, - callback = () => {} - ) { + httpmethod, + method, + params = {}, + multipart = false, + app_only_auth = false, + callback = () => {} + ) { const dfd = this._getDfd(); - let url = this._getEndpoint(method), + let url = this._getEndpoint(method), authorization = null; const xml = this._getXmlRequestObject(); @@ -1414,13 +1510,9 @@ } if (this._use_proxy) { - url_with_params = url_with_params.replace( - this._endpoint_base, - this._endpoint_proxy - ).replace( - this._endpoint_base_media, - this._endpoint_proxy - ); + url_with_params = url_with_params + .replace(this._endpoint_base, this._endpoint_proxy) + .replace(this._endpoint_base_media, this._endpoint_proxy); } xml.open(httpmethod, url_with_params, true); } else { @@ -1439,30 +1531,35 @@ params = this._http_build_query(params); } post_fields = params; - if (this._use_proxy || multipart) { // force proxy for multipart base64 - url = url.replace( - this._endpoint_base, - this._endpoint_proxy - ).replace( - this._endpoint_base_media, - this._endpoint_proxy - ); + if (this._use_proxy || multipart) { + // force proxy for multipart base64 + url = url + .replace(this._endpoint_base, this._endpoint_proxy) + .replace(this._endpoint_base_media, this._endpoint_proxy); } xml.open(httpmethod, url, true); if (multipart) { - xml.setRequestHeader("Content-Type", "multipart/form-data; boundary=" - + post_fields.split("\r\n")[0].substring(2)); + xml.setRequestHeader( + "Content-Type", + "multipart/form-data; boundary=" + + post_fields.split("\r\n")[0].substring(2) + ); } else if (this._detectJsonBody(method)) { xml.setRequestHeader("Content-Type", "application/json"); } else { - xml.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); + xml.setRequestHeader( + "Content-Type", + "application/x-www-form-urlencoded" + ); } } if (app_only_auth) { - if (this._oauth_consumer_key === null - && this._oauth_bearer_token === null - ) { - const error = "To make an app-only auth API request, consumer key or bearer token must be set."; + if ( + this._oauth_consumer_key === null && + this._oauth_bearer_token === null + ) { + const error = + "To make an app-only auth API request, consumer key or bearer token must be set."; if (dfd) { dfd.reject({ error }); return this._getPromise(dfd); @@ -1473,35 +1570,53 @@ if (this._oauth_bearer_token === null) { if (dfd) { return this.oauth2_token().then(() => { - return this._callApi(httpmethod, method, params, multipart, app_only_auth, callback); + return this._callApi( + httpmethod, + method, + params, + multipart, + app_only_auth, + callback + ); }); } this.oauth2_token(() => { - this._callApi(httpmethod, method, params, multipart, app_only_auth, callback); + this._callApi( + httpmethod, + method, + params, + multipart, + app_only_auth, + callback + ); }); return; } authorization = "Bearer " + this._oauth_bearer_token; } if (authorization !== null) { - xml.setRequestHeader(`${this._use_proxy ? "X-" : ""}Authorization`, authorization); + xml.setRequestHeader( + `${this._use_proxy ? "X-" : ""}Authorization`, + authorization + ); } xml.onreadystatechange = () => { if (xml.readyState >= 4) { let httpstatus = 12027; try { httpstatus = xml.status; - } catch (e) { } + } catch (e) {} let response = ""; try { response = xml.responseText; - } catch (e) { } + } catch (e) {} let reply = this._parseApiReply(response); reply.httpstatus = httpstatus; let rate = null; - if (typeof xml.getResponseHeader !== "undefined" - && xml.getResponseHeader("x-rate-limit-limit") !== "" - ) { + if ( + typeof xml.getResponseHeader !== "undefined" && + xml.getResponseHeader("x-rate-limit-limit") !== "" + ) { rate = { limit: xml.getResponseHeader("x-rate-limit-limit"), remaining: xml.getResponseHeader("x-rate-limit-remaining"), @@ -1576,24 +1691,25 @@ // map function name to API method const [method, method_template] = this._mapFnToApiMethod(fn, apiparams), - httpmethod = this._detectMethod(method_template, apiparams), - multipart = this._detectMultipart(method_template); + httpmethod = this._detectMethod(method_template, apiparams), + multipart = this._detectMultipart(method_template); return this._callApi( - httpmethod, - method, - apiparams, - multipart, - app_only_auth, - callback - ); + httpmethod, + method, + apiparams, + multipart, + app_only_auth, + callback + ); } - }; + } - if (typeof module === "object" - && module - && typeof module.exports === "object" - ) { + if ( + typeof module === "object" && + module && + typeof module.exports === "object" + ) { // Expose codebird as module.exports in loaders that implement the Node // module pattern (including browserify). Do not create the global, since // the user will be storing it themselves locally, and globals are frowned @@ -1601,8 +1717,7 @@ module.exports = Codebird; } else { // Otherwise expose codebird to the global object as usual - if (typeof window === "object" - && window) { + if (typeof window === "object" && window) { window.Codebird = Codebird; } @@ -1617,5 +1732,4 @@ define("codebird", [], () => Codebird); } } - })(); diff --git a/codebird.js b/codebird.js index ce4b7d9..81a65d2 100644 --- a/codebird.js +++ b/codebird.js @@ -1184,10 +1184,23 @@ function _classCallCheck(instance, Constructor) { if (!(instance instanceof Cons parsed = JSON.parse(reply); } catch (e) { parsed = {}; + // assume XML + if (reply.match(/^<\?xml/)) { + var errors = void 0; + if (errors = reply.match(/(.+)<\/errors>/)) { + parsed.errors = {}; + errors = errors[1].match(/(.+)/); + parsed.errors[error[1]] = error[2]; + } + } + return parsed; + } // assume query format var elements = reply.split("&"); - for (var i = 0; i < elements.length; i++) { - var element = elements[i].split("=", 2); + for (var _i = 0; _i < elements.length; _i++) { + var element = elements[_i].split("=", 2); if (element.length > 1) { parsed[element[0]] = decodeURIComponent(element[1]); } else { @@ -1556,8 +1569,6 @@ function _classCallCheck(instance, Constructor) { if (!(instance instanceof Cons return Codebird; }(); - ; - if ((typeof module === "undefined" ? "undefined" : _typeof(module)) === "object" && module && _typeof(module.exports) === "object") { // Expose codebird as module.exports in loaders that implement the Node // module pattern (including browserify). Do not create the global, since From fbd60ee585ad0ac68cdd96bde72cd41e400f1aec Mon Sep 17 00:00:00 2001 From: "J.M" Date: Sat, 15 Sep 2018 13:45:58 +0200 Subject: [PATCH 6/8] Update package.json --- package-lock.json | 203 +++++++++++++++++++++++----------------------- package.json | 2 +- 2 files changed, 102 insertions(+), 103 deletions(-) diff --git a/package-lock.json b/package-lock.json index f4adf61..03ee1e7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,10 +4,38 @@ "lockfileVersion": 1, "requires": true, "dependencies": { + "@babel/code-frame": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0.tgz", + "integrity": "sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA==", + "dev": true, + "requires": { + "@babel/highlight": "^7.0.0" + } + }, + "@babel/highlight": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0.tgz", + "integrity": "sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw==", + "dev": true, + "requires": { + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^4.0.0" + }, + "dependencies": { + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + } + } + }, "acorn": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.1.tgz", - "integrity": "sha512-d+nbxBUGKg7Arpsvbnlq61mc12ek3EY8EQldM3GPAhWJ1UVxC6TDGbIvUMNU6obBX3i1+ptCIzV4vq0gFPEGVQ==", + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz", + "integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==", "dev": true }, "acorn-jsx": { @@ -20,15 +48,15 @@ } }, "ajv": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.2.tgz", - "integrity": "sha512-hOs7GfvI6tUI1LfZddH82ky6mOMyTuY0mk7kE2pWpmhhUSkumzaTO5vbVwij39MdwPQWCV4Zv57Eo06NtL/GVA==", + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.3.tgz", + "integrity": "sha512-LqZ9wY+fx3UMiiPd741yB2pj3hhil+hQc8taf4o2QGRFpWgZ2V5C8HA165DY9sS3fJwsk7uT7ZlFEyC3Ig3lLg==", "dev": true, "requires": { "fast-deep-equal": "^2.0.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.1" + "uri-js": "^4.2.2" } }, "ajv-keywords": { @@ -1023,9 +1051,9 @@ "dev": true }, "supports-color": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", - "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, "requires": { "has-flag": "^3.0.0" @@ -1034,9 +1062,9 @@ } }, "chardet": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz", - "integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=", + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", "dev": true }, "chokidar": { @@ -1079,18 +1107,18 @@ "dev": true }, "color-convert": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.2.tgz", - "integrity": "sha512-3NUJZdhMhcdPn8vJ9v2UQJoH0qqoGUkYTgFEPZaPjEtwmmKUfNV46zZmgB2M5M4DCEQHMaCfWHCxiBflLm04Tg==", + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "dev": true, "requires": { - "color-name": "1.1.1" + "color-name": "1.1.3" } }, "color-name": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.1.tgz", - "integrity": "sha1-SxQVMEz1ACjqgWQ2Q72C6gWANok=", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", "dev": true }, "commander": { @@ -1131,12 +1159,12 @@ } }, "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.5.tgz", + "integrity": "sha512-D61LaDQPQkxJ5AUM2mbSJRbPkNs/TmdmOeLAi1hgDkpDfIfetSrjmWhccwtuResSwMbACjx/xXQofvM9CE/aeg==", "dev": true, "requires": { - "ms": "2.0.0" + "ms": "^2.1.1" } }, "deep-equal": { @@ -1236,13 +1264,13 @@ "dev": true }, "eslint": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.3.0.tgz", - "integrity": "sha512-N/tCqlMKkyNvAvLu+zI9AqDasnSLt00K+Hu8kdsERliC9jYEc8ck12XtjvOXrBKu8fK6RrBcN9bat6Xk++9jAg==", + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.6.0.tgz", + "integrity": "sha512-/eVYs9VVVboX286mBK7bbKnO1yamUy2UCRjiY6MryhQL2PaaXCExsCQ2aO83OeYRhU2eCU/FMFP+tVMoOrzNrA==", "dev": true, "requires": { - "ajv": "^6.5.0", - "babel-code-frame": "^6.26.0", + "@babel/code-frame": "^7.0.0", + "ajv": "^6.5.3", "chalk": "^2.1.0", "cross-spawn": "^6.0.5", "debug": "^3.1.0", @@ -1257,11 +1285,11 @@ "functional-red-black-tree": "^1.0.1", "glob": "^7.1.2", "globals": "^11.7.0", - "ignore": "^4.0.2", + "ignore": "^4.0.6", "imurmurhash": "^0.1.4", - "inquirer": "^5.2.0", + "inquirer": "^6.1.0", "is-resolvable": "^1.1.0", - "js-yaml": "^3.11.0", + "js-yaml": "^3.12.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.3.0", "lodash": "^4.17.5", @@ -1274,8 +1302,7 @@ "progress": "^2.0.0", "regexpp": "^2.0.0", "require-uncached": "^1.0.3", - "semver": "^5.5.0", - "string.prototype.matchall": "^2.0.0", + "semver": "^5.5.1", "strip-ansi": "^4.0.0", "strip-json-comments": "^2.0.1", "table": "^4.0.3", @@ -1388,13 +1415,13 @@ } }, "external-editor": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz", - "integrity": "sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.0.3.tgz", + "integrity": "sha512-bn71H9+qWoOQKyZDo25mOMVpSmXROAsTJVVVYzrrtol3d4y+AsKjf4Iwl2Q+IuT0kFSQ1qo166UuIwqYq7mGnA==", "dev": true, "requires": { - "chardet": "^0.4.0", - "iconv-lite": "^0.4.17", + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", "tmp": "^0.0.33" } }, @@ -2115,9 +2142,9 @@ "dev": true }, "glob": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", "dev": true, "requires": { "fs.realpath": "^1.0.0", @@ -2198,12 +2225,6 @@ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", "dev": true }, - "has-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", - "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=", - "dev": true - }, "home-or-tmp": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz", @@ -2215,9 +2236,9 @@ } }, "iconv-lite": { - "version": "0.4.23", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", - "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "dev": true, "requires": { "safer-buffer": ">= 2.1.2 < 3" @@ -2252,21 +2273,21 @@ "dev": true }, "inquirer": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-5.2.0.tgz", - "integrity": "sha512-E9BmnJbAKLPGonz0HeWHtbKf+EeSP93paWO3ZYoUpq/aowXvYGjjCSuashhXPpzbArIjBbji39THkxTz9ZeEUQ==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.2.0.tgz", + "integrity": "sha512-QIEQG4YyQ2UYZGDC4srMZ7BjHOmNk1lR2JQj5UknBapklm6WHA+VVH7N+sUdX3A7NeCfGF8o4X1S3Ao7nAcIeg==", "dev": true, "requires": { "ansi-escapes": "^3.0.0", "chalk": "^2.0.0", "cli-cursor": "^2.1.0", "cli-width": "^2.0.0", - "external-editor": "^2.1.0", + "external-editor": "^3.0.0", "figures": "^2.0.0", - "lodash": "^4.3.0", + "lodash": "^4.17.10", "mute-stream": "0.0.7", "run-async": "^2.2.0", - "rxjs": "^5.5.2", + "rxjs": "^6.1.0", "string-width": "^2.1.0", "strip-ansi": "^4.0.0", "through": "^2.3.6" @@ -2556,9 +2577,9 @@ } }, "lodash": { - "version": "4.17.10", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", - "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==", + "version": "4.17.11", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", + "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==", "dev": true }, "loose-envify": { @@ -2649,9 +2670,9 @@ } }, "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", "dev": true }, "mute-stream": { @@ -2674,9 +2695,9 @@ "dev": true }, "nice-try": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.4.tgz", - "integrity": "sha512-2NpiFHqC87y/zFke0fC0spBXL3bBsoh/p5H1EFhshxjCR5+0g2d6BiXbUFz9v1sAcxsk2htp2eQnNIci2dIYcA==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", "dev": true }, "normalize-path": { @@ -3016,15 +3037,6 @@ "is-equal-shallow": "^0.1.3" } }, - "regexp.prototype.flags": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.2.0.tgz", - "integrity": "sha512-ztaw4M1VqgMwl9HlPpOuiYgItcHlunW0He2fE6eNfT6E/CF2FtYi9ofOYe4mKntstYk0Fyh/rDRBdS3AnxjlrA==", - "dev": true, - "requires": { - "define-properties": "^1.1.2" - } - }, "regexpp": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.0.tgz", @@ -3167,12 +3179,12 @@ } }, "rxjs": { - "version": "5.5.11", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.5.11.tgz", - "integrity": "sha512-3bjO7UwWfA2CV7lmwYMBzj4fQ6Cq+ftHc2MvUe+WMS7wcdJ1LosDWmdjPQanYp2dBRj572p7PeU81JUxHKOcBA==", + "version": "6.3.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.3.2.tgz", + "integrity": "sha512-hV7criqbR0pe7EeL3O66UYVg92IR0XsA97+9y+BWTePK9SKmEI5Qd3Zj6uPnGkNzXsBywBQWTvujPl+1Kn9Zjw==", "dev": true, "requires": { - "symbol-observable": "1.0.1" + "tslib": "^1.9.0" } }, "safer-buffer": { @@ -3182,9 +3194,9 @@ "dev": true }, "semver": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", - "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.1.tgz", + "integrity": "sha512-PqpAxfrEhlSUWge8dwIp4tZnQ25DIOthpiaHNIthsjEFQD6EvqUKUDM7L8O2rShkFccYo1VjJR0coWfNkCubRw==", "dev": true }, "set-immediate-shim": { @@ -3286,19 +3298,6 @@ } } }, - "string.prototype.matchall": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-2.0.0.tgz", - "integrity": "sha512-WoZ+B2ypng1dp4iFLF2kmZlwwlE19gmjgKuhL1FJfDgCREWb3ye3SDVHSzLH6bxfnvYmkCxbzkmWcQZHA4P//Q==", - "dev": true, - "requires": { - "define-properties": "^1.1.2", - "es-abstract": "^1.10.0", - "function-bind": "^1.1.1", - "has-symbols": "^1.0.0", - "regexp.prototype.flags": "^1.2.0" - } - }, "string.prototype.trim": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.1.2.tgz", @@ -3337,15 +3336,9 @@ "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", "dev": true }, - "symbol-observable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.0.1.tgz", - "integrity": "sha1-g0D8RwLDEi310iKI+IKD9RPT/dQ=", - "dev": true - }, "table": { "version": "4.0.3", - "resolved": "https://registry.npmjs.org/table/-/table-4.0.3.tgz", + "resolved": "http://registry.npmjs.org/table/-/table-4.0.3.tgz", "integrity": "sha512-S7rnFITmBH1EnyKcvxBh1LjYeQMmnZtCXSEbHcH6S0NoKit24ZuFO/T1vDcLdYsLQkM188PVVhQmzKIuThNkKg==", "dev": true, "requires": { @@ -3536,6 +3529,12 @@ "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=", "dev": true }, + "tslib": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", + "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==", + "dev": true + }, "type-check": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", diff --git a/package.json b/package.json index b2a6cb0..ceef837 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,7 @@ "devDependencies": { "babel-cli": "^6.26.0", "babel-preset-es2015": "^6.24.1", - "eslint": "^5.3.0", + "eslint": "^5.6.0", "faucet": "0.0.1", "q": "^1.5.1", "tape": "^4.9.1", From e88ff5464cbb08f3f0e07421dd3de3e3cf535866 Mon Sep 17 00:00:00 2001 From: "J.M" Date: Sat, 15 Sep 2018 13:51:28 +0200 Subject: [PATCH 7/8] Drop nodeJS 0.10, 0.12 CI --- .travis.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 44e72cd..45bebde 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,9 +5,6 @@ language: node_js node_js: - - "0.10" - - "0.12" - - iojs - "8" - "9" - "10" From 77e6fbc28cdf8d4c4de144ebd0de68239af573a2 Mon Sep 17 00:00:00 2001 From: "J.M" Date: Sat, 15 Sep 2018 14:20:03 +0200 Subject: [PATCH 8/8] Update README --- CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG b/CHANGELOG index b492cbf..a085435 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -20,6 +20,7 @@ codebird-js - changelog - Remove contributor API methods - Remove deprecated statuses/update_with_media method - Remove deprecated statuses/update_profile_background_image method ++ Detect XML error replies (re-implemented detection) 2.6.0 (2015-04-08) + Allow to get the supported API methods as array