From 05d42467cf52f5136d06e46a5721d3241ebb95aa Mon Sep 17 00:00:00 2001 From: Jonny Barnes Date: Thu, 18 May 2017 15:15:53 +0100 Subject: [PATCH] =?UTF-8?q?Micropub=20Update=20and=20=E2=80=9CSwarm?= =?UTF-8?q?=E2=80=9D=20Support?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Much better micropub client/endpoint and the ability to support ownyourswarm. Squashed commit of the following: commit b5be117b852b7a598da72325a4eaf831526c700a Author: Jonny Barnes Date: Thu May 18 15:02:00 2017 +0100 Add a token endpoint test commit 71b7ff68d088180770ca8c2fb43e33bf4b385a96 Author: Jonny Barnes Date: Thu May 18 13:49:00 2017 +0100 Fix phpcs issue commit 54d96d32f280127061254e7bc6dfe196e2e35a39 Author: Jonny Barnes Date: Thu May 18 13:47:08 2017 +0100 Move token code into its own controller commit 3ed2b4d36d57a9b3bf2a532eb262ec71fc9aa046 Author: Jonny Barnes Date: Thu May 18 13:01:09 2017 +0100 Improve/fix code for issuing a token commit 8c411a1df1d59f12dd1c1a4ac884f53dbd1b9351 Author: Jonny Barnes Date: Thu May 18 12:59:33 2017 +0100 Remove sprurious comment commit 1b8a3b6502a2b982f737fb4b58602995451e38b9 Author: Jonny Barnes Date: Mon May 15 11:57:03 2017 +0100 Wrap helper functions in check for existence commit 75c706b5a6c1fca7bf45038409689416a3b5ba4d Author: Jonny Barnes Date: Mon May 15 11:40:17 2017 +0100 Fix a test error, array_key_exists must be run on an array commit 685e96501b8dc906c6945fca39721fb79fa34a8b Author: Jonny Barnes Date: Mon May 15 11:11:51 2017 +0100 Remove errant dd() commit 02536ebaa6daec2a78bc79c44392ac5a82c3200e Author: Jonny Barnes Date: Sun May 14 21:37:17 2017 +0100 Test a typical ownyourswarm request commit 31d959e35ccec9dac5986f93732d928c08e246c8 Author: Jonny Barnes Date: Sun May 14 21:36:37 2017 +0100 Better tagging of notes commit e3d4ff8b050ba41febed4d3ab0d30898f0056b33 Author: Jonny Barnes Date: Sun May 14 21:35:35 2017 +0100 When a note has a “checkin” try and create the place if it doesn’t already exist commit 4fc0dd0121ca09915a13f3c21c97611db1d744a6 Author: Jonny Barnes Date: Fri May 12 12:26:25 2017 +0100 better handling of exceptions, `return` the exctacted response method commit 2e3ca3297d2f494eb88af732519bd7ea8bcc3611 Author: Jonny Barnes Date: Fri May 12 11:22:52 2017 +0100 compser update and minor style tweak commit b883d03cc349798230986a5cb50e23e370ce5a09 Author: Jonny Barnes Date: Thu May 11 19:03:52 2017 +0100 Improve artisan tinker; see https://blog.tighten.co/supercharge-your-laravel-tinker-workflow commit 8de63172fc7d367870624ff25d1ada92af2d61a7 Author: Jonny Barnes Date: Thu May 11 17:28:16 2017 +0100 Tweak code, make it slightly more readable by catching custom exception, and moving a repeated response into its own function commit 8ff0a1196d254d8788477d26b548f2ecff0a7986 Author: Jonny Barnes Date: Thu May 11 17:21:39 2017 +0100 Add a custom exception to catch commit 3a568da65ef22b1b676ea8378cd51ce88750b6af Author: Jonny Barnes Date: Thu May 11 17:21:11 2017 +0100 composer update && yarn upgrade commit b70242e616827eab6a2132f3e691ec91be689fb5 Author: Jonny Barnes Date: Wed May 10 20:00:03 2017 +0100 modify some returns for style purposes commit b65170ba1515cbb07beb66fcb3358d69d86cf3a2 Author: Jonny Barnes Date: Wed May 10 19:39:46 2017 +0100 composer update commit cb6198db03a8e8c5c365e88d565401dd6420be13 Author: Jonny Barnes Date: Wed May 10 17:07:33 2017 +0100 composer update commit 91cdd9e17ba192b833da76e0243829cd037170f3 Author: Jonny Barnes Date: Fri Apr 28 21:41:12 2017 +0100 verify input array contains necessary data commit 6b230278bfcf2707b7ea1af8e15acd0d7cd27623 Author: Jonny Barnes Date: Fri Apr 28 21:40:30 2017 +0100 pass the right input content to the data array commit 96f30d25810751328f75964e81899496add7292e Author: Jonny Barnes Date: Fri Apr 28 21:19:28 2017 +0100 Add support for the published property commit 8168a14969711ff5f84d29eca73036980f9b5a6b Author: Jonny Barnes Date: Fri Apr 28 18:48:23 2017 +0100 Fix some bugs in the `normalize_url()` function and add some tests for the helpers functions commit 34adcebefa7cafec8d26d438b0046120751780be Author: Jonny Barnes Date: Fri Apr 28 15:12:08 2017 +0100 Fix PSR-2 issues, exluding group use declarations commit 4b6651c318d534db1fcb83e1b66562c6dd560165 Author: Jonny Barnes Date: Fri Apr 28 12:36:54 2017 +0100 composer update commit a0788ffb6bd4d24245986bf83fe349b8e39786cd Author: Jonny Barnes Date: Wed Apr 26 12:44:06 2017 +0100 Make the retreived a Note a model instance instead of collection, update the content of JSON resposes for updating a note commit f444cfd570a8316a8bb961b901ca2beb3ba74cd2 Author: Jonny Barnes Date: Wed Apr 26 12:43:23 2017 +0100 Add tests for updating a post commit ada7e513263b1b0519600538a8a2cb757c74d520 Author: Jonny Barnes Date: Wed Apr 26 12:42:50 2017 +0100 Add swarm_url column commit f4e1dba1b315b3d923049f9f5c7a47a730267cb7 Merge: 7208ec5 400857a Author: Jonny Barnes Date: Tue Apr 25 15:33:28 2017 +0100 Merge pull request #50 from jonnybarnes/analysis-XNDLxp Apply fixes from StyleCI commit 400857af57f873bf63b452fdf65ed2632eef9311 Author: Jonny Barnes Date: Tue Apr 25 14:33:06 2017 +0000 Apply fixes from StyleCI commit 7208ec53ff51f0c5d002c222c465767c46c275b2 Author: Jonny Barnes Date: Tue Apr 25 15:30:02 2017 +0100 Correct some tests, for example the error returned for the specific mistake in `getInvalidToken()` has changed commit 60550667156d7306cf750768b89fa329742c3927 Author: Jonny Barnes Date: Tue Apr 25 15:29:06 2017 +0100 Fix some typos commit 0324cb010e77606e0f99e8bb68376b79995abffc Author: Jonny Barnes Date: Tue Apr 25 15:20:27 2017 +0100 Allow notes to be updated by a micropub client endpoint commit b3b3170b359548d21ddae9a9572c182d2a7d51be Author: Jonny Barnes Date: Tue Apr 25 15:18:48 2017 +0100 When a note with a photo is being created, adjust for if URL is on my own endpoint, or somehwere else commit a2437879b000728a2e7d2b91fa642f7cdfd1e698 Author: Jonny Barnes Date: Tue Apr 25 15:17:25 2017 +0100 Modify method that returns URL to check if path is already a fully qualified URL commit 64eb53e0f87cb5ee55013de5ed8e2487eee36f0c Author: Jonny Barnes Date: Tue Apr 25 15:16:12 2017 +0100 Add a method to find notes based on NewBase60 id --- .env.example | 2 + app/Exceptions/InvalidTokenException.php | 13 + app/Http/Controllers/IndieAuthController.php | 85 +--- app/Http/Controllers/MicropubController.php | 374 +++++++++++------- .../Controllers/TokenEndpointController.php | 79 ++++ app/Media.php | 4 + app/Note.php | 14 + app/Providers/AppServiceProvider.php | 2 +- app/Services/NoteService.php | 31 +- app/Services/PlaceService.php | 35 ++ app/Services/TokenService.php | 13 +- app/helpers.php | 191 --------- composer.json | 2 +- composer.lock | 247 ++++++------ ...03734_update_notes_table_add_swarm_url.php | 32 ++ ...ate_places_table_add_foursquare_column.php | 32 ++ helpers.php | 199 ++++++++++ package.json | 6 +- routes/web.php | 12 +- tests/Feature/MicropubControllerTest.php | 45 ++- tests/Feature/SwarmTest.php | 69 ++++ tests/Feature/TokenEndpointTest.php | 38 ++ tests/Unit/HelpersTest.php | 69 ++++ tinker.config.php | 38 ++ yarn.lock | 349 +++++++++------- 25 files changed, 1283 insertions(+), 698 deletions(-) create mode 100644 app/Exceptions/InvalidTokenException.php create mode 100644 app/Http/Controllers/TokenEndpointController.php delete mode 100644 app/helpers.php create mode 100644 database/migrations/2017_04_25_203734_update_notes_table_add_swarm_url.php create mode 100644 database/migrations/2017_05_12_135451_update_places_table_add_foursquare_column.php create mode 100644 helpers.php create mode 100644 tests/Feature/SwarmTest.php create mode 100644 tests/Feature/TokenEndpointTest.php create mode 100644 tests/Unit/HelpersTest.php create mode 100644 tinker.config.php diff --git a/.env.example b/.env.example index 57ea1e36..72c19df3 100644 --- a/.env.example +++ b/.env.example @@ -52,3 +52,5 @@ TWITTER_ACCESS_TOKEN_SECRET= SCOUT_DRIVER=pgsql PIWIK=false + +PSYSH_CONFIG=tinker.config.php diff --git a/app/Exceptions/InvalidTokenException.php b/app/Exceptions/InvalidTokenException.php new file mode 100644 index 00000000..8184cfa7 --- /dev/null +++ b/app/Exceptions/InvalidTokenException.php @@ -0,0 +1,13 @@ +client = $client ?? new Client(); - $this->tokenService = $tokenService ?? new TokenService(); } /** @@ -90,37 +80,6 @@ class IndieAuthController extends Controller return redirect(route('micropub-client')); } -/* - $tokenEndpoint = $this->indieAuthService->getTokenEndpoint($request->input('me')); - if ($tokenEndpoint === false) { - return redirect(route('micropub-client'))->with( - 'error', - 'Unable to determine token endpoint' - ); - } - $data = [ - 'endpoint' => $tokenEndpoint, - 'code' => $request->input('code'), - 'me' => $request->input('me'), - 'redirect_url' => route('indieauth-callback'), - 'client_id' => route('micropub-client'), - 'state' => $request->input('state'), - ]; - $token = $this->indieAuthService->getAccessToken($data); - - if (array_key_exists('access_token', $token)) { - $request->session()->put('me', $token['me']); - $request->session()->put('token', $token['access_token']); - - return redirect(route('micropub-client')); - } - - return redirect(route('micropub-client'))->with( - 'error', - 'Unable to get a token from the endpoint' - ); -*/ - /** * Log out the user, flush the session data. * @@ -132,42 +91,4 @@ class IndieAuthController extends Controller return redirect(route('micropub-client')); } - - /** - * If the user has auth’d via IndieAuth, issue a valid token. - * - * @param \Illuminate\Http\Request $request - * @return \Illuminate\Http\Response - */ - public function tokenEndpoint(Request $request) - { - $authData = [ - 'code' => $request->input('code'), - 'me' => $request->input('me'), - 'redirect_url' => $request->input('redirect_uri'), - 'client_id' => $request->input('client_id'), - 'state' => $request->input('state'), - ]; - $auth = $this->indieAuthService->verifyIndieAuthCode($authData); - if (array_key_exists('me', $auth)) { - $scope = $auth['scope'] ?? ''; - $tokenData = [ - 'me' => $request->input('me'), - 'client_id' => $request->input('client_id'), - 'scope' => $auth['scope'], - ]; - $token = $this->tokenService->getNewToken($tokenData); - $content = http_build_query([ - 'me' => $request->input('me'), - 'scope' => $scope, - 'access_token' => $token, - ]); - - return response($content) - ->header('Content-Type', 'application/x-www-form-urlencoded'); - } - $content = 'There was an error verifying the authorisation code.'; - - return response($content, 400); - } } diff --git a/app/Http/Controllers/MicropubController.php b/app/Http/Controllers/MicropubController.php index 65ddb982..2aba67cc 100644 --- a/app/Http/Controllers/MicropubController.php +++ b/app/Http/Controllers/MicropubController.php @@ -3,8 +3,9 @@ namespace App\Http\Controllers; use Ramsey\Uuid\Uuid; -use App\{Media, Place}; +use App\{Media, Note, Place}; use Illuminate\Http\{Request, Response}; +use App\Exceptions\InvalidTokenException; use Ramsey\Uuid\Exception\UnsatisfiedDependencyException; use App\Services\{NoteService, PlaceService, TokenService}; @@ -49,7 +50,7 @@ class MicropubController extends Controller { try { $tokenData = $this->tokenService->validateToken($request->bearerToken()); - } catch (\Exception $e) { + } catch (InvalidTokenException $e) { return response()->json([ 'response' => 'error', 'error' => 'invalid_token', @@ -57,106 +58,203 @@ class MicropubController extends Controller ], 400); } if ($tokenData->hasClaim('scope')) { - $scopes = explode(' ', $tokenData->getClaim('scope')); - if (array_search('create', $scopes) !== false) { - $clientId = $tokenData->getClaim('client_id'); - if (($request->input('h') == 'entry') || ($request->input('type')[0] == 'h-entry')) { - $data = []; - $data['client-id'] = $clientId; - if ($request->header('Content-Type') == 'application/json') { - $data['content'] = $request->input('properties.content')[0]; - $data['in-reply-to'] = $request->input('properties.in-reply-to')[0]; - $data['location'] = $request->input('properties.location'); - //flatten location if array - if (is_array($data['location'])) { - $data['location'] = $data['location'][0]; - } - } else { - $data['content'] = $request->input('content'); - $data['in-reply-to'] = $request->input('in-reply-to'); - $data['location'] = $request->input('location'); - } - $data['syndicate'] = []; - $targets = array_pluck(config('syndication.targets'), 'uid', 'service.name'); - if (is_string($request->input('mp-syndicate-to'))) { - $service = array_search($request->input('mp-syndicate-to')); - if ($service == 'Twitter') { - $data['syndicate'][] = 'twitter'; - } - if ($service == 'Facebook') { - $data['syndicate'][] = 'facebook'; - } - } - if (is_array($request->input('mp-syndicate-to'))) { - foreach ($targets as $service => $target) { - if (in_array($target, $request->input('mp-syndicate-to'))) { - if ($service == 'Twitter') { - $data['syndicate'][] = 'twitter'; - } - if ($service == 'Facebook') { - $data['syndicate'][] = 'facebook'; - } - } - } - } - $data['photo'] = []; - if (is_array($request->input('photo'))) { - foreach ($request->input('photo') as $photo) { - if (is_string($photo)) { - //only supporting media URLs for now - $data['photo'][] = $photo; - } - } - } - try { - $note = $this->noteService->createNote($data); - } catch (Exception $exception) { - return response()->json(['error' => true], 400); - } - - return response()->json([ - 'response' => 'created', - 'location' => $note->longurl, - ], 201)->header('Location', $note->longurl); + if (($request->input('h') == 'entry') || ($request->input('type')[0] == 'h-entry')) { + if (stristr($tokenData->getClaim('scope'), 'create') === false) { + return $this->returnInsufficientScopeResponse(); } - if ($request->input('h') == 'card' || $request->input('type')[0] == 'h-card') { - $data = []; - if ($request->header('Content-Type') == 'application/json') { - $data['name'] = $request->input('properties.name'); - $data['description'] = $request->input('properties.description') ?? null; - if ($request->has('properties.geo')) { - $data['geo'] = $request->input('properties.geo'); - } - } else { - $data['name'] = $request->input('name'); - $data['description'] = $request->input('description'); - if ($request->has('geo')) { - $data['geo'] = $request->input('geo'); - } - if ($request->has('latitude')) { - $data['latitude'] = $request->input('latitude'); - $data['longitude'] = $request->input('longitude'); + $data = []; + $data['client-id'] = $tokenData->getClaim('client_id'); + if ($request->header('Content-Type') == 'application/json') { + if (is_string($request->input('properties.content')[0])) { + $data['content'] = $request->input('properties.content')[0]; //plaintext content + } + if (is_array($request->input('properties.content')[0]) + && array_key_exists('html', $request->input('properties.content')[0]) + ) { + $data['content'] = $request->input('properties.content')[0]['html']; + } + $data['in-reply-to'] = $request->input('properties.in-reply-to')[0]; + $data['location'] = $request->input('properties.location'); + //flatten location if array + if (is_array($data['location'])) { + $data['location'] = $data['location'][0]; + } + $data['published'] = $request->input('properties.published')[0]; + //create checkin place + if (array_key_exists('checkin', $request->input('properties'))) { + $data['checkin'] = $request->input('properties.checkin.0.properties.url.0'); + try { + $this->placeService->createPlaceFromCheckin($request->input('properties.checkin.0')); + } catch (\Exception $e) { + $data['checkin'] = null; } } - try { - $place = $this->placeService->createPlace($data); - } catch (Exception $exception) { - return response()->json(['error' => true], 400); + } else { + $data['content'] = $request->input('content'); + $data['in-reply-to'] = $request->input('in-reply-to'); + $data['location'] = $request->input('location'); + $data['published'] = $request->input('published'); + } + $data['syndicate'] = []; + $targets = array_pluck(config('syndication.targets'), 'uid', 'service.name'); + if (is_string($request->input('mp-syndicate-to'))) { + $service = array_search($request->input('mp-syndicate-to')); + if ($service == 'Twitter') { + $data['syndicate'][] = 'twitter'; } + if ($service == 'Facebook') { + $data['syndicate'][] = 'facebook'; + } + } + if (is_array($request->input('mp-syndicate-to'))) { + foreach ($targets as $service => $target) { + if (in_array($target, $request->input('mp-syndicate-to'))) { + if ($service == 'Twitter') { + $data['syndicate'][] = 'twitter'; + } + if ($service == 'Facebook') { + $data['syndicate'][] = 'facebook'; + } + } + } + } + $data['photo'] = []; + if (is_array($request->input('photo'))) { + foreach ($request->input('photo') as $photo) { + if (is_string($photo)) { + //only supporting media URLs for now + $data['photo'][] = $photo; + } + } + } + try { + $note = $this->noteService->createNote($data); + } catch (\Exception $exception) { + return response()->json(['error' => true], 400); + } - return response()->json([ - 'response' => 'created', - 'location' => $place->longurl, - ], 201)->header('Location', $place->longurl); + return response()->json([ + 'response' => 'created', + 'location' => $note->longurl, + ], 201)->header('Location', $note->longurl); + } + if ($request->input('h') == 'card' || $request->input('type')[0] == 'h-card') { + if (stristr($tokenData->getClaim('scope'), 'create') === false) { + return $this->returnInsufficientScopeResponse(); + } + $data = []; + if ($request->header('Content-Type') == 'application/json') { + $data['name'] = $request->input('properties.name'); + $data['description'] = $request->input('properties.description') ?? null; + if ($request->has('properties.geo')) { + $data['geo'] = $request->input('properties.geo'); + } + } else { + $data['name'] = $request->input('name'); + $data['description'] = $request->input('description'); + if ($request->has('geo')) { + $data['geo'] = $request->input('geo'); + } + if ($request->has('latitude')) { + $data['latitude'] = $request->input('latitude'); + $data['longitude'] = $request->input('longitude'); + } + } + try { + $place = $this->placeService->createPlace($data); + } catch (\Exception $exception) { + return response()->json(['error' => true], 400); + } + + return response()->json([ + 'response' => 'created', + 'location' => $place->longurl, + ], 201)->header('Location', $place->longurl); + } + if ($request->input('action') == 'update') { + if (stristr($tokenData->getClaim('scope'), 'update') === false) { + return $this->returnInsufficientScopeResponse(); + } + $urlPath = parse_url($request->input('url'), PHP_URL_PATH); + //is it a note we are updating? + if (mb_substr($urlPath, 1, 5) === 'notes') { + try { + $note = Note::nb60(basename($urlPath))->first(); + } catch (\Exception $exception) { + return response()->json([ + 'error' => 'invalid_request', + 'error_description' => 'No known note with given ID', + ]); + } + //got the note, are we dealing with a “replace” request? + if ($request->has('replace')) { + foreach ($request->input('replace') as $property => $value) { + if ($property == 'content') { + $note->note = $value[0]; + } + if ($property == 'syndication') { + foreach ($value as $syndicationURL) { + if (starts_with($syndicationURL, 'https://www.facebook.com')) { + $note->facebook_url = $syndicationURL; + } + if (starts_with($syndicationURL, 'https://www.swarmapp.com')) { + $note->swarm_url = $syndicationURL; + } + if (starts_with($syndicationURL, 'https://twitter.com')) { + $note->tweet_id = basename(parse_url($syndicationURL, PHP_URL_PATH)); + } + } + } + } + $note->save(); + + return response()->json([ + 'response' => 'updated', + ]); + } + //how about “add” + if ($request->has('add')) { + foreach ($request->input('add') as $property => $value) { + if ($property == 'syndication') { + foreach ($value as $syndicationURL) { + if (starts_with($syndicationURL, 'https://www.facebook.com')) { + $note->facebook_url = $syndicationURL; + } + if (starts_with($syndicationURL, 'https://www.swarmapp.com')) { + $note->swarm_url = $syndicationURL; + } + if (starts_with($syndicationURL, 'https://twitter.com')) { + $note->tweet_id = basename(parse_url($syndicationURL, PHP_URL_PATH)); + } + } + } + if ($property == 'photo') { + foreach ($value as $photoURL) { + if (start_with($photo, 'https://')) { + $media = new Media(); + $media->path = $photoURL; + $media->type = 'image'; + $media->save(); + $note->media()->save($media); + } + } + } + } + $note->save(); + + return response()->json([ + 'response' => 'updated', + ]); + } } } } return response()->json([ 'response' => 'error', - 'error' => 'invalid_token', - 'error_description' => 'The token provided is not valid or does not have the necessary scope', - ], 400); + 'error' => 'forbidden', + 'error_description' => 'The token has no scopes', + ], 403); } /** @@ -172,7 +270,7 @@ class MicropubController extends Controller { try { $tokenData = $this->tokenService->validateToken($request->bearerToken()); - } catch (\Exception $e) { + } catch (InvalidTokenException $e) { return response()->json([ 'response' => 'error', 'error' => 'invalid_token', @@ -232,8 +330,9 @@ class MicropubController extends Controller */ public function media(Request $request) { - $tokenData = $this->tokenService->validateToken($request->bearerToken()); - if ($tokenData === null) { + try { + $tokenData = $this->tokenService->validateToken($request->bearerToken()); + } catch (InvalidTokenException $e) { return response()->json([ 'response' => 'error', 'error' => 'invalid_token', @@ -243,54 +342,54 @@ class MicropubController extends Controller //check post scope if ($tokenData->hasClaim('scope')) { - $scopes = explode(' ', $tokenData->getClaim('scope')); - if (array_search('post', $scopes) !== false) { - //check media valid - if ($request->hasFile('file') && $request->file('file')->isValid()) { - $type = $this->getFileTypeFromMimeType($request->file('file')->getMimeType()); - try { - $filename = Uuid::uuid4() . '.' . $request->file('file')->extension(); - } catch (UnsatisfiedDependencyException $e) { - return response()->json([ - 'response' => 'error', - 'error' => 'internal_server_error', - 'error_description' => 'A problem occured handling your request', - ], 500); - } - try { - $path = $request->file('file')->storeAs('media', $filename, 's3'); - } catch (Exception $e) { // which exception? - return response()->json([ - 'response' => 'error', - 'error' => 'service_unavailable', - 'error_description' => 'Unable to save media to S3', - ], 503); - } - $media = new Media(); - $media->token = $request->bearerToken(); - $media->path = $path; - $media->type = $type; - $media->save(); - + if (stristr($token->getClaim('scope'), 'post') === false) { + return $this->returnInsufficientScopeResponse(); + } + //check media valid + if ($request->hasFile('file') && $request->file('file')->isValid()) { + $type = $this->getFileTypeFromMimeType($request->file('file')->getMimeType()); + try { + $filename = Uuid::uuid4() . '.' . $request->file('file')->extension(); + } catch (UnsatisfiedDependencyException $e) { return response()->json([ - 'response' => 'created', - 'location' => $media->url, - ], 201)->header('Location', $media->url); + 'response' => 'error', + 'error' => 'internal_server_error', + 'error_description' => 'A problem occured handling your request', + ], 500); } + try { + $path = $request->file('file')->storeAs('media', $filename, 's3'); + } catch (Exception $e) { // which exception? + return response()->json([ + 'response' => 'error', + 'error' => 'service_unavailable', + 'error_description' => 'Unable to save media to S3', + ], 503); + } + $media = new Media(); + $media->token = $request->bearerToken(); + $media->path = $path; + $media->type = $type; + $media->save(); return response()->json([ - 'response' => 'error', - 'error' => 'invalid_request', - 'error_description' => 'The uploaded file failed validation', - ], 400); + 'response' => 'created', + 'location' => $media->url, + ], 201)->header('Location', $media->url); } + + return response()->json([ + 'response' => 'error', + 'error' => 'invalid_request', + 'error_description' => 'The uploaded file failed validation', + ], 400); } return response()->json([ 'response' => 'error', - 'error' => 'insufficient_scope', - 'error_description' => 'The provided token has insufficient scopes', - ], 401); + 'error' => 'invalid_request', + 'error_description' => 'The provided token has no scopes', + ], 400); } /** @@ -336,4 +435,13 @@ class MicropubController extends Controller return 'download'; } + + private function returnInsufficientScopeResponse() + { + return response()->json([ + 'response' => 'error', + 'error' => 'insufficient_scope', + 'error_description' => 'The token’s scope does not have the necessary requirements.', + ], 401); + } } diff --git a/app/Http/Controllers/TokenEndpointController.php b/app/Http/Controllers/TokenEndpointController.php new file mode 100644 index 00000000..fb0da5ac --- /dev/null +++ b/app/Http/Controllers/TokenEndpointController.php @@ -0,0 +1,79 @@ +client = $client ?? new Client(); + $this->tokenService = $tokenService ?? new TokenService(); + } + + /** + * If the user has auth’d via the IndieAuth protocol, issue a valid token. + * + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\Response + */ + public function create(Request $request) + { + $authorizationEndpoint = $this->client->discoverAuthorizationEndpoint(normalize_url($request->input('me'))); + if ($authorizationEndpoint) { + $auth = $this->client->verifyIndieAuthCode( + $authorizationEndpoint, + $request->input('code'), + $request->input('me'), + $request->input('redirect_uri'), + $request->input('client_id'), + $request->input('state') + ); + if (array_key_exists('me', $auth)) { + $scope = $auth['scope'] ?? ''; + $tokenData = [ + 'me' => $request->input('me'), + 'client_id' => $request->input('client_id'), + 'scope' => $scope, + ]; + $token = $this->tokenService->getNewToken($tokenData); + $content = http_build_query([ + 'me' => $request->input('me'), + 'scope' => $scope, + 'access_token' => $token, + ]); + + return response($content)->header( + 'Content-Type', + 'application/x-www-form-urlencoded' + ); + } + + return response('There was an error verifying the authorisation code.', 400); + } + + return response('Can’t determine the authorisation endpoint.', 400); + } +} diff --git a/app/Media.php b/app/Media.php index f0234c6e..bf329391 100644 --- a/app/Media.php +++ b/app/Media.php @@ -28,6 +28,10 @@ class Media extends Model */ public function getUrlAttribute() { + if (starts_with($this->path, 'https://')) { + return $this->path; + } + return config('filesystems.disks.s3.url') . '/' . $this->path; } } diff --git a/app/Note.php b/app/Note.php index 6a51a896..000ccd25 100644 --- a/app/Note.php +++ b/app/Note.php @@ -191,6 +191,20 @@ class Note extends Model return $name; } + /** + * Scope a query to select a note via a NewBase60 id. + * + * @param \Illuminate\Database\Eloquent\Builder $query + * @param string $nb60id + * @return \Illuminate\Database\Eloquent\Builder + */ + public function scopeNb60($query, $nb60id) + { + $numbers = new Numbers(); + + return $query->where('id', $numbers->b60tonum($nb60id)); + } + /** * Take note that this method does two things, given @username (NOT [@username](URL)!) * we try to create a fancy hcard from our contact info. If this is not possible diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index d3aa0488..88d47aec 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -33,7 +33,7 @@ class AppServiceProvider extends ServiceProvider //Add tags for notes Note::created(function ($note) { $tagsToAdd = []; - preg_match_all('/#([^\s<>]+)\b/', $note, $tags); + preg_match_all('/#([^\s<>]+)\b/', $note->note, $tags); foreach ($tags[1] as $tag) { $tag = Tag::normalizeTag($tag); } diff --git a/app/Services/NoteService.php b/app/Services/NoteService.php index d395327b..09dbb1c9 100644 --- a/app/Services/NoteService.php +++ b/app/Services/NoteService.php @@ -17,6 +17,16 @@ class NoteService */ public function createNote(array $data): Note { + //check the input + if (array_key_exists('content', $data) === false) { + throw new \Exception('No content defined'); //we can’t fudge the data + } + if (array_key_exists('in-reply-to', $data) === false) { + $data['in-reply-to'] = null; + } + if (array_key_exists('client-id', $data) === false) { + $data['client-id'] = null; + } $note = Note::create( [ 'note' => $data['content'], @@ -25,6 +35,11 @@ class NoteService ] ); + if (array_key_exists('published', $data) && empty($data['published']) === false) { + $carbon = new \Carbon\Carbon($data['published']); + $note->created_at = $note->updated_at = $carbon->toDateTimeString(); + } + if (array_key_exists('location', $data) && $data['location'] !== null && $data['location'] !== 'no-location') { if (starts_with($data['location'], config('app.url'))) { //uri of form http://host/places/slug, we want slug @@ -44,6 +59,13 @@ class NoteService } } + if (array_key_exists('checkin', $data) && $data['checkin'] !== null) { + $place = Place::where('foursquare', $data['checkin'])->first(); + if ($place !== null) { + $note->place()->associate($place); + } + } + /* drop image support for now //add images to media library if ($request->hasFile('photo')) { @@ -55,12 +77,17 @@ class NoteService */ //add support for media uploaded as URLs foreach ($data['photo'] as $photo) { - // check the media was uploaded to my endpoint + // check the media was uploaded to my endpoint, and use path if (starts_with($photo, config('filesystems.disks.s3.url'))) { $path = substr($photo, strlen(config('filesystems.disks.s3.url'))); $media = Media::where('path', ltrim($path, '/'))->firstOrFail(); - $note->media()->save($media); + } else { + $media = Media::firstOrNew(['path' => $photo]); + // currently assuming this is a photo from Swarm + $media->type = 'image'; + $media->save(); } + $note->media()->save($media); } $note->save(); diff --git a/app/Services/PlaceService.php b/app/Services/PlaceService.php index 1a02dff5..f1bc249a 100644 --- a/app/Services/PlaceService.php +++ b/app/Services/PlaceService.php @@ -36,4 +36,39 @@ class PlaceService return $place; } + + /** + * Create a place from a h-card checkin, for exameple from OwnYourSwarm. + * + * @param array + * @return bool + */ + public function createPlaceFromCheckin(array $checkin): bool + { + //check if the place exists if from swarm + if (array_key_exists('url', $checkin['properties'])) { + $search = Place::where('foursquare', $checkin['properties']['url'][0])->count(); + if ($search === 1) { + return true; + } + } + if (array_key_exists('name', $checkin['properties']) === false) { + throw new \InvalidArgumentException('Missing required name'); + } + if (array_key_exists('latitude', $checkin['properties']) === false) { + throw new \InvalidArgumentException('Missing required longitude/latitude'); + } + $place = new Place(); + $place->name = $checkin['properties']['name'][0]; + if (starts_with($checkin['properties']['url'][0], 'https://foursquare.com')) { + $place->foursquare = $checkin['properties']['url'][0]; + } + $place->location = new Point( + (float) $checkin['properties']['latitude'][0], + (float) $checkin['properties']['longitude'][0] + ); + $place->save(); + + return true; + } } diff --git a/app/Services/TokenService.php b/app/Services/TokenService.php index fecb8ded..42f2f718 100644 --- a/app/Services/TokenService.php +++ b/app/Services/TokenService.php @@ -4,10 +4,9 @@ declare(strict_types=1); namespace App\Services; -use Lcobucci\JWT\Token; -use Lcobucci\JWT\Parser; -use Lcobucci\JWT\Builder; use Lcobucci\JWT\Signer\Hmac\Sha256; +use App\Exceptions\InvalidTokenException; +use Lcobucci\JWT\{Builder, Parser, Token}; class TokenService { @@ -40,9 +39,13 @@ class TokenService public function validateToken(string $bearerToken): ?Token { $signer = new Sha256(); - $token = (new Parser())->parse((string) $bearerToken); + try { + $token = (new Parser())->parse((string) $bearerToken); + } catch (\InvalidArgumentException $e) { + throw new InvalidTokenException('Token could not be parsed'); + } if (! $token->verify($signer, config('app.key'))) { - throw new \Exception('Token not verified'); + throw new InvalidTokenException('Token failed verification'); } return $token; diff --git a/app/helpers.php b/app/helpers.php deleted file mode 100644 index 6dcad34d..00000000 --- a/app/helpers.php +++ /dev/null @@ -1,191 +0,0 @@ - 80, 'https' => 443]; - if (isset($url['scheme'])) { - $url['scheme'] = strtolower($url['scheme']); - // Strip scheme default ports - if ( - isset($defaultSchemes[$url['scheme']]) && - isset($url['port']) && - $defaultSchemes[$url['scheme']] == $url['port'] - ) { - unset($url['port']); - } - $newUrl .= "{$url['scheme']}://"; - } - if (isset($url['host'])) { - $url['host'] = mb_strtolower($url['host']); - $newUrl .= $url['host']; - } - if (isset($url['port'])) { - $newUrl .= ":{$url['port']}"; - } - // here we only want to drop a slash for the root domain - // e.g. http://example.com/ -> http://example.com - // but http://example.com/path/ -/-> http://example.com/path - if (isset($url['path']) && $url['path'] == '/') { - unset($url['path']); - } - if (isset($url['path'])) { - // Case normalization - $url['path'] = normalizer_normalize($url['path'], Normalizer::FORM_C); - // Strip duplicate slashes - while (preg_match("/\/\//", $url['path'])) { - $url['path'] = preg_replace('/\/\//', '/', $url['path']); - } - - /* - * Decode unreserved characters, http://www.apps.ietf.org/rfc/rfc3986.html#sec-2.3 - * Heavily rewritten version of urlDecodeUnreservedChars() in Glen Scott's url-normalizer. - */ - $u = []; - for ($o = 65; $o <= 90; $o++) { - $u[] = dechex($o); - } - for ($o = 97; $o <= 122; $o++) { - $u[] = dechex($o); - } - for ($o = 48; $o <= 57; $o++) { - $u[] = dechex($o); - } - $chrs = ['-', '.', '_', '~']; - foreach ($chrs as $chr) { - $u[] = dechex(ord($chr)); - } - $url['path'] = preg_replace_callback( - array_map( - create_function('$str', 'return "/%" . strtoupper($str) . "/x";'), - $u - ), - create_function('$matches', 'return chr(hexdec($matches[0]));'), - $url['path'] - ); - // Remove directory index - $defaultIndexes = ["/default\.aspx/" => 'default.aspx', "/default\.asp/" => 'default.asp', - "/index\.html/" => 'index.html', "/index\.htm/" => 'index.htm', - "/default\.html/" => 'default.html', "/default\.htm/" => 'default.htm', - "/index\.php/" => 'index.php', "/index\.jsp/" => 'index.jsp', ]; - foreach ($defaultIndexes as $index => $strip) { - if (preg_match($index, $url['path'])) { - $url['path'] = str_replace($strip, '', $url['path']); - } - } - - /** - * Path segment normalization, http://www.apps.ietf.org/rfc/rfc3986.html#sec-5.2.4 - * Heavily rewritten version of removeDotSegments() in Glen Scott's url-normalizer. - */ - $new_path = ''; - while (! empty($url['path'])) { - if (preg_match('!^(\.\./|\./)!x', $url['path'])) { - $url['path'] = preg_replace('!^(\.\./|\./)!x', '', $url['path']); - } elseif (preg_match('!^(/\./)!x', $url['path'], $matches) || preg_match('!^(/\.)$!x', $url['path'], $matches)) { - $url['path'] = preg_replace('!^' . $matches[1] . '!', '/', $url['path']); - } elseif (preg_match('!^(/\.\./|/\.\.)!x', $url['path'], $matches)) { - $url['path'] = preg_replace('!^' . preg_quote($matches[1], '!') . '!x', '/', $url['path']); - $new_path = preg_replace('!/([^/]+)$!x', '', $new_path); - } elseif (preg_match('!^(\.|\.\.)$!x', $url['path'])) { - $url['path'] = preg_replace('!^(\.|\.\.)$!x', '', $url['path']); - } else { - if (preg_match('!(/*[^/]*)!x', $url['path'], $matches)) { - $first_path_segment = $matches[1]; - $url['path'] = preg_replace('/^' . preg_quote($first_path_segment, '/') . '/', '', $url['path'], 1); - $new_path .= $first_path_segment; - } - } - } - $newUrl .= $new_path; - } - - if (isset($url['fragment'])) { - unset($url['fragment']); - } - - // Sort GET params alphabetically, not because the RFC requires it but because it's cool! - if (isset($url['query'])) { - if (preg_match('/&/', $url['query'])) { - $s = explode('&', $url['query']); - $url['query'] = ''; - sort($s); - foreach ($s as $z) { - $url['query'] .= "{$z}&"; - } - $url['query'] = preg_replace('/&\Z/', '', $url['query']); - } - $newUrl .= "?{$url['query']}"; - } - - return $newUrl; -} - -// sourced from https://stackoverflow.com/a/9776726 -function prettyPrintJson(string $json): string -{ - $result = ''; - $level = 0; - $in_quotes = false; - $in_escape = false; - $ends_line_level = null; - $json_length = strlen($json); - - for ($i = 0; $i < $json_length; $i++) { - $char = $json[$i]; - $new_line_level = null; - $post = ''; - if ($ends_line_level !== null) { - $new_line_level = $ends_line_level; - $ends_line_level = null; - } - if ($in_escape) { - $in_escape = false; - } elseif ($char === '"') { - $in_quotes = ! $in_quotes; - } elseif (! $in_quotes) { - switch ($char) { - case '}': case ']': - $level--; - $ends_line_level = null; - $new_line_level = $level; - break; - - case '{': case '[': - $level++; - case ',': - $ends_line_level = $level; - break; - - case ':': - $post = ' '; - break; - - case ' ': case "\t": case "\n": case "\r": - $char = ''; - $ends_line_level = $new_line_level; - $new_line_level = null; - break; - } - } elseif ($char === '\\') { - $in_escape = true; - } - if ($new_line_level !== null) { - $result .= "\n".str_repeat("\t", $new_line_level); - } - $result .= $char.$post; - } - - return str_replace("\t", ' ', $result); -} diff --git a/composer.json b/composer.json index b5531f9a..d1ff9e16 100644 --- a/composer.json +++ b/composer.json @@ -44,7 +44,7 @@ "App\\": "app/" }, "files": [ - "app/helpers.php" + "helpers.php" ] }, "autoload-dev": { diff --git a/composer.lock b/composer.lock index 2188bc12..02b615a3 100644 --- a/composer.lock +++ b/composer.lock @@ -8,16 +8,16 @@ "packages": [ { "name": "aws/aws-sdk-php", - "version": "3.25.7", + "version": "3.27.2", "source": { "type": "git", "url": "https://github.com/aws/aws-sdk-php.git", - "reference": "d4f1104f5ac9c755875c5e6e9bade2c70708219a" + "reference": "eb10e43cccf8e868f9622ab8ce2beb9fb756b5a8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/d4f1104f5ac9c755875c5e6e9bade2c70708219a", - "reference": "d4f1104f5ac9c755875c5e6e9bade2c70708219a", + "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/eb10e43cccf8e868f9622ab8ce2beb9fb756b5a8", + "reference": "eb10e43cccf8e868f9622ab8ce2beb9fb756b5a8", "shasum": "" }, "require": { @@ -39,7 +39,7 @@ "ext-simplexml": "*", "ext-spl": "*", "nette/neon": "^2.3", - "phpunit/phpunit": "~4.0|~5.0", + "phpunit/phpunit": "^4.8.35|^5.4.0", "psr/cache": "^1.0" }, "suggest": { @@ -84,7 +84,7 @@ "s3", "sdk" ], - "time": "2017-04-11T22:31:27+00:00" + "time": "2017-05-11T21:23:43+00:00" }, { "name": "barnabywalters/mf-cleaner", @@ -1104,16 +1104,16 @@ }, { "name": "indieauth/client", - "version": "0.2.0", + "version": "0.2.1", "source": { "type": "git", "url": "https://github.com/indieweb/indieauth-client-php.git", - "reference": "4b9bd766a92b8abbe420f5889bf7ebac7678151d" + "reference": "f5f6efad79334d1ff9370fe4dce8ccf4814820fa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/indieweb/indieauth-client-php/zipball/4b9bd766a92b8abbe420f5889bf7ebac7678151d", - "reference": "4b9bd766a92b8abbe420f5889bf7ebac7678151d", + "url": "https://api.github.com/repos/indieweb/indieauth-client-php/zipball/f5f6efad79334d1ff9370fe4dce8ccf4814820fa", + "reference": "f5f6efad79334d1ff9370fe4dce8ccf4814820fa", "shasum": "" }, "require": { @@ -1139,7 +1139,7 @@ } ], "description": "IndieAuth Client Library", - "time": "2017-02-09T23:42:05+00:00" + "time": "2017-04-26T21:44:35+00:00" }, { "name": "indieweb/link-rel-parser", @@ -1464,16 +1464,16 @@ }, { "name": "laravel/framework", - "version": "v5.4.19", + "version": "v5.4.23", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "02444b7450350db17a7607c8a52f7268ebdb0dad" + "reference": "ad82327705658dbf5f0ce72805caa950dfbe150d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/02444b7450350db17a7607c8a52f7268ebdb0dad", - "reference": "02444b7450350db17a7607c8a52f7268ebdb0dad", + "url": "https://api.github.com/repos/laravel/framework/zipball/ad82327705658dbf5f0ce72805caa950dfbe150d", + "reference": "ad82327705658dbf5f0ce72805caa950dfbe150d", "shasum": "" }, "require": { @@ -1589,7 +1589,7 @@ "framework", "laravel" ], - "time": "2017-04-16T13:33:34+00:00" + "time": "2017-05-11T20:10:35+00:00" }, { "name": "laravel/scout", @@ -1769,16 +1769,16 @@ }, { "name": "league/commonmark", - "version": "0.15.3", + "version": "0.15.4", "source": { "type": "git", "url": "https://github.com/thephpleague/commonmark.git", - "reference": "c8b43ee5821362216f8e9ac684f0f59de164edcc" + "reference": "c4c8e6bf99e62d9568875d9fc3ef473fe3e18e0c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/c8b43ee5821362216f8e9ac684f0f59de164edcc", - "reference": "c8b43ee5821362216f8e9ac684f0f59de164edcc", + "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/c4c8e6bf99e62d9568875d9fc3ef473fe3e18e0c", + "reference": "c4c8e6bf99e62d9568875d9fc3ef473fe3e18e0c", "shasum": "" }, "require": { @@ -1834,20 +1834,20 @@ "markdown", "parser" ], - "time": "2016-12-19T00:11:43+00:00" + "time": "2017-05-09T12:47:53+00:00" }, { "name": "league/flysystem", - "version": "1.0.37", + "version": "1.0.40", "source": { "type": "git", "url": "https://github.com/thephpleague/flysystem.git", - "reference": "78b5cc4feb61a882302df4fbaf63b7662e5e4ccd" + "reference": "3828f0b24e2c1918bb362d57a53205d6dc8fde61" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/78b5cc4feb61a882302df4fbaf63b7662e5e4ccd", - "reference": "78b5cc4feb61a882302df4fbaf63b7662e5e4ccd", + "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/3828f0b24e2c1918bb362d57a53205d6dc8fde61", + "reference": "3828f0b24e2c1918bb362d57a53205d6dc8fde61", "shasum": "" }, "require": { @@ -1869,12 +1869,12 @@ "league/flysystem-azure": "Allows you to use Windows Azure Blob storage", "league/flysystem-cached-adapter": "Flysystem adapter decorator for metadata caching", "league/flysystem-copy": "Allows you to use Copy.com storage", - "league/flysystem-dropbox": "Allows you to use Dropbox storage", "league/flysystem-eventable-filesystem": "Allows you to use EventableFilesystem", "league/flysystem-rackspace": "Allows you to use Rackspace Cloud Files", "league/flysystem-sftp": "Allows you to use SFTP server storage via phpseclib", "league/flysystem-webdav": "Allows you to use WebDAV storage", - "league/flysystem-ziparchive": "Allows you to use ZipArchive adapter" + "league/flysystem-ziparchive": "Allows you to use ZipArchive adapter", + "spatie/flysystem-dropbox": "Allows you to use Dropbox storage" }, "type": "library", "extra": { @@ -1917,25 +1917,25 @@ "sftp", "storage" ], - "time": "2017-03-22T15:43:14+00:00" + "time": "2017-04-28T10:15:08+00:00" }, { "name": "league/flysystem-aws-s3-v3", - "version": "1.0.13", + "version": "1.0.15", "source": { "type": "git", "url": "https://github.com/thephpleague/flysystem-aws-s3-v3.git", - "reference": "dc56a8faf3aff0841f9eae04b6af94a50657896c" + "reference": "c947f36f977b495a57e857ae1630df0da35ec456" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem-aws-s3-v3/zipball/dc56a8faf3aff0841f9eae04b6af94a50657896c", - "reference": "dc56a8faf3aff0841f9eae04b6af94a50657896c", + "url": "https://api.github.com/repos/thephpleague/flysystem-aws-s3-v3/zipball/c947f36f977b495a57e857ae1630df0da35ec456", + "reference": "c947f36f977b495a57e857ae1630df0da35ec456", "shasum": "" }, "require": { "aws/aws-sdk-php": "^3.0.0", - "league/flysystem": "~1.0", + "league/flysystem": "^1.0.40", "php": ">=5.5.0" }, "require-dev": { @@ -1964,7 +1964,7 @@ } ], "description": "Flysystem adapter for the AWS S3 SDK v3.x", - "time": "2016-06-21T21:34:35+00:00" + "time": "2017-04-28T10:21:54+00:00" }, { "name": "martinbean/laravel-sluggable-trait", @@ -2844,16 +2844,16 @@ }, { "name": "swiftmailer/swiftmailer", - "version": "v5.4.6", + "version": "v5.4.8", "source": { "type": "git", "url": "https://github.com/swiftmailer/swiftmailer.git", - "reference": "81fdccfaf8bdc5d5d7a1ef6bb3a61bbb1a6c4a3e" + "reference": "9a06dc570a0367850280eefd3f1dc2da45aef517" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/81fdccfaf8bdc5d5d7a1ef6bb3a61bbb1a6c4a3e", - "reference": "81fdccfaf8bdc5d5d7a1ef6bb3a61bbb1a6c4a3e", + "url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/9a06dc570a0367850280eefd3f1dc2da45aef517", + "reference": "9a06dc570a0367850280eefd3f1dc2da45aef517", "shasum": "" }, "require": { @@ -2894,20 +2894,20 @@ "mail", "mailer" ], - "time": "2017-02-13T07:52:53+00:00" + "time": "2017-05-01T15:54:03+00:00" }, { "name": "symfony/console", - "version": "v3.2.7", + "version": "v3.2.8", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "c30243cc51f726812be3551316b109a2f5deaf8d" + "reference": "a7a17e0c6c3c4d70a211f80782e4b90ddadeaa38" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/c30243cc51f726812be3551316b109a2f5deaf8d", - "reference": "c30243cc51f726812be3551316b109a2f5deaf8d", + "url": "https://api.github.com/repos/symfony/console/zipball/a7a17e0c6c3c4d70a211f80782e4b90ddadeaa38", + "reference": "a7a17e0c6c3c4d70a211f80782e4b90ddadeaa38", "shasum": "" }, "require": { @@ -2957,20 +2957,20 @@ ], "description": "Symfony Console Component", "homepage": "https://symfony.com", - "time": "2017-04-04T14:33:42+00:00" + "time": "2017-04-26T01:39:17+00:00" }, { "name": "symfony/css-selector", - "version": "v3.2.7", + "version": "v3.2.8", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", - "reference": "a48f13dc83c168f1253a5d2a5a4fb46c36244c4c" + "reference": "02983c144038e697c959e6b06ef6666de759ccbc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/a48f13dc83c168f1253a5d2a5a4fb46c36244c4c", - "reference": "a48f13dc83c168f1253a5d2a5a4fb46c36244c4c", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/02983c144038e697c959e6b06ef6666de759ccbc", + "reference": "02983c144038e697c959e6b06ef6666de759ccbc", "shasum": "" }, "require": { @@ -3010,20 +3010,20 @@ ], "description": "Symfony CssSelector Component", "homepage": "https://symfony.com", - "time": "2017-02-21T09:12:04+00:00" + "time": "2017-05-01T14:55:58+00:00" }, { "name": "symfony/debug", - "version": "v3.2.7", + "version": "v3.2.8", "source": { "type": "git", "url": "https://github.com/symfony/debug.git", - "reference": "56f613406446a4a0a031475cfd0a01751de22659" + "reference": "fd6eeee656a5a7b384d56f1072243fe1c0e81686" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/debug/zipball/56f613406446a4a0a031475cfd0a01751de22659", - "reference": "56f613406446a4a0a031475cfd0a01751de22659", + "url": "https://api.github.com/repos/symfony/debug/zipball/fd6eeee656a5a7b384d56f1072243fe1c0e81686", + "reference": "fd6eeee656a5a7b384d56f1072243fe1c0e81686", "shasum": "" }, "require": { @@ -3067,20 +3067,20 @@ ], "description": "Symfony Debug Component", "homepage": "https://symfony.com", - "time": "2017-03-28T21:38:24+00:00" + "time": "2017-04-19T20:17:50+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v3.2.7", + "version": "v3.2.8", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "154bb1ef7b0e42ccc792bd53edbce18ed73440ca" + "reference": "b8a401f733b43251e1d088c589368b2a94155e40" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/154bb1ef7b0e42ccc792bd53edbce18ed73440ca", - "reference": "154bb1ef7b0e42ccc792bd53edbce18ed73440ca", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/b8a401f733b43251e1d088c589368b2a94155e40", + "reference": "b8a401f733b43251e1d088c589368b2a94155e40", "shasum": "" }, "require": { @@ -3127,20 +3127,20 @@ ], "description": "Symfony EventDispatcher Component", "homepage": "https://symfony.com", - "time": "2017-04-04T07:26:27+00:00" + "time": "2017-05-01T14:58:48+00:00" }, { "name": "symfony/finder", - "version": "v3.2.7", + "version": "v3.2.8", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "b20900ce5ea164cd9314af52725b0bb5a758217a" + "reference": "9cf076f8f492f4b1ffac40aae9c2d287b4ca6930" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/b20900ce5ea164cd9314af52725b0bb5a758217a", - "reference": "b20900ce5ea164cd9314af52725b0bb5a758217a", + "url": "https://api.github.com/repos/symfony/finder/zipball/9cf076f8f492f4b1ffac40aae9c2d287b4ca6930", + "reference": "9cf076f8f492f4b1ffac40aae9c2d287b4ca6930", "shasum": "" }, "require": { @@ -3176,20 +3176,20 @@ ], "description": "Symfony Finder Component", "homepage": "https://symfony.com", - "time": "2017-03-20T09:32:19+00:00" + "time": "2017-04-12T14:13:17+00:00" }, { "name": "symfony/http-foundation", - "version": "v3.2.7", + "version": "v3.2.8", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "cb0b6418f588952c9290b3df4ca650f1b7ab570a" + "reference": "9de6add7f731e5af7f5b2e9c0da365e43383ebef" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/cb0b6418f588952c9290b3df4ca650f1b7ab570a", - "reference": "cb0b6418f588952c9290b3df4ca650f1b7ab570a", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/9de6add7f731e5af7f5b2e9c0da365e43383ebef", + "reference": "9de6add7f731e5af7f5b2e9c0da365e43383ebef", "shasum": "" }, "require": { @@ -3229,20 +3229,20 @@ ], "description": "Symfony HttpFoundation Component", "homepage": "https://symfony.com", - "time": "2017-04-04T15:30:56+00:00" + "time": "2017-05-01T14:55:58+00:00" }, { "name": "symfony/http-kernel", - "version": "v3.2.7", + "version": "v3.2.8", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "8285ab5faf1306b1a5ebcf287fe91c231a6de88e" + "reference": "46e8b209abab55c072c47d72d5cd1d62c0585e05" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/8285ab5faf1306b1a5ebcf287fe91c231a6de88e", - "reference": "8285ab5faf1306b1a5ebcf287fe91c231a6de88e", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/46e8b209abab55c072c47d72d5cd1d62c0585e05", + "reference": "46e8b209abab55c072c47d72d5cd1d62c0585e05", "shasum": "" }, "require": { @@ -3311,7 +3311,7 @@ ], "description": "Symfony HttpKernel Component", "homepage": "https://symfony.com", - "time": "2017-04-05T12:52:03+00:00" + "time": "2017-05-01T17:46:48+00:00" }, { "name": "symfony/polyfill-mbstring", @@ -3374,16 +3374,16 @@ }, { "name": "symfony/process", - "version": "v3.2.7", + "version": "v3.2.8", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "57fdaa55827ae14d617550ebe71a820f0a5e2282" + "reference": "999c2cf5061e627e6cd551dc9ebf90dd1d11d9f0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/57fdaa55827ae14d617550ebe71a820f0a5e2282", - "reference": "57fdaa55827ae14d617550ebe71a820f0a5e2282", + "url": "https://api.github.com/repos/symfony/process/zipball/999c2cf5061e627e6cd551dc9ebf90dd1d11d9f0", + "reference": "999c2cf5061e627e6cd551dc9ebf90dd1d11d9f0", "shasum": "" }, "require": { @@ -3419,20 +3419,20 @@ ], "description": "Symfony Process Component", "homepage": "https://symfony.com", - "time": "2017-03-27T18:07:02+00:00" + "time": "2017-04-12T14:13:17+00:00" }, { "name": "symfony/routing", - "version": "v3.2.7", + "version": "v3.2.8", "source": { "type": "git", "url": "https://github.com/symfony/routing.git", - "reference": "d6605f9a5767bc5bc4895e1c762ba93964608aee" + "reference": "5029745d6d463585e8b487dbc83d6333f408853a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/routing/zipball/d6605f9a5767bc5bc4895e1c762ba93964608aee", - "reference": "d6605f9a5767bc5bc4895e1c762ba93964608aee", + "url": "https://api.github.com/repos/symfony/routing/zipball/5029745d6d463585e8b487dbc83d6333f408853a", + "reference": "5029745d6d463585e8b487dbc83d6333f408853a", "shasum": "" }, "require": { @@ -3494,20 +3494,20 @@ "uri", "url" ], - "time": "2017-03-02T15:58:09+00:00" + "time": "2017-04-12T14:13:17+00:00" }, { "name": "symfony/translation", - "version": "v3.2.7", + "version": "v3.2.8", "source": { "type": "git", "url": "https://github.com/symfony/translation.git", - "reference": "c740eee70783d2af4d3d6b70d5146f209e6b4d13" + "reference": "f4a04d2df710f81515df576b2de06bdeee518b83" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/c740eee70783d2af4d3d6b70d5146f209e6b4d13", - "reference": "c740eee70783d2af4d3d6b70d5146f209e6b4d13", + "url": "https://api.github.com/repos/symfony/translation/zipball/f4a04d2df710f81515df576b2de06bdeee518b83", + "reference": "f4a04d2df710f81515df576b2de06bdeee518b83", "shasum": "" }, "require": { @@ -3558,20 +3558,20 @@ ], "description": "Symfony Translation Component", "homepage": "https://symfony.com", - "time": "2017-03-21T21:44:32+00:00" + "time": "2017-04-12T14:13:17+00:00" }, { "name": "symfony/var-dumper", - "version": "v3.2.7", + "version": "v3.2.8", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "81dce20f69a8b40427e1f4e6462178df87cafc03" + "reference": "fa47963ac7979ddbd42b2d646d1b056bddbf7bb8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/81dce20f69a8b40427e1f4e6462178df87cafc03", - "reference": "81dce20f69a8b40427e1f4e6462178df87cafc03", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/fa47963ac7979ddbd42b2d646d1b056bddbf7bb8", + "reference": "fa47963ac7979ddbd42b2d646d1b056bddbf7bb8", "shasum": "" }, "require": { @@ -3582,9 +3582,11 @@ "phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0" }, "require-dev": { + "ext-iconv": "*", "twig/twig": "~1.20|~2.0" }, "suggest": { + "ext-iconv": "To convert non-UTF-8 strings to UTF-8 (or symfony/polyfill-iconv in case ext-iconv cannot be used).", "ext-symfony_debug": "" }, "type": "library", @@ -3624,7 +3626,7 @@ "debug", "dump" ], - "time": "2017-03-12T16:07:05+00:00" + "time": "2017-05-01T14:55:58+00:00" }, { "name": "themattharris/tmhoauth", @@ -3670,16 +3672,16 @@ }, { "name": "thujohn/twitter", - "version": "2.2.2", + "version": "2.2.5", "source": { "type": "git", "url": "https://github.com/thujohn/twitter.git", - "reference": "203d903856212835206675ae9c0504d74b681886" + "reference": "ff414bdadba3f1570ca211355e5359ec266552d8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thujohn/twitter/zipball/203d903856212835206675ae9c0504d74b681886", - "reference": "203d903856212835206675ae9c0504d74b681886", + "url": "https://api.github.com/repos/thujohn/twitter/zipball/ff414bdadba3f1570ca211355e5359ec266552d8", + "reference": "ff414bdadba3f1570ca211355e5359ec266552d8", "shasum": "" }, "require": { @@ -3710,7 +3712,7 @@ "laravel5", "twitter" ], - "time": "2017-01-31T23:37:52+00:00" + "time": "2017-04-27T09:00:04+00:00" }, { "name": "tijsverkoyen/css-to-inline-styles", @@ -3921,16 +3923,16 @@ }, { "name": "facebook/webdriver", - "version": "1.4.0", + "version": "1.4.1", "source": { "type": "git", "url": "https://github.com/facebook/php-webdriver.git", - "reference": "3ea034c056189e11c0ce7985332a9f4b5b2b5db2" + "reference": "eadb0b7a7c3e6578185197fd40158b08c3164c83" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/facebook/php-webdriver/zipball/3ea034c056189e11c0ce7985332a9f4b5b2b5db2", - "reference": "3ea034c056189e11c0ce7985332a9f4b5b2b5db2", + "url": "https://api.github.com/repos/facebook/php-webdriver/zipball/eadb0b7a7c3e6578185197fd40158b08c3164c83", + "reference": "eadb0b7a7c3e6578185197fd40158b08c3164c83", "shasum": "" }, "require": { @@ -3969,7 +3971,7 @@ "selenium", "webdriver" ], - "time": "2017-03-22T10:56:03+00:00" + "time": "2017-04-28T14:54:49+00:00" }, { "name": "fzaninotto/faker", @@ -4113,21 +4115,22 @@ }, { "name": "laravel/dusk", - "version": "v1.0.12", + "version": "v1.1.0", "source": { "type": "git", "url": "https://github.com/laravel/dusk.git", - "reference": "9a150bedc3ae6566d05f0a8d1657c658ce98dcad" + "reference": "6b81e97ae1ce384e3d8dbd020b2b9751c1449889" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/dusk/zipball/9a150bedc3ae6566d05f0a8d1657c658ce98dcad", - "reference": "9a150bedc3ae6566d05f0a8d1657c658ce98dcad", + "url": "https://api.github.com/repos/laravel/dusk/zipball/6b81e97ae1ce384e3d8dbd020b2b9751c1449889", + "reference": "6b81e97ae1ce384e3d8dbd020b2b9751c1449889", "shasum": "" }, "require": { "facebook/webdriver": "~1.0", - "illuminate/support": "~5.3", + "illuminate/console": "~5.4", + "illuminate/support": "~5.4", "nesbot/carbon": "~1.20", "php": ">=5.6.4", "symfony/console": "~3.2", @@ -4164,7 +4167,7 @@ "testing", "webdriver" ], - "time": "2017-04-07T21:47:06+00:00" + "time": "2017-04-23T17:13:04+00:00" }, { "name": "maximebf/debugbar", @@ -4232,12 +4235,12 @@ "version": "0.9.9", "source": { "type": "git", - "url": "https://github.com/padraic/mockery.git", + "url": "https://github.com/mockery/mockery.git", "reference": "6fdb61243844dc924071d3404bb23994ea0b6856" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/padraic/mockery/zipball/6fdb61243844dc924071d3404bb23994ea0b6856", + "url": "https://api.github.com/repos/mockery/mockery/zipball/6fdb61243844dc924071d3404bb23994ea0b6856", "reference": "6fdb61243844dc924071d3404bb23994ea0b6856", "shasum": "" }, @@ -5537,16 +5540,16 @@ }, { "name": "symfony/yaml", - "version": "v3.2.7", + "version": "v3.2.8", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "62b4cdb99d52cb1ff253c465eb1532a80cebb621" + "reference": "acec26fcf7f3031e094e910b94b002fa53d4e4d6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/62b4cdb99d52cb1ff253c465eb1532a80cebb621", - "reference": "62b4cdb99d52cb1ff253c465eb1532a80cebb621", + "url": "https://api.github.com/repos/symfony/yaml/zipball/acec26fcf7f3031e094e910b94b002fa53d4e4d6", + "reference": "acec26fcf7f3031e094e910b94b002fa53d4e4d6", "shasum": "" }, "require": { @@ -5588,20 +5591,20 @@ ], "description": "Symfony Yaml Component", "homepage": "https://symfony.com", - "time": "2017-03-20T09:45:15+00:00" + "time": "2017-05-01T14:55:58+00:00" }, { "name": "theseer/fdomdocument", - "version": "1.6.4", + "version": "1.6.5", "source": { "type": "git", "url": "https://github.com/theseer/fDOMDocument.git", - "reference": "cf219ede922fb47956726f35e2127277ebd302ca" + "reference": "8dcfd392135a5bd938c3c83ea71419501ad9855d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theseer/fDOMDocument/zipball/cf219ede922fb47956726f35e2127277ebd302ca", - "reference": "cf219ede922fb47956726f35e2127277ebd302ca", + "url": "https://api.github.com/repos/theseer/fDOMDocument/zipball/8dcfd392135a5bd938c3c83ea71419501ad9855d", + "reference": "8dcfd392135a5bd938c3c83ea71419501ad9855d", "shasum": "" }, "require": { @@ -5628,7 +5631,7 @@ ], "description": "The classes contained within this repository extend the standard DOM to use exceptions at all occasions of errors instead of PHP warnings or notices. They also add various custom methods and shortcuts for convenience and to simplify the usage of DOM.", "homepage": "https://github.com/theseer/fDOMDocument", - "time": "2017-04-17T09:08:13+00:00" + "time": "2017-04-21T14:50:31+00:00" }, { "name": "webmozart/assert", diff --git a/database/migrations/2017_04_25_203734_update_notes_table_add_swarm_url.php b/database/migrations/2017_04_25_203734_update_notes_table_add_swarm_url.php new file mode 100644 index 00000000..4a161735 --- /dev/null +++ b/database/migrations/2017_04_25_203734_update_notes_table_add_swarm_url.php @@ -0,0 +1,32 @@ +string('swarm_url')->nullable(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('notes', function (Blueprint $table) { + $table->dropColumn('swarm_url'); + }); + } +} diff --git a/database/migrations/2017_05_12_135451_update_places_table_add_foursquare_column.php b/database/migrations/2017_05_12_135451_update_places_table_add_foursquare_column.php new file mode 100644 index 00000000..0e352aff --- /dev/null +++ b/database/migrations/2017_05_12_135451_update_places_table_add_foursquare_column.php @@ -0,0 +1,32 @@ +string('foursquare')->nullable(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('places', function (Blueprint $table) { + $table->dropColumn('foursquare'); + }); + } +} diff --git a/helpers.php b/helpers.php new file mode 100644 index 00000000..d70efc56 --- /dev/null +++ b/helpers.php @@ -0,0 +1,199 @@ + 80, 'https' => 443]; + if (isset($url['scheme'])) { + $url['scheme'] = strtolower($url['scheme']); + // Strip scheme default ports + if (isset($defaultSchemes[$url['scheme']]) && + isset($url['port']) && + $defaultSchemes[$url['scheme']] == $url['port'] + ) { + unset($url['port']); + } + $newUrl .= "{$url['scheme']}://"; + } + if (isset($url['host'])) { + $url['host'] = mb_strtolower($url['host']); + $newUrl .= $url['host']; + } + if (isset($url['port'])) { + $newUrl .= ":{$url['port']}"; + } + + if (isset($url['path'])) { + // Case normalization + $url['path'] = normalizer_normalize($url['path'], Normalizer::FORM_C); + // Strip duplicate slashes + while (preg_match("/\/\//", $url['path'])) { + $url['path'] = preg_replace('/\/\//', '/', $url['path']); + } + + /* + * Decode unreserved characters, http://www.apps.ietf.org/rfc/rfc3986.html#sec-2.3 + * Heavily rewritten version of urlDecodeUnreservedChars() in Glen Scott's url-normalizer. + */ + $u = []; + for ($o = 65; $o <= 90; $o++) { + $u[] = dechex($o); + } + for ($o = 97; $o <= 122; $o++) { + $u[] = dechex($o); + } + for ($o = 48; $o <= 57; $o++) { + $u[] = dechex($o); + } + $chrs = ['-', '.', '_', '~']; + foreach ($chrs as $chr) { + $u[] = dechex(ord($chr)); + } + $url['path'] = preg_replace_callback( + array_map( + create_function('$str', 'return "/%" . strtoupper($str) . "/x";'), + $u + ), + create_function('$matches', 'return chr(hexdec($matches[0]));'), + $url['path'] + ); + // Remove directory index + $defaultIndexes = ["/default\.aspx/" => 'default.aspx/', "/default\.asp/" => 'default.asp/', + "/index\.html/" => 'index.html/', "/index\.htm/" => 'index.htm/', + "/default\.html/" => 'default.html/', "/default\.htm/" => 'default.htm/', + "/index\.php/" => 'index.php/', "/index\.jsp/" => 'index.jsp/', ]; + foreach ($defaultIndexes as $index => $strip) { + if (preg_match($index, $url['path'])) { + $url['path'] = str_replace($strip, '', $url['path']); + } + } + // here we only want to drop a slash for the root domain + // e.g. http://example.com/ -> http://example.com + // but http://example.com/path/ -/-> http://example.com/path + if ($url['path'] == '/') { + unset($url['path']); + } + + /** + * Path segment normalization, http://www.apps.ietf.org/rfc/rfc3986.html#sec-5.2.4 + * Heavily rewritten version of removeDotSegments() in Glen Scott's url-normalizer. + */ + $new_path = ''; + while (! empty($url['path'])) { + if (preg_match('!^(\.\./|\./)!x', $url['path'])) { + $url['path'] = preg_replace('!^(\.\./|\./)!x', '', $url['path']); + } elseif (preg_match('!^(/\./)!x', $url['path'], $matches) + || preg_match('!^(/\.)$!x', $url['path'], $matches)) { + $url['path'] = preg_replace('!^' . $matches[1] . '!', '/', $url['path']); + } elseif (preg_match('!^(/\.\./|/\.\.)!x', $url['path'], $matches)) { + $url['path'] = preg_replace('!^' . preg_quote($matches[1], '!') . '!x', '/', $url['path']); + $new_path = preg_replace('!/([^/]+)$!x', '', $new_path); + } elseif (preg_match('!^(\.|\.\.)$!x', $url['path'])) { + $url['path'] = preg_replace('!^(\.|\.\.)$!x', '', $url['path']); + } else { + if (preg_match('!(/*[^/]*)!x', $url['path'], $matches)) { + $first_path_segment = $matches[1]; + $url['path'] = preg_replace('/^' . preg_quote($first_path_segment, '/') . '/', '', $url['path'], 1); + $new_path .= $first_path_segment; + } + } + } + $newUrl .= $new_path; + } + + if (isset($url['fragment'])) { + unset($url['fragment']); + } + + // Sort GET params alphabetically, not because the RFC requires it but because it's cool! + if (isset($url['query'])) { + if (preg_match('/&/', $url['query'])) { + $s = explode('&', $url['query']); + $url['query'] = ''; + sort($s); + foreach ($s as $z) { + $url['query'] .= "{$z}&"; + } + $url['query'] = preg_replace('/&\Z/', '', $url['query']); + } + $newUrl .= "?{$url['query']}"; + } + + return $newUrl; + } +} + +// sourced from https://stackoverflow.com/a/9776726 +if (! function_exists('prettyPrintJson')) { + function prettyPrintJson(string $json): string + { + $result = ''; + $level = 0; + $in_quotes = false; + $in_escape = false; + $ends_line_level = null; + $json_length = strlen($json); + + for ($i = 0; $i < $json_length; $i++) { + $char = $json[$i]; + $new_line_level = null; + $post = ''; + if ($ends_line_level !== null) { + $new_line_level = $ends_line_level; + $ends_line_level = null; + } + if ($in_escape) { + $in_escape = false; + } elseif ($char === '"') { + $in_quotes = ! $in_quotes; + } elseif (! $in_quotes) { + switch ($char) { + case '}': + case ']': + $level--; + $ends_line_level = null; + $new_line_level = $level; + break; + case '{': + case '[': + $level++; + //no break + case ',': + $ends_line_level = $level; + break; + case ':': + $post = ' '; + break; + case ' ': + case "\t": + case "\n": + case "\r": + $char = ''; + $ends_line_level = $new_line_level; + $new_line_level = null; + break; + } + } elseif ($char === '\\') { + $in_escape = true; + } + if ($new_line_level !== null) { + $result .= "\n".str_repeat("\t", $new_line_level); + } + $result .= $char.$post; + } + + return str_replace("\t", ' ', $result); + } +} diff --git a/package.json b/package.json index e071c0af..9af1ac7b 100644 --- a/package.json +++ b/package.json @@ -6,15 +6,15 @@ "license": "CC0-1.0", "dependencies": { "alertify.js": "^1.0.12", - "mapbox-gl": "^0.34.0", + "mapbox-gl": "0.37.0", "marked": "^0.3.6", - "normalize.css": "^5.0.0", + "normalize.css": "7.0.0", "webStorage": "^1.2.2" }, "devDependencies": { "babel-cli": "^6.18.0", "babel-core": "^6.21.0", - "babel-loader": "^6.2.10", + "babel-loader": "7.0.0", "babel-preset-env": "^1.2.2", "babel-preset-es2015": "^6.18.0", "babel-preset-latest": "^6.16.0", diff --git a/routes/web.php b/routes/web.php index ceaf1fb4..fe462895 100644 --- a/routes/web.php +++ b/routes/web.php @@ -89,21 +89,19 @@ Route::group(['domain' => config('url.longurl')], function () { Route::get('blog/{year?}/{month?}', 'ArticlesController@index'); Route::get('blog/{year}/{month}/{slug}', 'ArticlesController@show'); - //micropub new notes page - //this needs to be first so `notes/new` doesn't match `notes/{id}` - - //Notes pages using NotesController Route::get('notes', 'NotesController@index'); Route::get('notes/{id}', 'NotesController@show'); Route::get('note/{id}', 'NotesController@redirect'); Route::get('notes/tagged/{tag}', 'NotesController@tagged'); - //indieauth + // IndieAuth Route::post('indieauth/start', 'IndieAuthController@start')->name('indieauth-start'); Route::get('indieauth/callback', 'IndieAuthController@callback')->name('indieauth-callback'); Route::get('logout', 'IndieAuthController@logout')->name('indieauth-logout'); - Route::post('api/token', 'IndieAuthController@tokenEndpoint'); //hmmm? + + // Token Endpoint + Route::post('api/token', 'TokenEndpointController@create'); // Micropub Client Route::get('micropub/create', 'MicropubClientController@create')->name('micropub-client'); @@ -118,7 +116,7 @@ Route::group(['domain' => config('url.longurl')], function () { Route::post('micropub/media', 'MicropubClientController@processMedia')->name('process-media'); Route::get('micropub/media/clearlinks', 'MicropubClientController@clearLinks'); - // Micropub Endpoint + // Micropub Endpoints Route::get('api/post', 'MicropubController@get')->middleware('micropub.token'); Route::post('api/post', 'MicropubController@post')->middleware('micropub.token'); Route::post('api/media', 'MicropubController@media')->middleware('micropub.token')->name('media-endpoint'); diff --git a/tests/Feature/MicropubControllerTest.php b/tests/Feature/MicropubControllerTest.php index d91a627b..f1fd39a7 100644 --- a/tests/Feature/MicropubControllerTest.php +++ b/tests/Feature/MicropubControllerTest.php @@ -231,9 +231,9 @@ class MicropubControllerTest extends TestCase $response ->assertJson([ 'response' => 'error', - 'error' => 'invalid_token' + 'error' => 'insufficient_scope' ]) - ->assertStatus(400); + ->assertStatus(401); } public function test_micropub_request_with_json_syntax_creates_new_place() @@ -276,6 +276,47 @@ class MicropubControllerTest extends TestCase ->assertStatus(201); } + public function test_micropub_request_with_json_syntax_update_replace_post() + { + $response = $this->json( + 'POST', + '/api/post', + [ + 'action' => 'update', + 'url' => config('app.url') . '/notes/A', + 'replace' => [ + 'content' => ['replaced content'], + ], + ], + ['HTTP_Authorization' => 'Bearer ' . $this->getToken()] + ); + $response + ->assertJson(['response' => 'updated']) + ->assertStatus(200); + } + + public function test_micropub_request_with_json_syntax_update_add_post() + { + $response = $this->json( + 'POST', + '/api/post', + [ + 'action' => 'update', + 'url' => config('app.url') . '/notes/A', + 'add' => [ + 'syndication' => ['https://www.swarmapp.com/checkin/123'], + ], + ], + ['HTTP_Authorization' => 'Bearer ' . $this->getToken()] + ); + $response + ->assertJson(['response' => 'updated']) + ->assertStatus(200); + $this->assertDatabaseHas('notes', [ + 'swarm_url' => 'https://www.swarmapp.com/checkin/123' + ]); + } + /** * Generate a valid token to be used in the tests. * diff --git a/tests/Feature/SwarmTest.php b/tests/Feature/SwarmTest.php new file mode 100644 index 00000000..8ae48974 --- /dev/null +++ b/tests/Feature/SwarmTest.php @@ -0,0 +1,69 @@ +json( + 'POST', + 'api/post', + [ + 'type' => ['h-entry'], + 'properties' => [ + 'published' => [\Carbon\Carbon::now()->toDateTimeString()], + 'syndication' => ['https://www.swarmapp.com/checkin/abc'], + 'content' => [[ + 'value' => 'My first #checkin using Example Product', + 'html' => 'My first #checkin using Example Product', + ]], + 'checkin' => [[ + 'type' => ['h-card'], + 'properties' => [ + 'name' => ['Awesome Venue'], + 'url' => ['https://foursquare.com/v/123456'], + 'latitude' => ['1.23'], + 'longitude' => ['4.56'], + ], + ]], + ], + ], + ['HTTP_Authorization' => 'Bearer ' . $this->getToken()] + ); + $response + ->assertStatus(201) + ->assertJson(['response' => 'created']); + $this->assertDatabaseHas('places', [ + 'foursquare' => 'https://foursquare.com/v/123456' + ]); + } + + /** + * Generate a valid token to be used in the tests. + * + * @return Lcobucci\JWT\Token\Plain $token + */ + private function getToken() + { + $signer = new Sha256(); + $token = (new Builder()) + ->set('client_id', 'https://ownyourswarm.p3k.io') + ->set('me', 'https://jonnybarnes.localhost') + ->set('scope', 'create update') + ->set('issued_at', time()) + ->sign($signer, env('APP_KEY')) + ->getToken(); + + return $token; + } +} diff --git a/tests/Feature/TokenEndpointTest.php b/tests/Feature/TokenEndpointTest.php new file mode 100644 index 00000000..964c9211 --- /dev/null +++ b/tests/Feature/TokenEndpointTest.php @@ -0,0 +1,38 @@ +shouldReceive('discoverAuthorizationEndpoint') + ->with(normalize_url(config('app.url'))) + ->once() + ->andReturn('https://indieauth.com/auth'); + $mockClient->shouldReceive('verifyIndieAuthCode') + ->andReturn([ + 'me' => config('app.url'), + 'scope' => 'create update', + ]); + $this->app->instance(Client::class, $mockClient); + $response = $this->post('/api/token', [ + 'me' => config('app.url'), + 'code' => 'abc123', + 'redirect_uri' => route('indieauth-callback'), + 'client_id' => route('micropub-client'), + 'state' => mt_rand(1000, 10000), + ]); + parse_str($response->content(), $output); + $this->assertEquals(config('app.url'), $output['me']); + $this->assertTrue(array_key_exists('access_token', $output)); + } +} diff --git a/tests/Unit/HelpersTest.php b/tests/Unit/HelpersTest.php new file mode 100644 index 00000000..9b31e4f3 --- /dev/null +++ b/tests/Unit/HelpersTest.php @@ -0,0 +1,69 @@ +assertEquals(normalize_url(normalize_url($input)), normalize_url($input)); + } + + /** + * @dataProvider urlProvider + */ + public function test_normalize_url($input, $output) + { + $this->assertEquals($output, normalize_url($input)); + } + + public function urlProvider() + { + return [ + ['https://example.org/', 'https://example.org'], + ['https://example.org:443/', 'https://example.org'], + ['http://www.foo.bar/index.php/', 'http://www.foo.bar'], + ['https://example.org/?foo=bar&baz=true', 'https://example.org?baz=true&foo=bar'], + ]; + } + + public function test_pretty_print_json() + { + $json = <<assertEquals($expected, prettyPrintJson($json)); + } +} diff --git a/tinker.config.php b/tinker.config.php new file mode 100644 index 00000000..63111c79 --- /dev/null +++ b/tinker.config.php @@ -0,0 +1,38 @@ +files()->name('*.php')->in(base_path().'/app'); + foreach ($finder as $file) { + $namespace = 'App\\'; + if ($relativePath = $file->getRelativePath()) { + $namespace .= strtr($relativePath, '/', '\\') . '\\'; + } + $class = $namespace . $file->getBasename('.php'); + try { + $r = new \ReflectionClass($class); + if ($r->isSubclassOf('Illuminate\\Database\\Eloquent\\Model')) { + class_alias($class, $file->getBasename('.php')); + } + } catch (Exception $e) { + // + } + } + } +} + +aliasModels(); + +return [ + 'startupMessage' => 'Using local config file (tinker.config.php)', + + 'commands' => [ + // new \App\Tinker\TestCommand, + ], +]; diff --git a/yarn.lock b/yarn.lock index c4aa7ce1..a7415b99 100644 --- a/yarn.lock +++ b/yarn.lock @@ -48,17 +48,21 @@ acorn@^3.0.4, acorn@^3.1.0, acorn@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a" -acorn@^4.0.0, acorn@^4.0.3, acorn@^4.0.4: +acorn@^4.0.0, acorn@^4.0.3: version "4.0.11" resolved "https://registry.yarnpkg.com/acorn/-/acorn-4.0.11.tgz#edcda3bd937e7556410d42ed5860f67399c794c0" +acorn@^5.0.0: + version "5.0.3" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.0.3.tgz#c460df08491463f028ccb82eab3730bf01087b3d" + ajv-keywords@^1.1.1: version "1.5.1" resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-1.5.1.tgz#314dd0a4b3368fad3dfcdc54ede6171b886daf3c" ajv@^4.7.0, ajv@^4.9.1: - version "4.11.6" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-4.11.6.tgz#947e93049790942b2a2d60a8289b28924d39f987" + version "4.11.8" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-4.11.8.tgz#82ffb02b29e662ae53bdc20af15947706739c536" dependencies: co "^4.6.0" json-stable-stringify "^1.0.1" @@ -117,11 +121,11 @@ archy@^1.0.0: resolved "https://registry.yarnpkg.com/archy/-/archy-1.0.0.tgz#f9c8c13757cc1dd7bc379ac77b2c62a5c2868c40" are-we-there-yet@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.2.tgz#80e470e95a084794fe1899262c5667c6e88de1b3" + version "1.1.4" + resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz#bb5dca382bb94f05e15194373d16fd3ba1ca110d" dependencies: delegates "^1.0.0" - readable-stream "^2.0.0 || ^1.1.13" + readable-stream "^2.0.6" argparse@^1.0.7: version "1.0.9" @@ -136,8 +140,8 @@ arr-diff@^2.0.0: arr-flatten "^1.0.1" arr-flatten@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.0.1.tgz#e5ffe54d45e19f32f216e91eb99c8ce892bb604b" + version "1.0.3" + resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.0.3.tgz#a274ed85ac08849b6bd7847c4580745dc51adfb1" array-unique@^0.2.1: version "0.2.1" @@ -182,8 +186,8 @@ async-each@^1.0.0: resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d" async@^2.1.2: - version "2.3.0" - resolved "https://registry.yarnpkg.com/async/-/async-2.3.0.tgz#1013d1051047dd320fe24e494d5c66ecaf6147d9" + version "2.4.0" + resolved "https://registry.yarnpkg.com/async/-/async-2.4.0.tgz#4990200f18ea5b837c2cc4f8c031a6985c385611" dependencies: lodash "^4.14.0" @@ -374,14 +378,13 @@ babel-helpers@^6.24.1: babel-runtime "^6.22.0" babel-template "^6.24.1" -babel-loader@^6.2.10: - version "6.4.1" - resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-6.4.1.tgz#0b34112d5b0748a8dcdbf51acf6f9bd42d50b8ca" +babel-loader@7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-7.0.0.tgz#2e43a66bee1fff4470533d0402c8a4532fafbaf7" dependencies: find-cache-dir "^0.1.1" - loader-utils "^0.2.16" + loader-utils "^1.0.2" mkdirp "^0.5.1" - object-assign "^4.0.1" babel-messages@^6.23.0: version "6.23.0" @@ -613,8 +616,8 @@ babel-polyfill@^6.23.0: regenerator-runtime "^0.10.0" babel-preset-env@^1.2.2: - version "1.3.3" - resolved "https://registry.yarnpkg.com/babel-preset-env/-/babel-preset-env-1.3.3.tgz#5913407784e3d98de2aa814a3ef9059722b34e0b" + version "1.4.0" + resolved "https://registry.yarnpkg.com/babel-preset-env/-/babel-preset-env-1.4.0.tgz#c8e02a3bcc7792f23cded68e0355b9d4c28f0f7a" dependencies: babel-plugin-check-es2015-constants "^6.22.0" babel-plugin-syntax-trailing-function-commas "^6.22.0" @@ -749,8 +752,8 @@ babel-types@^6.19.0, babel-types@^6.24.1: to-fast-properties "^1.0.1" babylon@^6.11.0, babylon@^6.15.0: - version "6.16.1" - resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.16.1.tgz#30c5a22f481978a9e7f8cdfdf496b11d94b404d3" + version "6.17.1" + resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.17.1.tgz#17f14fddf361b695981fe679385e4f1c01ebd86f" balanced-match@^0.4.1: version "0.4.2" @@ -778,6 +781,10 @@ binary-extensions@^1.0.0: version "1.8.0" resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.8.0.tgz#48ec8d16df4377eae5fa5884682480af4d95c774" +bindings@1.2.x: + version "1.2.1" + resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.2.1.tgz#14ad6113812d2d37d72e67b4cacb4bb726505f11" + block-stream@*: version "0.0.9" resolved "https://registry.yarnpkg.com/block-stream/-/block-stream-0.0.9.tgz#13ebfe778a03205cfe03751481ebb4b3300c126a" @@ -812,7 +819,7 @@ boxen@^0.3.1: string-width "^1.0.1" widest-line "^1.0.0" -brace-expansion@^1.0.0: +brace-expansion@^1.0.0, brace-expansion@^1.1.7: version "1.1.7" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.7.tgz#3effc3c50e000531fb720eaff80f0ae8ef23cf59" dependencies: @@ -967,8 +974,8 @@ camelcase@^3.0.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a" caniuse-db@^1.0.30000639: - version "1.0.30000650" - resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000650.tgz#615f564d367533d32b82d72ada09661e75386bab" + version "1.0.30000666" + resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000666.tgz#951ed9f3d3bfaa08a06dafbb5089ab07cce6ab90" capture-stack-trace@^1.0.0: version "1.0.0" @@ -996,8 +1003,8 @@ chalk@^1.0.0, chalk@^1.1.0, chalk@^1.1.1, chalk@^1.1.3: supports-color "^2.0.0" chokidar@^1.4.3, chokidar@^1.6.1: - version "1.6.1" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-1.6.1.tgz#2f4447ab5e96e50fb3d789fd90d4c72e0e4c70c2" + version "1.7.0" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-1.7.0.tgz#798e689778151c8076b4b360e5edd28cda2bb468" dependencies: anymatch "^1.3.0" async-each "^1.0.0" @@ -1010,7 +1017,7 @@ chokidar@^1.4.3, chokidar@^1.6.1: optionalDependencies: fsevents "^1.0.0" -cipher-base@^1.0.0, cipher-base@^1.0.1: +cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.3.tgz#eeabf194419ce900da3018c207d212f2a6df0a07" dependencies: @@ -1095,7 +1102,7 @@ concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" -concat-stream@^1.4.7: +concat-stream@^1.4.7, concat-stream@~1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.0.tgz#0aac662fd52be78964d5532f694784e70110acf7" dependencies: @@ -1109,14 +1116,6 @@ concat-stream@~1.2.1: dependencies: bops "0.0.6" -concat-stream@~1.4.5: - version "1.4.10" - resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.4.10.tgz#acc3bbf5602cb8cc980c6ac840fa7d8603e3ef36" - dependencies: - inherits "~2.0.1" - readable-stream "~1.1.9" - typedarray "~0.0.5" - configstore@^1.0.0, configstore@^1.2.0: version "1.4.0" resolved "https://registry.yarnpkg.com/configstore/-/configstore-1.4.0.tgz#c35781d0501d268c25c54b8b17f6240e8a4fb021" @@ -1196,21 +1195,25 @@ create-error-class@^3.0.1: dependencies: capture-stack-trace "^1.0.0" -create-hash@^1.1.0, create-hash@^1.1.1: - version "1.1.2" - resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.1.2.tgz#51210062d7bb7479f6c65bb41a92208b1d61abad" +create-hash@^1.1.0, create-hash@^1.1.1, create-hash@^1.1.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.1.3.tgz#606042ac8b9262750f483caddab0f5819172d8fd" dependencies: cipher-base "^1.0.1" inherits "^2.0.1" - ripemd160 "^1.0.0" - sha.js "^2.3.6" + ripemd160 "^2.0.0" + sha.js "^2.4.0" -create-hmac@^1.1.0, create-hmac@^1.1.2: - version "1.1.4" - resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.4.tgz#d3fb4ba253eb8b3f56e39ea2fbcb8af747bd3170" +create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4: + version "1.1.6" + resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.6.tgz#acb9e221a4e17bdb076e90657c42b93e3726cf06" dependencies: + cipher-base "^1.0.3" create-hash "^1.1.0" inherits "^2.0.1" + ripemd160 "^2.0.0" + safe-buffer "^5.0.1" + sha.js "^2.4.8" cross-spawn@^5.0.1: version "5.1.0" @@ -1248,18 +1251,18 @@ dashdash@^1.12.0: assert-plus "^1.0.0" date-fns@^1.27.2: - version "1.28.2" - resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-1.28.2.tgz#19e4192d68875c0bf7c9537e3f296a8ec64853ef" + version "1.28.4" + resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-1.28.4.tgz#7938aec34ba31fc8bd134d2344bc2e0bbfd95165" date-now@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b" debug@^2.1.1, debug@^2.2.0: - version "2.6.3" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.3.tgz#0f7eb8c30965ec08c72accfa0130c8b79984141d" + version "2.6.6" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.6.tgz#a9fa6fbe9ca43cf1e79f73b75c0189cbb7d6db5a" dependencies: - ms "0.7.2" + ms "0.7.3" decamelize@^1.0.0, decamelize@^1.1.1: version "1.2.0" @@ -1270,8 +1273,8 @@ deep-equal@^1.0.0: resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5" deep-extend@~0.4.0: - version "0.4.1" - resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.4.1.tgz#efe4113d08085f4e6f9687759810f807469e2253" + version "0.4.2" + resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.4.2.tgz#48b699c27e334bf89f10892be432f6e4c7d34a7f" deep-is@~0.1.3: version "0.1.3" @@ -1348,8 +1351,8 @@ ecc-jsbn@~0.1.1: jsbn "~0.1.0" electron-to-chromium@^1.2.7: - version "1.3.3" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.3.tgz#651eb63fe89f39db70ffc8dbd5d9b66958bc6a0e" + version "1.3.10" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.10.tgz#63d62b785471f0d8dda85199d64579de8a449f08" elegant-spinner@^1.0.1: version "1.0.1" @@ -1521,8 +1524,8 @@ expand-range@^1.8.1: fill-range "^2.1.0" extend@~3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.0.tgz#5a474353b9f3353ddd8176dfd37b91c83a46f1d4" + version "3.0.1" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444" extglob@^0.3.1: version "0.3.2" @@ -1555,8 +1558,8 @@ figures@^1.3.5, figures@^1.7.0: object-assign "^4.1.0" filename-regex@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.0.tgz#996e3e80479b98b9897f15a8a58b3d084e926775" + version "2.0.1" + resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.1.tgz#c1c4b9bee3e09725ddb106b75c1e301fe2f18b26" fill-range@^2.1.0: version "2.2.3" @@ -1588,8 +1591,8 @@ find-up@^1.0.0: pinkie-promise "^2.0.0" flow-remove-types@^1.1.2: - version "1.2.0" - resolved "https://registry.yarnpkg.com/flow-remove-types/-/flow-remove-types-1.2.0.tgz#c285516eabba72177a1b10bfb58f6ffb675a8877" + version "1.2.1" + resolved "https://registry.yarnpkg.com/flow-remove-types/-/flow-remove-types-1.2.1.tgz#58e261bf8b842bd234c86cafb982a1213aff0edb" dependencies: babylon "^6.15.0" vlq "^0.2.1" @@ -1656,9 +1659,9 @@ function-bind@^1.0.2: version "1.1.0" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.0.tgz#16176714c801798e4e8f2cf7f7529467bb4a5771" -gauge@~2.7.1: - version "2.7.3" - resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.3.tgz#1c23855f962f17b3ad3d0dc7443f304542edfe09" +gauge@~2.7.3: + version "2.7.4" + resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" dependencies: aproba "^1.0.3" console-control-strings "^1.0.0" @@ -1696,8 +1699,8 @@ get-stream@^3.0.0: resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" getpass@^0.1.1: - version "0.1.6" - resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.6.tgz#283ffd9fc1256840875311c1b60e8c40187110e6" + version "0.1.7" + resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" dependencies: assert-plus "^1.0.0" @@ -1817,6 +1820,12 @@ hasbin@^1.2.3: dependencies: async "~1.5" +hash-base@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-2.0.2.tgz#66ea1d856db4e8a5470cadf6fce23ae5244ef2e1" + dependencies: + inherits "^2.0.1" + hash.js@^1.0.0, hash.js@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.0.3.tgz#1332ff00156c0a0ffdd8236013d07b77a0451573" @@ -1852,8 +1861,8 @@ home-or-tmp@^2.0.0: os-tmpdir "^1.0.1" hosted-git-info@^2.1.4: - version "2.4.1" - resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.4.1.tgz#4b0445e41c004a8bd1337773a4ff790ca40318c8" + version "2.4.2" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.4.2.tgz#0076b9f46a270506ddbaaea56496897460612a67" http-signature@~1.1.0: version "1.1.1" @@ -1931,8 +1940,8 @@ inquirer@1.0.3: through "^2.3.6" interpret@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.0.2.tgz#f4f623f0bb7122f15f5717c8e254b8161b5c5b2d" + version "1.0.3" + resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.0.3.tgz#cbc35c62eeee73f19ab7b10a801511401afc0f90" invariant@^2.2.0, invariant@^2.2.2: version "2.2.2" @@ -1954,7 +1963,7 @@ is-binary-path@^1.0.0: dependencies: binary-extensions "^1.0.0" -is-buffer@^1.0.2: +is-buffer@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.5.tgz#1f3b26ef613b214b88cbca23cc6c01d87961eecc" @@ -1994,6 +2003,10 @@ is-fullwidth-code-point@^1.0.0: dependencies: number-is-nan "^1.0.0" +is-fullwidth-code-point@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + is-glob@^2.0.0, is-glob@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-2.0.1.tgz#d096f926a3ded5600f3fdfd91198cb0888c2d863" @@ -2079,8 +2092,8 @@ js-tokens@^3.0.0: resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.1.tgz#08e9f132484a2c45a30907e9dc4d5567b7f114d7" js-yaml@^3.4.3, js-yaml@^3.5.3: - version "3.8.3" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.8.3.tgz#33a05ec481c850c8875929166fe1beb61c728766" + version "3.8.4" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.8.4.tgz#520b4564f86573ba96662af85a8cafa7b4b5a6f6" dependencies: argparse "^1.0.7" esprima "^3.1.1" @@ -2115,7 +2128,7 @@ json-stringify-safe@~5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" -json5@^0.5.0: +json5@^0.5.0, json5@^0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821" @@ -2137,10 +2150,10 @@ kdbush@^1.0.1: resolved "https://registry.yarnpkg.com/kdbush/-/kdbush-1.0.1.tgz#3cbd03e9dead9c0f6f66ccdb96450e5cecc640e0" kind-of@^3.0.2: - version "3.1.0" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.1.0.tgz#475d698a5e49ff5e53d14e3e732429dc8bf4cf47" + version "3.2.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.0.tgz#b58abe4d5c044ad33726a8c1525b48cf891bff07" dependencies: - is-buffer "^1.0.2" + is-buffer "^1.1.5" latest-version@^1.0.0: version "1.0.1" @@ -2172,13 +2185,13 @@ levn@~0.3.0: type-check "~0.3.2" lint-staged@^3.2.1: - version "3.4.0" - resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-3.4.0.tgz#52fa85dfc92bb1c6fe8ad0d0d98ca13924e03e4b" + version "3.4.1" + resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-3.4.1.tgz#96cd1cf7a1ac92d81662643c37d1cca28b91b046" dependencies: app-root-path "^2.0.0" cosmiconfig "^1.1.0" execa "^0.6.0" - listr "^0.11.0" + listr "^0.12.0" minimatch "^3.0.0" npm-which "^3.0.1" staged-git-files "0.0.4" @@ -2209,9 +2222,9 @@ listr-verbose-renderer@^0.4.0: date-fns "^1.27.2" figures "^1.7.0" -listr@^0.11.0: - version "0.11.0" - resolved "https://registry.yarnpkg.com/listr/-/listr-0.11.0.tgz#5e778bc23806ac3ab984ed75564458151f39b03e" +listr@^0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/listr/-/listr-0.12.0.tgz#6bce2c0f5603fa49580ea17cd6a00cc0e5fa451a" dependencies: chalk "^1.1.3" cli-truncate "^0.2.1" @@ -2225,6 +2238,7 @@ listr@^0.11.0: log-symbols "^1.0.2" log-update "^1.0.2" ora "^0.2.3" + p-map "^1.1.1" rxjs "^5.0.0-beta.11" stream-to-observable "^0.1.0" strip-ansi "^3.0.1" @@ -2252,6 +2266,14 @@ loader-utils@^0.2.16: json5 "^0.5.0" object-assign "^4.0.1" +loader-utils@^1.0.2: + version "1.1.0" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.1.0.tgz#c98aef488bcceda2ffb5e2de646d6a754429f5cd" + dependencies: + big.js "^3.1.3" + emojis-list "^2.0.0" + json5 "^0.5.0" + lodash.assign@^4.0.3, lodash.assign@^4.0.6: version "4.2.0" resolved "https://registry.yarnpkg.com/lodash.assign/-/lodash.assign-4.2.0.tgz#0d99f3ccd7a6d261d19bdaeb9245005d285808e7" @@ -2320,9 +2342,9 @@ mapbox-gl-supported@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/mapbox-gl-supported/-/mapbox-gl-supported-1.2.0.tgz#cbd34df894206cadda9a33c8d9a4609f26bb1989" -mapbox-gl@^0.34.0: - version "0.34.0" - resolved "https://registry.yarnpkg.com/mapbox-gl/-/mapbox-gl-0.34.0.tgz#f35effae360b8174b7466476a400cc1306a19713" +mapbox-gl@0.37.0: + version "0.37.0" + resolved "https://registry.yarnpkg.com/mapbox-gl/-/mapbox-gl-0.37.0.tgz#20103b0ae73a0e77fe9405439749318b87c04f10" dependencies: "@mapbox/gl-matrix" "^0.0.1" "@mapbox/shelf-pack" "^3.0.0" @@ -2377,6 +2399,13 @@ micromatch@^2.1.5: parse-glob "^3.0.4" regex-cache "^0.4.2" +microtime@^2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/microtime/-/microtime-2.1.3.tgz#0d1307f25da0ca7fde4ab791edc8c91dd7b4e3be" + dependencies: + bindings "1.2.x" + nan "2.6.x" + miller-rabin@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.0.tgz#4a62fb1d42933c05583982f4c716f6fb9e6c6d3d" @@ -2409,10 +2438,10 @@ minimatch@3.0.2: brace-expansion "^1.0.0" minimatch@^3.0.0, minimatch@^3.0.2: - version "3.0.3" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.3.tgz#2a4e4090b96b2db06a9d7df01055a62a77c9b774" + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" dependencies: - brace-expansion "^1.0.0" + brace-expansion "^1.1.7" minimist@0.0.5: version "0.0.5" @@ -2432,9 +2461,9 @@ minimist@^1.1.0, minimist@^1.1.3, minimist@^1.2.0: dependencies: minimist "0.0.8" -ms@0.7.2: - version "0.7.2" - resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.2.tgz#ae25cf2512b3885a1d95d7f037868d8431124765" +ms@0.7.3: + version "0.7.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.3.tgz#708155a5e44e33f5fd0fc53e81d0d40a91be1fff" multi-stage-sourcemap@^0.2.1: version "0.2.1" @@ -2446,9 +2475,9 @@ mute-stream@0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.6.tgz#48962b19e169fd1dfc240b3f1e7317627bbc47db" -nan@^2.3.0: - version "2.6.1" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.6.1.tgz#8c84f7b14c96b89f57fbc838012180ec8ca39a01" +nan@2.6.x, nan@^2.3.0: + version "2.6.2" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.6.2.tgz#e4ff34e6c95fdfb5aecc08de6596f43605a7db45" nconf@^0.7.2: version "0.7.2" @@ -2518,8 +2547,8 @@ nopt@^4.0.1: osenv "^0.1.4" normalize-package-data@^2.3.2: - version "2.3.6" - resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.3.6.tgz#498fa420c96401f787402ba21e600def9f981fff" + version "2.3.8" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.3.8.tgz#d819eda2a9dedbd1ffa563ea4071d936782295bb" dependencies: hosted-git-info "^2.1.4" is-builtin-module "^1.0.0" @@ -2532,9 +2561,9 @@ normalize-path@^2.0.1: dependencies: remove-trailing-separator "^1.0.1" -normalize.css@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/normalize.css/-/normalize.css-5.0.0.tgz#7cec875ce8178a5333c4de80b68ea9c18b9d7c37" +normalize.css@7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/normalize.css/-/normalize.css-7.0.0.tgz#abfb1dd82470674e0322b53ceb1aaf412938e4bf" npm-path@^2.0.2: version "2.0.3" @@ -2557,12 +2586,12 @@ npm-which@^3.0.1: which "^1.2.10" npmlog@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.0.2.tgz#d03950e0e78ce1527ba26d2a7592e9348ac3e75f" + version "4.1.0" + resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.0.tgz#dc59bee85f64f00ed424efb2af0783df25d1c0b5" dependencies: are-we-there-yet "~1.1.2" console-control-strings "~1.1.0" - gauge "~2.7.1" + gauge "~2.7.3" set-blocking "~2.0.0" number-is-nan@^1.0.0: @@ -2694,6 +2723,10 @@ p-finally@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" +p-map@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-1.1.1.tgz#05f5e4ae97a068371bc2a5cc86bfbdbc19c4ae7a" + package-json-versionify@^1.0.2: version "1.0.4" resolved "https://registry.yarnpkg.com/package-json-versionify/-/package-json-versionify-1.0.4.tgz#5860587a944873a6b7e6d26e8e51ffb22315bf17" @@ -2783,10 +2816,16 @@ pbf@^1.3.2: resolve-protobuf-schema "^2.0.0" pbkdf2@^3.0.3: - version "3.0.9" - resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.9.tgz#f2c4b25a600058b3c3773c086c37dbbee1ffe693" + version "3.0.11" + resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.11.tgz#791b7414e50c848438976e12ea2651003037ca6b" dependencies: - create-hmac "^1.1.2" + create-hash "^1.1.2" + create-hmac "^1.1.4" + ripemd160 "^2.0.1" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + optionalDependencies: + microtime "^2.1.3" performance-now@^0.2.0: version "0.2.0" @@ -2845,8 +2884,8 @@ process-nextick-args@~1.0.6: resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3" process@^0.11.0: - version "0.11.9" - resolved "https://registry.yarnpkg.com/process/-/process-0.11.9.tgz#7bd5ad21aa6253e7da8682264f1e11d11c0318c1" + version "0.11.10" + resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" "promise@>=3.2 <8": version "7.1.1" @@ -2876,11 +2915,11 @@ public-encrypt@^4.0.0: parse-asn1 "^5.0.0" randombytes "^2.0.1" -punycode@1.3.2: +punycode@1.3.2, punycode@^1.2.4: version "1.3.2" resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" -punycode@^1.2.4, punycode@^1.4.1: +punycode@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" @@ -2957,7 +2996,7 @@ read-pkg@^1.0.0: normalize-package-data "^2.3.2" path-type "^1.0.0" -readable-stream@^2.0.0, "readable-stream@^2.0.0 || ^1.1.13", readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.1.4, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.2.6: +readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.1.4, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.2.6: version "2.2.9" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.2.9.tgz#cf78ec6f4a6d1eb43d26488cac97f042e74b7fc8" dependencies: @@ -3001,8 +3040,8 @@ regenerate@^1.2.1: resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.3.2.tgz#d1941c67bad437e1be76433add5b385f95b19260" regenerator-runtime@^0.10.0: - version "0.10.3" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.10.3.tgz#8c4367a904b51ea62a908ac310bf99ff90a82a3e" + version "0.10.5" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz#336c3efc1220adcedda2c9fab67b5a7955a33658" regenerator-transform@0.9.11: version "0.9.11" @@ -3028,10 +3067,11 @@ regexpu-core@^2.0.0: regjsparser "^0.1.4" registry-auth-token@^3.0.1: - version "3.1.2" - resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-3.1.2.tgz#1b9e51a185c930da34a9894b12a52ea998f1adaf" + version "3.3.1" + resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-3.3.1.tgz#fb0d3289ee0d9ada2cbb52af5dfe66cb070d3006" dependencies: rc "^1.1.6" + safe-buffer "^5.0.1" registry-url@^3.0.0, registry-url@^3.0.3: version "3.1.0" @@ -3119,8 +3159,8 @@ resolve-protobuf-schema@^2.0.0: protocol-buffers-schema "^2.0.2" resolve@^1.1.5: - version "1.3.2" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.3.2.tgz#1f0442c9e0cbb8136e87b9305f932f46c7f28235" + version "1.3.3" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.3.3.tgz#655907c3469a8680dc2de3a275a8fdd69691f0e5" dependencies: path-parse "^1.0.5" @@ -3143,9 +3183,12 @@ rimraf@2, rimraf@^2.5.1, rimraf@^2.6.1: dependencies: glob "^7.0.5" -ripemd160@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-1.0.1.tgz#93a4bbd4942bc574b69a8fa57c71de10ecca7d6e" +ripemd160@^2.0.0, ripemd160@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.1.tgz#0f4584295c53a3628af7e6d79aca21ce57d1c6e7" + dependencies: + hash-base "^2.0.0" + inherits "^2.0.1" run-async@^2.2.0: version "2.3.0" @@ -3158,8 +3201,8 @@ rx@^4.1.0: resolved "https://registry.yarnpkg.com/rx/-/rx-4.1.0.tgz#a5f13ff79ef3b740fe30aa803fb09f98805d4782" rxjs@^5.0.0-beta.11: - version "5.3.0" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-5.3.0.tgz#d88ccbdd46af290cbdb97d5d8055e52453fabe2d" + version "5.4.0" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-5.4.0.tgz#a7db14ab157f9d7aac6a56e655e7a3860d39bf26" dependencies: symbol-observable "^1.0.1" @@ -3189,7 +3232,7 @@ setimmediate@^1.0.4: version "1.0.5" resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" -sha.js@^2.3.6: +sha.js@^2.4.0, sha.js@^2.4.8: version "2.4.8" resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.8.tgz#37068c2c476b6baf402d14a49c67f597921f634f" dependencies: @@ -3309,8 +3352,8 @@ snyk-try-require@^1.1.1, snyk-try-require@^1.2.0: then-fs "^2.0.0" snyk@^1.14.3: - version "1.28.0" - resolved "https://registry.yarnpkg.com/snyk/-/snyk-1.28.0.tgz#f805cc5917d57203aa7fe055650e70d1b31daba2" + version "1.30.0" + resolved "https://registry.yarnpkg.com/snyk/-/snyk-1.30.0.tgz#a323809ea477d6aff0e325f5995cb491c0d7ca3d" dependencies: abbrev "^1.0.7" ansi-escapes "^1.3.0" @@ -3340,12 +3383,12 @@ snyk@^1.14.3: uuid "^3.0.1" source-list-map@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-1.1.1.tgz#1a33ac210ca144d1e561f906ebccab5669ff4cb4" + version "1.1.2" + resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-1.1.2.tgz#9889019d1024cce55cdc069498337ef6186a11a1" source-map-support@^0.4.2: - version "0.4.14" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.14.tgz#9d4463772598b86271b4f523f6c1f4e02a7d6aef" + version "0.4.15" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.15.tgz#03202df65c06d2bd8c7ec2362a193056fef8d3b1" dependencies: source-map "^0.5.6" @@ -3391,8 +3434,8 @@ sprintf-js@~1.0.2: resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" sshpk@^1.7.0: - version "1.11.0" - resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.11.0.tgz#2d8d5ebb4a6fab28ffba37fa62a90f4a3ea59d77" + version "1.13.0" + resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.13.0.tgz#ff2a3e4fd04497555fed97b39a0fd82fafb3a33c" dependencies: asn1 "~0.2.3" assert-plus "^1.0.0" @@ -3416,10 +3459,10 @@ static-eval@~0.2.0: escodegen "~0.0.24" static-module@^1.1.0: - version "1.3.1" - resolved "https://registry.yarnpkg.com/static-module/-/static-module-1.3.1.tgz#79071d340e4419e4ab5ce87976a9eb67250c8493" + version "1.3.2" + resolved "https://registry.yarnpkg.com/static-module/-/static-module-1.3.2.tgz#329fb9f223a566266bda71843b7d932c767174f3" dependencies: - concat-stream "~1.4.5" + concat-stream "~1.6.0" duplexer2 "~0.0.2" escodegen "~1.3.2" falafel "^1.0.0" @@ -3439,8 +3482,8 @@ stream-browserify@^2.0.1: readable-stream "^2.0.2" stream-http@^2.3.1: - version "2.7.0" - resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.7.0.tgz#cec1f4e3b494bc4a81b451808970f8b20b4ed5f6" + version "2.7.1" + resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.7.1.tgz#546a51741ad5a6b07e9e31b0b10441a917df528a" dependencies: builtin-status-codes "^3.0.0" inherits "^2.0.1" @@ -3470,6 +3513,13 @@ string-width@^1.0.1, string-width@^1.0.2: is-fullwidth-code-point "^1.0.0" strip-ansi "^3.0.0" +string-width@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.0.0.tgz#635c5436cc72a6e0c387ceca278d4e2eec52687e" + dependencies: + is-fullwidth-code-point "^2.0.0" + strip-ansi "^3.0.0" + string_decoder@^0.10.25, string_decoder@~0.10.x: version "0.10.31" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" @@ -3607,8 +3657,8 @@ to-arraybuffer@^1.0.0: resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43" to-fast-properties@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.2.tgz#f3f5c0c3ba7299a7ef99427e44633257ade43320" + version "1.0.3" + resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47" to-utf8@0.0.1: version "0.0.1" @@ -3644,13 +3694,13 @@ type-check@~0.3.2: dependencies: prelude-ls "~1.1.2" -typedarray@^0.0.6, typedarray@~0.0.5: +typedarray@^0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" uglify-js@^2.8.5: - version "2.8.22" - resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.8.22.tgz#d54934778a8da14903fa29a326fb24c0ab51a1a0" + version "2.8.23" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.8.23.tgz#8230dd9783371232d62a7821e2cf9a817270a8a0" dependencies: source-map "~0.5.1" yargs "~3.10.0" @@ -3762,8 +3812,8 @@ uuid@^3.0.0, uuid@^3.0.1: resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.0.1.tgz#6544bba2dfda8c1cf17e629a3a305e2bb1fee6c1" v8flags@^2.0.10: - version "2.0.12" - resolved "https://registry.yarnpkg.com/v8flags/-/v8flags-2.0.12.tgz#73235d9f7176f8e8833fb286795445f7938d84e5" + version "2.1.1" + resolved "https://registry.yarnpkg.com/v8flags/-/v8flags-2.1.1.tgz#aab1a1fa30d45f88dd321148875ac02c0b55e5b4" dependencies: user-home "^1.1.1" @@ -3787,8 +3837,8 @@ verror@1.3.6: extsprintf "1.0.2" vlq@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/vlq/-/vlq-0.2.1.tgz#14439d711891e682535467f8587c5630e4222a6c" + version "0.2.2" + resolved "https://registry.yarnpkg.com/vlq/-/vlq-0.2.2.tgz#e316d5257b40b86bb43cb8d5fea5d7f54d6b0ca1" vm-browserify@0.0.4: version "0.0.4" @@ -3826,10 +3876,10 @@ webpack-sources@^0.2.3: source-map "~0.5.3" webpack@^2.2.0: - version "2.3.3" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-2.3.3.tgz#eecc083c18fb7bf958ea4f40b57a6640c5a0cc78" + version "2.5.1" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-2.5.1.tgz#61742f0cf8af555b87460a9cd8bba2f1e3ee2fce" dependencies: - acorn "^4.0.4" + acorn "^5.0.0" acorn-dynamic-import "^2.0.0" ajv "^4.7.0" ajv-keywords "^1.1.1" @@ -3837,6 +3887,7 @@ webpack@^2.2.0: enhanced-resolve "^3.0.0" interpret "^1.0.0" json-loader "^0.5.4" + json5 "^0.5.1" loader-runner "^2.3.0" loader-utils "^0.2.16" memory-fs "~0.4.1" @@ -3869,10 +3920,10 @@ which@1.2.x, which@^1.2.10, which@^1.2.9: isexe "^2.0.0" wide-align@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.0.tgz#40edde802a71fea1f070da3e62dcda2e7add96ad" + version "1.1.1" + resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.1.tgz#d2ea8aa2db2e66467e8b60cc3e897de3bc4429e6" dependencies: - string-width "^1.0.1" + string-width "^2.0.0" widest-line@^1.0.0: version "1.0.0" @@ -3918,8 +3969,8 @@ wrappy@1: resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" write-file-atomic@^1.1.2: - version "1.3.1" - resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-1.3.1.tgz#7d45ba32316328dd1ec7d90f60ebc0d845bb759a" + version "1.3.4" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-1.3.4.tgz#f807a4f0b1d9e913ae7a48112e6cc3af1991b45f" dependencies: graceful-fs "^4.1.11" imurmurhash "^0.1.4"