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"