diff --git a/LICENSE.txt b/LICENSE.md similarity index 91% rename from LICENSE.txt rename to LICENSE.md index b5c737ae..87cbc8f4 100644 --- a/LICENSE.txt +++ b/LICENSE.md @@ -1,7 +1,6 @@ The MIT License (MIT) -SwaggerBake - Delightfully tasty tool for your cakephp project. -Copyright (c) 2020, Chris Nizzardini +Copyright (c) Chris Nizzardini Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), diff --git a/README.md b/README.md index dc04ca36..4d7eada7 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,10 @@ # SwaggerBake plugin for CakePHP4 +[![Latest Version on Packagist](https://img.shields.io/packagist/v/cnizzardini/cakephp-swagger-bake.svg?style=flat-square)](https://packagist.org/packages/cnizzardini/cakephp-swagger-bake) +[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE.md) + A delightfully tasty tool for generating Swagger documentation with OpenApi 3.0.0 schema. This plugin automatically -builds your Swagger UI (v3.25) from your existing cake models and routes. A redoc option is also available. +builds your Swagger UI and ReDoc from your existing cake models and routes. - Creates paths from your [RESTful](https://book.cakephp.org/4/en/development/rest.html) routes. - Creates schema from your Entities and Tables. @@ -52,7 +55,7 @@ Using the above example you should now see your swagger documentation after brow ### Hot Reload Swagger JSON You can enable hot reloading. This setting re-generates swagger.json on each reload of Swagger UI. Simply set -`hotReload` equal to `true` in your `config/swagger_bake.php` file. +`hotReload` equal to `true` (using `Configure::read('debug')` is recommended) in your `config/swagger_bake.php` file. ## Automatic Documentation @@ -120,6 +123,20 @@ public function index() { } ``` +#### `@SwagDto` +Method level annotation for building query or form parameters from a DataTransferObject. DTOs are more than just a +best practice. Using them with SwaggerBake greatly reduces the amount of annotations you need to write. Consider +using a DTO in place of SwagQuery or SwagForm. SwagDto parses property doc blocks to build swagger query and +post parameters and should work with any DTO library. This has been tested with +[spatie/data-transfer-object](https://github.com/spatie/data-transfer-object). + +```php +/** + * @Swag\SwagDto(class="\App\My\Dto") + */ +public function index() {} +``` + #### `@SwagQuery` Method level annotation for adding query parameters. @@ -165,7 +182,7 @@ Method level annotation for hiding a controller action from swagger. ```php /** - * @Swag\SwagOperation(isVisible=false) + * @SwagOperation(isVisible=false) */ public function index() {} ``` @@ -185,7 +202,7 @@ Method level annotation for describing custom content in request body. ```php /** - * @Swag\SwagRequestBodyContent(refEntity="#/components/schemas/Lead", mimeType="application/json") + * @Swag\SwagRequestBodyContent(refEntity="#/components/schemas/Actor", mimeType="application/json") */ public function index() {} ``` @@ -195,8 +212,10 @@ Method level annotation for defining custom response schema. Leave refEntity emp ```php /** - * @Swag\SwagResponseSchema(refEntity="#/components/schemas/Lead", description="summary", httpCode=200) + * @Swag\SwagResponseSchema(refEntity="#/components/schemas/Actor", description="summary", httpCode=200) * @Swag\SwagResponseSchema(refEntity="", description="fatal error", httpCode=500) + * @Swag\SwagResponseSchema(refEntity="#/components/schemas/Actor", mimeType="application/xml") + * @Swag\SwagResponseSchema(refEntity="#/components/schemas/Actor", mimeType="application/json") */ public function index() {} ``` @@ -216,7 +235,7 @@ Class level annotation for exposing entities to Swagger UI. You can hide entiti ```php /** - * @Swag\SwagEntity(isVisible=true) + * @Swag\SwagEntity(isVisible=false) */ class Employee extends Entity { ``` @@ -302,7 +321,8 @@ bin/cake swagger models - DateTime fields named `created` and `modified` are automatically set to read only per Cake convention. - Table Validators: - Fields set to not allow empty will be marked as required in Swagger. -- SwaggerBake has been developed for application/json and has not been tested with application/xml. +- SwaggerBake has been developed primarily for application/json and application/x-www-form-urlencoded, but does have +some support for application/xml and *should* work with application/vnd.api+json. ## Supported Versions @@ -310,7 +330,7 @@ This is built for CakePHP 4.x only. | Version | Cake Version | Supported | Unit Tests | Notes | | ------------- | ------------- | ------------- | ------------- | ------------- | -| 1.* | 4.0.* | Yes | Yes | Currently supported | +| 1.* | 4.* | Yes | Yes | Currently supported | | cake-3.8 | 3.8.* | NO | Yes | See branch cake-3.8. Completely untested and unsupported | ## Common Issues diff --git a/assets/swagger.yml b/assets/swagger.yml index ad5d65dc..4ea0b834 100644 --- a/assets/swagger.yml +++ b/assets/swagger.yml @@ -24,3 +24,8 @@ components: message: type: string example: Internal Error + OperationResult: + type: object + properties: + result: + type: boolean diff --git a/assets/swagger_bake.php b/assets/swagger_bake.php index e9fb2412..9bc4c89a 100644 --- a/assets/swagger_bake.php +++ b/assets/swagger_bake.php @@ -1,21 +1,45 @@ schemas defined in your swagger.yml. + * Default: Exception. * - * @var array $namespaces: Can be used if your controllers or entities exist in non-standard namespace such as a plugin + * @var array[] $namespaces: Array of namespaces. Useful if your controllers or entities exist in non-standard + * namespace such as a plugin. This was mostly added to aid in unit testing, but there are cases where controllers + * may exist in a plugin namespace etc... + * Default: \App\\ */ return [ 'SwaggerBake' => [ @@ -26,6 +50,8 @@ 'hotReload' => \Cake\Core\Configure::read('debug'), /** optional configurations below: **/ /* + 'requestAccepts' => ['application/x-www-form-urlencoded'], + 'responseContentTypes' => ['application/json'], 'docType' => 'swagger', 'exceptionSchema' => 'Exception', 'namespaces' => [ diff --git a/src/Lib/Annotation/SwagDto.php b/src/Lib/Annotation/SwagDto.php new file mode 100644 index 00000000..6d659e36 --- /dev/null +++ b/src/Lib/Annotation/SwagDto.php @@ -0,0 +1,27 @@ +class = $values['class']; + } +} \ No newline at end of file diff --git a/src/Lib/Annotation/SwagEntityAttributeHandler.php b/src/Lib/Annotation/SwagEntityAttributeHandler.php index d9e8e35b..86a2c871 100644 --- a/src/Lib/Annotation/SwagEntityAttributeHandler.php +++ b/src/Lib/Annotation/SwagEntityAttributeHandler.php @@ -6,10 +6,13 @@ class SwagEntityAttributeHandler { + /** + * @param SwagEntityAttribute $annotation + * @return SchemaProperty + */ public function getSchemaProperty(SwagEntityAttribute $annotation) : SchemaProperty { - $schemaProperty = new SchemaProperty(); - $schemaProperty + return (new SchemaProperty()) ->setName($annotation->name) ->setDescription($annotation->description) ->setType($annotation->type) @@ -17,7 +20,5 @@ public function getSchemaProperty(SwagEntityAttribute $annotation) : SchemaPrope ->setWriteOnly($annotation->writeOnly) ->setRequired($annotation->required) ; - - return $schemaProperty; } } \ No newline at end of file diff --git a/src/Lib/Annotation/SwagFormHandler.php b/src/Lib/Annotation/SwagFormHandler.php index c9a11328..c69884a3 100644 --- a/src/Lib/Annotation/SwagFormHandler.php +++ b/src/Lib/Annotation/SwagFormHandler.php @@ -6,16 +6,17 @@ class SwagFormHandler { + /** + * @param SwagForm $annotation + * @return SchemaProperty + */ public function getSchemaProperty(SwagForm $annotation) : SchemaProperty { - $schemaProperty = new SchemaProperty(); - $schemaProperty + return (new SchemaProperty()) ->setDescription($annotation->description) ->setName($annotation->name) ->setType($annotation->type) ->setRequired($annotation->required) ; - - return $schemaProperty; } } \ No newline at end of file diff --git a/src/Lib/Annotation/SwagHeaderHandler.php b/src/Lib/Annotation/SwagHeaderHandler.php index 7a31b0a9..1eb2f44c 100644 --- a/src/Lib/Annotation/SwagHeaderHandler.php +++ b/src/Lib/Annotation/SwagHeaderHandler.php @@ -7,18 +7,20 @@ class SwagHeaderHandler { + /** + * @param SwagHeader $annotation + * @return Parameter + */ public function getHeaderParameters(SwagHeader $annotation) : Parameter { - $parameter = new Parameter(); - $parameter + return (new Parameter()) ->setName($annotation->name) ->setDescription($annotation->description) ->setAllowEmptyValue(false) ->setDeprecated(false) ->setRequired($annotation->required) ->setIn('header') - ->setSchema((new Schema())->setType($annotation->type)); - - return $parameter; + ->setSchema((new Schema())->setType($annotation->type)) + ; } } \ No newline at end of file diff --git a/src/Lib/Annotation/SwagPaginatorHandler.php b/src/Lib/Annotation/SwagPaginatorHandler.php index 0e305893..02a55fe7 100644 --- a/src/Lib/Annotation/SwagPaginatorHandler.php +++ b/src/Lib/Annotation/SwagPaginatorHandler.php @@ -7,6 +7,10 @@ class SwagPaginatorHandler { + /** + * @param SwagPaginator $annotation + * @return array + */ public function getQueryParameters(SwagPaginator $annotation) : array { $paginators = [ @@ -16,8 +20,7 @@ public function getQueryParameters(SwagPaginator $annotation) : array 'direction' => 'string' ]; - $parameter = new Parameter(); - $parameter + $parameter = (new Parameter()) ->setAllowEmptyValue(false) ->setDeprecated(false) ->setRequired(false) diff --git a/src/Lib/Annotation/SwagQueryHandler.php b/src/Lib/Annotation/SwagQueryHandler.php index b5eaf05f..2bfb6ce9 100644 --- a/src/Lib/Annotation/SwagQueryHandler.php +++ b/src/Lib/Annotation/SwagQueryHandler.php @@ -9,16 +9,14 @@ class SwagQueryHandler { public function getQueryParameter(SwagQuery $annotation) : Parameter { - $parameter = new Parameter(); - $parameter + return (new Parameter()) ->setName($annotation->name) ->setDescription($annotation->description) ->setAllowEmptyValue(false) ->setDeprecated(false) ->setRequired($annotation->required) ->setIn('query') - ->setSchema((new Schema())->setType($annotation->type)); - - return $parameter; + ->setSchema((new Schema())->setType($annotation->type)) + ; } } \ No newline at end of file diff --git a/src/Lib/Annotation/SwagRequestBodyContent.php b/src/Lib/Annotation/SwagRequestBodyContent.php index 9a2b30ba..bd9e342f 100644 --- a/src/Lib/Annotation/SwagRequestBodyContent.php +++ b/src/Lib/Annotation/SwagRequestBodyContent.php @@ -22,7 +22,6 @@ class SwagRequestBodyContent public function __construct(array $values) { - $values = array_merge(['refEntity' => '', 'mimeType' => 'text/plain'], $values); $this->refEntity = $values['refEntity']; $this->mimeType = $values['mimeType']; } diff --git a/src/Lib/Annotation/SwagRequestBodyContentHandler.php b/src/Lib/Annotation/SwagRequestBodyContentHandler.php index c3faedce..599c2f2d 100644 --- a/src/Lib/Annotation/SwagRequestBodyContentHandler.php +++ b/src/Lib/Annotation/SwagRequestBodyContentHandler.php @@ -6,6 +6,10 @@ class SwagRequestBodyContentHandler { + /** + * @param SwagRequestBodyContent $annotation + * @return Content|null + */ public function getContent(SwagRequestBodyContent $annotation) : ?Content { return (new Content()) diff --git a/src/Lib/Annotation/SwagRequestBodyHandler.php b/src/Lib/Annotation/SwagRequestBodyHandler.php index f7e87cfd..d86b97dd 100644 --- a/src/Lib/Annotation/SwagRequestBodyHandler.php +++ b/src/Lib/Annotation/SwagRequestBodyHandler.php @@ -6,6 +6,10 @@ class SwagRequestBodyHandler { + /** + * @param SwagRequestBody $annotation + * @return RequestBody + */ public function getResponse(SwagRequestBody $annotation) : RequestBody { return (new RequestBody()) diff --git a/src/Lib/Annotation/SwagResponseSchema.php b/src/Lib/Annotation/SwagResponseSchema.php index 5e705866..9fc37aef 100644 --- a/src/Lib/Annotation/SwagResponseSchema.php +++ b/src/Lib/Annotation/SwagResponseSchema.php @@ -11,10 +11,23 @@ * @Attribute("refEntity", type = "string"), * @Attribute("httpCode", type = "integer"), * @Attribute("description", type = "string"), + * @Attribute("mimeType", type = "string"), + * @Attribute("schemaType", type = "string"), + * @Attribute("schemaFormat", type = "string"), * }) */ class SwagResponseSchema { + /** @var array */ + private const DEFAULTS = [ + 'refEntity' => '', + 'httpCode' => 200, + 'description' => '', + 'mimeType' => '', + 'schemaType' => '', + 'schemaFormat' => '' + ]; + /** @var string */ public $refEntity; @@ -24,12 +37,24 @@ class SwagResponseSchema /** @var string */ public $description; + /** @var string */ + public $mimeType; + + /** @var string */ + public $schemaType; + + /** @var string */ + public $schemaFormat; + public function __construct(array $values) { - $values = array_merge(['refEntity' => '','httpCode' => 200, 'description' => ''], $values); + $values = array_merge(SELF::DEFAULTS, $values); $this->refEntity = $values['refEntity']; $this->httpCode = intval($values['httpCode']); $this->description = $values['description']; + $this->mimeType = $values['mimeType']; + $this->schemaType = $values['schemaType']; + $this->schemaFormat = $values['schemaFormat']; } } \ No newline at end of file diff --git a/src/Lib/Annotation/SwagResponseSchemaHandler.php b/src/Lib/Annotation/SwagResponseSchemaHandler.php index 110b3d76..019a817c 100644 --- a/src/Lib/Annotation/SwagResponseSchemaHandler.php +++ b/src/Lib/Annotation/SwagResponseSchemaHandler.php @@ -2,15 +2,31 @@ namespace SwaggerBake\Lib\Annotation; +use SwaggerBake\Lib\OpenApi\Content; use SwaggerBake\Lib\OpenApi\Response; class SwagResponseSchemaHandler { + /** + * @param SwagResponseSchema $annotation + * @return Response + */ public function getResponse(SwagResponseSchema $annotation) : Response { - return (new Response()) - ->setSchemaRef($annotation->refEntity) + $response = (new Response()) ->setCode(intval($annotation->httpCode)) ->setDescription($annotation->description); + + if (empty($annotation->schemaFormat) && empty($annotation->mimeType)) { + return $response; + } + + return $response->pushContent( + (new Content()) + ->setSchema($annotation->refEntity) + ->setFormat($annotation->schemaFormat) + ->setType($annotation->schemaType) + ->setMimeType($annotation->mimeType) + ); } } \ No newline at end of file diff --git a/src/Lib/Annotation/SwagSecurityHandler.php b/src/Lib/Annotation/SwagSecurityHandler.php index a1455363..f30dd349 100644 --- a/src/Lib/Annotation/SwagSecurityHandler.php +++ b/src/Lib/Annotation/SwagSecurityHandler.php @@ -6,13 +6,15 @@ class SwagSecurityHandler { + /** + * @param SwagSecurity $annotation + * @return PathSecurity + */ public function getPathSecurity(SwagSecurity $annotation) : PathSecurity { - $security = new PathSecurity(); - $security + return (new PathSecurity()) ->setName($annotation->name) - ->setScopes($annotation->scopes); - - return $security; + ->setScopes($annotation->scopes) + ; } } \ No newline at end of file diff --git a/src/Lib/AnnotationLoader.php b/src/Lib/AnnotationLoader.php index 831d5bee..9f012cc6 100644 --- a/src/Lib/AnnotationLoader.php +++ b/src/Lib/AnnotationLoader.php @@ -7,8 +7,9 @@ class AnnotationLoader { - public static function load() + public static function load() : void { + AnnotationRegistry::loadAnnotationClass(SwagAnnotation\SwagDto::class); AnnotationRegistry::loadAnnotationClass(SwagAnnotation\SwagEntity::class); AnnotationRegistry::loadAnnotationClass(SwagAnnotation\SwagEntityAttribute::class); AnnotationRegistry::loadAnnotationClass(SwagAnnotation\SwagForm::class); diff --git a/src/Lib/CakeModel.php b/src/Lib/CakeModel.php index fcc2a794..45146506 100644 --- a/src/Lib/CakeModel.php +++ b/src/Lib/CakeModel.php @@ -1,13 +1,10 @@ cakeRoute; } + /** + * @return string + */ public function getPrefix() : string { return $this->prefix; } + /** + * @return Configuration + */ public function getConfig() : Configuration { return $this->config; } + /** + * @param string $className + * @return string|null + */ private function getEntityFromNamespaces(string $className) : ?string { $namespaces = $this->config->getNamespaces(); @@ -111,6 +121,10 @@ private function getEntityFromNamespaces(string $className) : ?string return null; } + /** + * @param array $routes + * @return string[] + */ private function getTablesFromRoutes(array $routes) : array { $return = []; @@ -123,6 +137,11 @@ private function getTablesFromRoutes(array $routes) : array return array_unique($return); } + /** + * @param EntityInterface $entity + * @param TableSchema $schema + * @return ExpressiveAttribute[] + */ private function getExpressiveAttributes(EntityInterface $entity, TableSchema $schema) : array { $return = []; @@ -151,6 +170,11 @@ private function getExpressiveAttributes(EntityInterface $entity, TableSchema $s return $return; } + /** + * @param array $schemaDebugInfo + * @param string $columnName + * @return bool + */ private function isPrimaryKey(array $schemaDebugInfo, string $columnName) : bool { if (!isset($schemaDebugInfo['constraints']['primary']['columns'])) { diff --git a/src/Lib/CakeRoute.php b/src/Lib/CakeRoute.php index dfc15696..23ccceab 100644 --- a/src/Lib/CakeRoute.php +++ b/src/Lib/CakeRoute.php @@ -60,6 +60,10 @@ public function getRoutes() : array return $routes; } + /** + * @param Route $route + * @return ExpressiveRoute + */ private function createExpressiveRouteFromRoute(Route $route) : ExpressiveRoute { $defaults = (array) $route->defaults; @@ -79,6 +83,10 @@ private function createExpressiveRouteFromRoute(Route $route) : ExpressiveRoute ; } + /** + * @param Route $route + * @return bool + */ private function isRouteAllowed(Route $route) : bool { if (substr($route->template, 0, $this->prefixLength) != $this->prefix) { diff --git a/src/Lib/Configuration.php b/src/Lib/Configuration.php index f71728bf..85af5c8c 100644 --- a/src/Lib/Configuration.php +++ b/src/Lib/Configuration.php @@ -8,7 +8,10 @@ class Configuration { + /** @var array */ private $configs = []; + + /** @var string */ private $root = ''; public function __construct($config = [], $root = ROOT) @@ -25,6 +28,8 @@ public function __construct($config = [], $root = ROOT) 'docType' => 'swagger', 'hotReload' => false, 'exceptionSchema' => 'Exception', + 'requestAccepts' => ['application/x-www-form-urlencoded'], + 'responseContentTypes' => ['application/json'], 'namespaces' => [ 'controllers' => ['\App\\'], 'entities' => ['\App\\'], @@ -35,6 +40,10 @@ public function __construct($config = [], $root = ROOT) ); } + /** + * @param string $var + * @return mixed + */ public function get(string $var) { if (!isset($this->configs[$var])) { @@ -44,57 +53,91 @@ public function get(string $var) return $this->configs[$var]; } + /** + * @return string + */ public function getYml() : string { return $this->root . $this->get('yml'); } + /** + * @return string + */ public function getJson() : string { return $this->root . $this->get('json'); } + /** + * @return string + */ public function getPrefix() : string { return $this->get('prefix'); } + /** + * @return string + */ public function getWebPath() : string { return $this->get('webPath'); } + /** + * @return bool + */ public function getHotReload() : bool { return (bool) $this->get('hotReload'); } + /** + * @return array + */ public function getNamespaces() : array { return $this->get('namespaces'); } + /** + * @return array + */ public function getParsedYml() : array { return Yaml::parseFile($this->getYml()); } + /** + * @return mixed|string + */ public function getTitleFromYml() { $yml = $this->getParsedYml(); return isset($yml['info']['title']) ? $yml['info']['title'] : ''; } + /** + * @return string + */ public function getDocType() : string { return strtolower($this->get('docType')); } + /** + * @return string + */ public function getExceptionSchema() : string { return $this->get('exceptionSchema'); } + /** + * @param string|null $doctype + * @return string + */ public function getLayout(?string $doctype = null) : string { $doctype = empty($doctype) ? $this->getDocType() : $doctype; @@ -104,6 +147,10 @@ public function getLayout(?string $doctype = null) : string return 'SwaggerBake.default'; } + /** + * @param string|null $doctype + * @return string + */ public function getView(?string $doctype = null) : string { $doctype = empty($doctype) ? $this->getDocType() : $doctype; @@ -112,4 +159,20 @@ public function getView(?string $doctype = null) : string } return 'SwaggerBake.Swagger/index'; } + + /** + * @return array + */ + public function getRequestAccepts() : array + { + return $this->get('requestAccepts'); + } + + /** + * @return array + */ + public function getResponseContentTypes() : array + { + return $this->get('responseContentTypes'); + } } \ No newline at end of file diff --git a/src/Lib/ExceptionHandler.php b/src/Lib/ExceptionHandler.php index b8dba34b..79860483 100644 --- a/src/Lib/ExceptionHandler.php +++ b/src/Lib/ExceptionHandler.php @@ -42,6 +42,11 @@ public function __construct(string $exceptionClass) $this->code = $this->code < 400 ? 500 : $this->code; } + /** + * Assigns ExceptionHandler::message using the Exception $instance argument + * + * @param $instance + */ private function assignMessage($instance) : void { $this->message = trim($instance->getMessage()); @@ -59,11 +64,17 @@ private function assignMessage($instance) : void $this->message = $class; } + /** + * @return int + */ public function getCode() : int { return $this->code; } + /** + * @return string + */ public function getMessage() : string { return $this->message; diff --git a/src/Lib/Factory/PathFactory.php b/src/Lib/Factory/PathFactory.php index dd33706b..79313503 100644 --- a/src/Lib/Factory/PathFactory.php +++ b/src/Lib/Factory/PathFactory.php @@ -4,21 +4,24 @@ use Cake\Utility\Inflector; use Exception; +use LogicException; +use ReflectionClass; use phpDocumentor\Reflection\DocBlock; -use phpDocumentor\Reflection\DocBlockFactory; -use ReflectionMethod; use SwaggerBake\Lib\Annotation as SwagAnnotation; use SwaggerBake\Lib\Configuration; use SwaggerBake\Lib\Exception\SwaggerBakeRunTimeException; use SwaggerBake\Lib\ExceptionHandler; use SwaggerBake\Lib\Model\ExpressiveRoute; +use SwaggerBake\Lib\OpenApi\Content; use SwaggerBake\Lib\OpenApi\OperationExternalDoc; use SwaggerBake\Lib\OpenApi\Path; use SwaggerBake\Lib\OpenApi\Parameter; use SwaggerBake\Lib\OpenApi\RequestBody; use SwaggerBake\Lib\OpenApi\Response; use SwaggerBake\Lib\OpenApi\Schema; +use SwaggerBake\Lib\OpenApi\SchemaProperty; use SwaggerBake\Lib\Utility\AnnotationUtility; +use SwaggerBake\Lib\Utility\DocBlockUtility; /** * Class SwaggerPath @@ -78,6 +81,7 @@ public function create() : ?Path ->setDeprecated($this->isDeprecated()) ; + $path = $this->withDataTransferObject($path, $methodAnnotations); $path = $this->withResponses($path, $methodAnnotations); $path = $this->withRequestBody($path, $methodAnnotations); $path = $this->withExternalDoc($path); @@ -86,6 +90,11 @@ public function create() : ?Path return $path; } + /** + * Returns a route (e.g. /api/model/action) + * + * @return string + */ private function getPathName() : string { $pieces = array_map( @@ -108,6 +117,11 @@ function ($piece) { ); } + /** + * Returns an array of Parameter + * + * @return Parameter[] + */ private function getPathParameters() : array { $return = []; @@ -149,6 +163,13 @@ private function getPathParameters() : array return $return; } + /** + * Returns an array of Lib/Annotation objects that can be applied to methods + * + * @param string $className + * @param string $method + * @return array + */ private function getMethodAnnotations(string $className, string $method) : array { $className = $className . 'Controller'; @@ -156,6 +177,96 @@ private function getMethodAnnotations(string $className, string $method) : array return AnnotationUtility::getMethodAnnotations($controller, $method); } + private function withDataTransferObject(Path $path, array $annotations) : Path + { + if (empty($annotations)) { + return $path; + } + + $dataTransferObjects = array_filter($annotations, function ($annotation) { + return $annotation instanceof SwagAnnotation\SwagDto; + }); + + if (empty($dataTransferObjects)) { + return $path; + } + + $dto = reset($dataTransferObjects); + $class = $dto->class; + + if (!class_exists($class)) { + return $path; + } + + $instance = (new ReflectionClass($class))->newInstanceWithoutConstructor(); + $properties = DocBlockUtility::getProperties($instance); + + if (empty($properties)) { + return $path; + } + + $filteredProperties = array_filter($properties, function ($property) use ($instance) { + if (!isset($property->class) || $property->class != get_class($instance)) { + return null; + } + return true; + }); + + $pathType = strtolower($path->getType()); + if ($pathType == 'post') { + $requestBody = new RequestBody(); + $schema = (new Schema())->setType('object'); + } + + foreach ($filteredProperties as $name => $reflectionProperty) { + $docBlock = DocBlockUtility::getPropertyDocBlock($reflectionProperty); + $vars = $docBlock->getTagsByName('var'); + if (empty($vars)) { + throw new LogicException('@var must be set for ' . $class . '::' . $name); + } + $var = reset($vars); + $dataType = DocBlockUtility::getDocBlockConvertedVar($var); + + if ($pathType == 'get') { + $path->pushParameter( + (new Parameter()) + ->setName($name) + ->setIn('query') + ->setRequired(!empty($docBlock->getTagsByName('required'))) + ->setDescription($docBlock->getSummary()) + ->setSchema((new Schema())->setType($dataType)) + ); + } else if ($pathType == 'post' && isset($schema)) { + $schema->pushProperty( + (new SchemaProperty()) + ->setDescription($docBlock->getSummary()) + ->setName($name) + ->setType($dataType) + ->setRequired(!empty($docBlock->getTagsByName('required'))) + ); + } + } + + if (isset($schema) && isset($requestBody)) { + $content = (new Content()) + ->setMimeType('application/x-www-form-urlencoded') + ->setSchema($schema); + + $path->setRequestBody( + $requestBody->pushContent($content) + ); + } + + return $path; + } + + /** + * Returns path with responses + * + * @param Path $path + * @param array $annotations + * @return Path + */ private function withResponses(Path $path, array $annotations) : Path { if (!empty($annotations)) { @@ -182,8 +293,19 @@ private function withResponses(Path $path, array $annotations) : Path return $path; } + /** + * Returns Path with request body + * + * @param Path $path + * @param array $annotations + * @return Path + */ private function withRequestBody(Path $path, array $annotations) : Path { + if (!empty($path->getRequestBody())) { + return $path; + } + if (empty($annotations)) { return $path; } @@ -211,6 +333,9 @@ private function withRequestBody(Path $path, array $annotations) : Path return $path->setRequestBody($requestBody); } + /** + * @return DocBlock|null + */ private function getDocBlock() : ?DocBlock { if (empty($this->route->getController())) { @@ -219,31 +344,23 @@ private function getDocBlock() : ?DocBlock $className = $this->route->getController() . 'Controller'; $methodName = $this->route->getAction(); - $controller = $this->getControllerFromNamespaces($className); if (!class_exists($controller)) { return null; } - $instance = new $controller; - try { - $reflectionMethod = new ReflectionMethod(get_class($instance), $methodName); + return DocBlockUtility::getMethodDocBlock(new $controller, $methodName); } catch (Exception $e) { return null; } - - $comments = $reflectionMethod->getDocComment(); - - if (!$comments) { - return null; - } - - $docFactory = DocBlockFactory::createInstance(); - return $docFactory->create($comments); } + /** + * @param string $className + * @return string|null + */ private function getControllerFromNamespaces(string $className) : ?string { $namespaces = $this->config->getNamespaces(); @@ -264,6 +381,10 @@ private function getControllerFromNamespaces(string $className) : ?string return null; } + /** + * @param string $className + * @return bool + */ private function isControllerVisible(string $className) : bool { $className = $className . 'Controller'; @@ -284,6 +405,10 @@ private function isControllerVisible(string $className) : bool return true; } + /** + * @param array $annotations + * @return bool + */ private function isMethodVisible(array $annotations) : bool { foreach ($annotations as $annotation) { @@ -295,6 +420,11 @@ private function isMethodVisible(array $annotations) : bool return true; } + /** + * Check if this path/operation is deprecated + * + * @return bool + */ private function isDeprecated() : bool { if (!$this->dockBlock || !$this->dockBlock instanceof DocBlock) { @@ -304,6 +434,11 @@ private function isDeprecated() : bool return $this->dockBlock->hasTag('deprecated'); } + /** + * Defines external documentation using see tag + * @param Path $path + * @return Path + */ private function withExternalDoc(Path $path) : Path { if (!$this->dockBlock || !$this->dockBlock instanceof DocBlock) { diff --git a/src/Lib/Factory/SchemaFactory.php b/src/Lib/Factory/SchemaFactory.php index 59a6fbd5..9bb1fa32 100644 --- a/src/Lib/Factory/SchemaFactory.php +++ b/src/Lib/Factory/SchemaFactory.php @@ -1,6 +1,5 @@ config = $config; } + /** + * @param ExpressiveModel $model + * @return Schema|null + */ public function create(ExpressiveModel $model) : ?Schema { if (!$this->isSwaggable($model)) { @@ -62,6 +70,10 @@ public function create(ExpressiveModel $model) : ?Schema return $schema; } + /** + * @param ExpressiveModel $model + * @return array + */ private function getProperties(ExpressiveModel $model) : array { $return = $this->getSwagPropertyAnnotations($model); @@ -78,6 +90,10 @@ private function getProperties(ExpressiveModel $model) : array return $return; } + /** + * @param ExpressiveModel $model + * @return DocBlock|null + */ private function getDocBlock(ExpressiveModel $model) : ?DocBlock { $entity = $this->getEntityFromNamespaces($model->getName()); @@ -99,6 +115,10 @@ private function getDocBlock(ExpressiveModel $model) : ?DocBlock return $docFactory->create($comments); } + /** + * @param string $className + * @return string|null + */ private function getEntityFromNamespaces(string $className) : ?string { $namespaces = $this->config->getNamespaces(); @@ -119,6 +139,10 @@ private function getEntityFromNamespaces(string $className) : ?string return null; } + /** + * @param string $className + * @return string|null + */ private function getTableFromNamespaces(string $className) : ?string { $namespaces = $this->config->getNamespaces(); @@ -139,6 +163,10 @@ private function getTableFromNamespaces(string $className) : ?string return null; } + /** + * @param ExpressiveAttribute $attribute + * @return SchemaProperty + */ private function getSchemaProperty(ExpressiveAttribute $attribute) : SchemaProperty { $isReadOnlyField = in_array($attribute->getName(), self::READ_ONLY_FIELDS); @@ -155,6 +183,12 @@ private function getSchemaProperty(ExpressiveAttribute $attribute) : SchemaPrope return $property; } + /** + * Returns key-value pair of property name => SchemaProperty + * + * @param ExpressiveModel $model + * @return SchemaProperty[] + */ private function getSwagPropertyAnnotations(ExpressiveModel $model) : array { $return = []; @@ -172,6 +206,10 @@ private function getSwagPropertyAnnotations(ExpressiveModel $model) : array return $return; } + /** + * @param ExpressiveModel $model + * @return bool + */ private function isSwaggable(ExpressiveModel $model) : bool { $entity = $this->getEntityFromNamespaces($model->getName()); @@ -186,6 +224,10 @@ private function isSwaggable(ExpressiveModel $model) : bool return true; } + /** + * @param ExpressiveAttribute $attribute + * @return bool + */ private function isAttributeRequired(ExpressiveAttribute $attribute) : bool { if (!$this->validator) { @@ -200,6 +242,10 @@ private function isAttributeRequired(ExpressiveAttribute $attribute) : bool return false; } + /** + * @param string $className + * @return Validator|null + */ private function getValidator(string $className) : ?Validator { try { diff --git a/src/Lib/FormData.php b/src/Lib/FormData.php index 68ed1621..99a15ca1 100644 --- a/src/Lib/FormData.php +++ b/src/Lib/FormData.php @@ -3,9 +3,13 @@ namespace SwaggerBake\Lib; use SwaggerBake\Lib\Annotation as SwagAnnotation; +use SwaggerBake\Lib\OpenApi\SchemaProperty; class FormData extends AbstractParameter { + /** + * @return SchemaProperty[] + */ public function getSchemaProperties() : array { $return = []; diff --git a/src/Lib/HeaderParameter.php b/src/Lib/HeaderParameter.php index 71c8129a..dc8cd0eb 100644 --- a/src/Lib/HeaderParameter.php +++ b/src/Lib/HeaderParameter.php @@ -3,9 +3,13 @@ namespace SwaggerBake\Lib; use SwaggerBake\Lib\Annotation as SwagAnnotation; +use SwaggerBake\Lib\OpenApi\Parameter; class HeaderParameter extends AbstractParameter { + /** + * @return Parameter[] + */ public function getHeaderParameters() : array { $return = []; diff --git a/src/Lib/Model/ExpressiveAttribute.php b/src/Lib/Model/ExpressiveAttribute.php index 0cc80e56..569cdc04 100644 --- a/src/Lib/Model/ExpressiveAttribute.php +++ b/src/Lib/Model/ExpressiveAttribute.php @@ -6,9 +6,16 @@ class ExpressiveAttribute { + /** @var string */ private $name = ''; + + /** @var string */ private $type = ''; + + /** @var string */ private $default = ''; + + /** @var bool */ private $isPrimaryKey = false; /** diff --git a/src/Lib/Model/ExpressiveModel.php b/src/Lib/Model/ExpressiveModel.php index 225e1ff3..beb48d5e 100644 --- a/src/Lib/Model/ExpressiveModel.php +++ b/src/Lib/Model/ExpressiveModel.php @@ -6,7 +6,10 @@ class ExpressiveModel { + /** @var string */ private $name = ''; + + /** @var array */ private $attributes = []; /** diff --git a/src/Lib/Model/ExpressiveRoute.php b/src/Lib/Model/ExpressiveRoute.php index edb80f31..f013d588 100644 --- a/src/Lib/Model/ExpressiveRoute.php +++ b/src/Lib/Model/ExpressiveRoute.php @@ -6,76 +6,127 @@ class ExpressiveRoute { - private - $name, - $plugin, - $controller, - $action, - $methods = [], - $template - ; - - public function getName() + /** @var string|null */ + private $name; + + /** @var string|null */ + private $plugin; + + /** @var string|null */ + private $controller; + + /** @var string|null */ + private $action; + + /** @var array */ + private $methods = []; + + /** @var string|null */ + private $template; + + /** + * @return string + */ + public function getName(): ?string { return $this->name; } - public function setName($name) + /** + * @param $name + * @return $this + */ + public function setName(string $name): ExpressiveRoute { $this->name = $name; return $this; } - public function getPlugin() + /** + * @return string + */ + public function getPlugin(): ?string { return $this->plugin; } - public function setPlugin($plugin) + /** + * @param string|null $plugin + * @return $this + */ + public function setPlugin(?string $plugin): ExpressiveRoute { $this->plugin = $plugin; return $this; } - public function getController() + /** + * @return string + */ + public function getController(): ?string { return $this->controller; } - public function setController($controller) + /** + * @param $controller + * @return $this + */ + public function setController(string $controller) { $this->controller = $controller; return $this; } - public function getAction() + /** + * @return string + */ + public function getAction(): ?string { return $this->action; } - public function setAction($action) + /** + * @param $action + * @return $this + */ + public function setAction($action): ExpressiveRoute { $this->action = $action; return $this; } + /** + * @return array + */ public function getMethods(): array { return $this->methods; } + /** + * @param array $methods + * @return $this + */ public function setMethods(array $methods): ExpressiveRoute { $this->methods = $methods; return $this; } - public function getTemplate() + /** + * @return string + */ + public function getTemplate() : ?string { return $this->template; } - public function setTemplate($template) + /** + * @param $template + * @return $this + */ + public function setTemplate(string $template): ExpressiveRoute { $this->template = $template; return $this; diff --git a/src/Lib/OpenApi/Content.php b/src/Lib/OpenApi/Content.php index 671e4aee..79a0e12c 100644 --- a/src/Lib/OpenApi/Content.php +++ b/src/Lib/OpenApi/Content.php @@ -7,9 +7,26 @@ class Content implements JsonSerializable { + /** @var string[] */ + private const STANDARD_FORMATS = [ + 'application/json', + 'application/xml', + 'application/vnd.api+json', + 'application/x-www-form-urlencoded' + ]; + + /** @var string */ private $mimeType = ''; + + /** @var string|Schema */ private $schema; + /** @var string $type value can be string, number etc. */ + private $type = ''; + + /** @var string $format value can be binary for images for instance */ + private $format = ''; + public function toArray() : array { $vars = get_object_vars($this); @@ -18,6 +35,12 @@ public function toArray() : array unset($vars['schema']); $vars['schema']['$ref'] = $this->schema; } + + if (in_array($this->mimeType, self::STANDARD_FORMATS)) { + unset($vars['type']); + unset($vars['format']); + } + return $vars; } @@ -63,4 +86,40 @@ public function setSchema($schema) : Content $this->schema = $schema; return $this; } + + /** + * @return string + */ + public function getType(): string + { + return $this->type; + } + + /** + * @param string $type + * @return Content + */ + public function setType(string $type): Content + { + $this->type = $type; + return $this; + } + + /** + * @return string + */ + public function getFormat(): string + { + return $this->format; + } + + /** + * @param string $format + * @return Content + */ + public function setFormat(string $format): Content + { + $this->format = $format; + return $this; + } } \ No newline at end of file diff --git a/src/Lib/OpenApi/OperationExternalDoc.php b/src/Lib/OpenApi/OperationExternalDoc.php index f7998579..decf2743 100644 --- a/src/Lib/OpenApi/OperationExternalDoc.php +++ b/src/Lib/OpenApi/OperationExternalDoc.php @@ -6,7 +6,10 @@ class OperationExternalDoc implements JsonSerializable { + /** @var string */ private $description = ''; + + /** @var string */ private $url = ''; public function toArray() : array diff --git a/src/Lib/OpenApi/Parameter.php b/src/Lib/OpenApi/Parameter.php index e705f0e9..2af27641 100644 --- a/src/Lib/OpenApi/Parameter.php +++ b/src/Lib/OpenApi/Parameter.php @@ -3,6 +3,7 @@ namespace SwaggerBake\Lib\OpenApi; +use LogicException; use InvalidArgumentException; use JsonSerializable; @@ -35,6 +36,10 @@ class Parameter implements JsonSerializable public function toArray() : array { + if (empty($this->in)) { + throw new LogicException('Parameter::in is required for ' . $this->name); + } + return get_object_vars($this); } @@ -44,6 +49,9 @@ public function jsonSerialize() if (empty($vars['description'])) { unset($vars['description']); } + if (empty($vars['schema'])) { + unset($vars['schema']); + } return $vars; } diff --git a/src/Lib/OpenApi/Path.php b/src/Lib/OpenApi/Path.php index 058cc948..0dd69e16 100644 --- a/src/Lib/OpenApi/Path.php +++ b/src/Lib/OpenApi/Path.php @@ -1,10 +1,8 @@ getCode(); + $existingResponse = $this->getResponseByCode($response->getCode()); + if ($this->getResponseByCode($response->getCode())) { + $content = $existingResponse->getContent() + $response->getContent(); + $existingResponse->setContent($content); + $this->responses[$code] = $existingResponse; + return $this; + } $this->responses[$code] = $response; return $this; } diff --git a/src/Lib/OpenApi/PathSecurity.php b/src/Lib/OpenApi/PathSecurity.php index bdf7d30a..931d1be6 100644 --- a/src/Lib/OpenApi/PathSecurity.php +++ b/src/Lib/OpenApi/PathSecurity.php @@ -6,9 +6,15 @@ class PathSecurity implements JsonSerializable { + /** @var string */ private $name = ''; + + /** @var string[] */ private $scopes = []; + /** + * @return array|array[] + */ public function toArray() : array { return [ @@ -16,6 +22,9 @@ public function toArray() : array ]; } + /** + * @return array|array[]|mixed + */ public function jsonSerialize() { return $this->toArray(); diff --git a/src/Lib/OpenApi/RequestBody.php b/src/Lib/OpenApi/RequestBody.php index 0422aac7..347576b3 100644 --- a/src/Lib/OpenApi/RequestBody.php +++ b/src/Lib/OpenApi/RequestBody.php @@ -7,11 +7,21 @@ class RequestBody implements JsonSerializable { + /** @var string */ private $description = ''; + + /** @var Content[] */ private $content = []; + + /** @var bool */ private $required = false; + + /** @var bool */ private $ignoreCakeSchema = false; + /** + * @return array + */ public function toArray() : array { $vars = get_object_vars($this); @@ -19,6 +29,9 @@ public function toArray() : array return $vars; } + /** + * @return array|mixed + */ public function jsonSerialize() { return $this->toArray(); @@ -42,18 +55,29 @@ public function setDescription(string $description): RequestBody return $this; } + /** + * @return array|Content[] + */ public function getContent() : array { return $this->content; } + /** + * @param Content $content + * @return $this + */ public function pushContent(Content $content) : RequestBody { $this->content[$content->getMimeType()] = $content; return $this; } - public function getContentByType(string $mimeType) : ?array + /** + * @param string $mimeType + * @return Content|null + */ + public function getContentByType(string $mimeType) : ?Content { if (isset($this->content[$mimeType])) { return $this->content[$mimeType]; diff --git a/src/Lib/OpenApi/Response.php b/src/Lib/OpenApi/Response.php index a0aaa36b..8fed3c70 100644 --- a/src/Lib/OpenApi/Response.php +++ b/src/Lib/OpenApi/Response.php @@ -7,19 +7,31 @@ class Response implements JsonSerializable { + /** @var int */ private $code = 0; + + /** @var string */ private $description = ''; - private $schemaRef = ''; + /** @var Content[] */ + private $content = []; + + /** + * @return array + */ public function toArray() : array { $vars = get_object_vars($this); unset($vars['code']); - unset($vars['schemaRef']); - $vars['content']['application/json']['schema']['$ref'] = $this->schemaRef; + if (empty($vars['content'])) { + unset($vars['content']); + } return $vars; } + /** + * @return array|mixed + */ public function jsonSerialize() { return $this->toArray(); @@ -62,22 +74,34 @@ public function setDescription(string $description): Response } /** - * @return string + * @return Content[] */ - public function getSchemaRef(): string + public function getContent(): array { - return $this->schemaRef; + return $this->content; } /** - * @param string|null $schemaRef - * @return $this + * Sets the array of Content[] + * + * @param Content[] $contents + * @return Response */ - public function setSchemaRef(?string $schemaRef): Response + public function setContent(array $contents): Response { - $this->schemaRef = $schemaRef; + $this->content = $contents; return $this; } - + /** + * Appends to array of Content[] + * + * @param Content $content + * @return Response + */ + public function pushContent(Content $content): Response + { + $this->content[$content->getMimeType()] = $content; + return $this; + } } \ No newline at end of file diff --git a/src/Lib/OpenApi/Schema.php b/src/Lib/OpenApi/Schema.php index a71c6f81..91f50f89 100644 --- a/src/Lib/OpenApi/Schema.php +++ b/src/Lib/OpenApi/Schema.php @@ -16,15 +16,18 @@ class Schema implements JsonSerializable /** @var string */ private $type = ''; - /** @var array */ + /** @var string[] */ private $required = []; - /** @var array */ + /** @var SchemaProperty[] */ private $properties = []; - /** @var array */ + /** @var string[] */ private $items = []; + /** + * @return array + */ public function toArray() : array { $vars = get_object_vars($this); @@ -43,6 +46,9 @@ public function toArray() : array return $vars; } + /** + * @return array|mixed + */ public function jsonSerialize() { return $this->toArray(); @@ -93,7 +99,7 @@ public function getRequired(): array } /** - * @param array $required + * @param string[] $required * @return Schema */ public function setRequired(array $required): Schema @@ -131,7 +137,7 @@ public function setProperties(array $properties): Schema } /** - * @param SchemaProperty $properties + * @param SchemaProperty $property * @return Schema */ public function pushProperty(SchemaProperty $property): Schema @@ -174,7 +180,7 @@ public function getItems(): array } /** - * @param array $items + * @param string[] $items * @return Schema */ public function setItems(array $items): Schema diff --git a/src/Lib/QueryParameter.php b/src/Lib/QueryParameter.php index 86e3c3ed..a70ed0cd 100644 --- a/src/Lib/QueryParameter.php +++ b/src/Lib/QueryParameter.php @@ -2,11 +2,14 @@ namespace SwaggerBake\Lib; - use SwaggerBake\Lib\Annotation as SwagAnnotation; +use SwaggerBake\Lib\OpenApi\Parameter; class QueryParameter extends AbstractParameter { + /** + * @return Parameter[] + */ public function getQueryParameters() : array { $return = []; diff --git a/src/Lib/RequestBodyBuilder.php b/src/Lib/RequestBodyBuilder.php index 08c89121..fdee1e2c 100644 --- a/src/Lib/RequestBodyBuilder.php +++ b/src/Lib/RequestBodyBuilder.php @@ -1,6 +1,5 @@ swagger = $swagger; } + /** + * @return RequestBody|null + */ public function build() : ?RequestBody { $requestBody = $this->path->getRequestBody(); @@ -26,39 +28,54 @@ public function build() : ?RequestBody $requestBody = new RequestBody(); } - foreach ($this->path->getTags() as $tag) { + if (!in_array($this->path->getType(), ['put','patch', 'post'])) { + return null; + } + + $requestBody->setRequired(true); + + return $this->requestBodyWithContent($requestBody); + } + + /** + * @param RequestBody $requestBody + * @return RequestBody + */ + private function requestBodyWithContent(RequestBody $requestBody) : RequestBody + { + $tags = $this->path->getTags(); + $tag = preg_replace('/\s+/', '', reset($tags)); + $tag = Inflector::singularize($tag); - if (!in_array($this->path->getType(), ['put','patch', 'post'])) { + foreach ($this->swagger->getConfig()->getRequestAccepts() as $mimeType) { + + if ($requestBody->getContentByType($mimeType)) { continue; } - $schema = new Schema(); - $schema->setType('object'); - - if (!$requestBody->isIgnoreCakeSchema()) { - $schema = $this->withSchemaFromModel($schema, $tag); + if ($mimeType == 'application/x-www-form-urlencoded') { + $schema = new Schema(); + $schema->setType('object'); + if (!$requestBody->isIgnoreCakeSchema()) { + $schema = $this->withSchemaFromModel($schema, $tag); + } + $schema = $this->withSchemaFromAnnotations($schema); + $requestBody->pushContent((new Content())->setMimeType($mimeType)->setSchema($schema)); + continue; } - $schema = $this->withSchemaFromAnnotations($schema); - break; - } - if (!isset($schema)) { - return null; + $schema = '#/components/schemas/' . $tag; + $requestBody->pushContent((new Content())->setMimeType($mimeType)->setSchema($schema)); } - $content = (new Content()) - ->setMimeType('application/x-www-form-urlencoded') - ->setSchema($schema); - ; - - $requestBody - ->pushContent($content) - ->setRequired(true) - ; - return $requestBody; } + /** + * @param Schema $schema + * @param string $tag + * @return Schema + */ private function withSchemaFromModel(Schema $schema, string $tag) : Schema { $className = Inflector::classify($tag); @@ -78,6 +95,10 @@ private function withSchemaFromModel(Schema $schema, string $tag) : Schema return $schema; } + /** + * @param Schema $schema + * @return Schema + */ private function withSchemaFromAnnotations(Schema $schema) : Schema { $schemaProperties = (new FormData($this->route, $this->swagger->getConfig()))->getSchemaProperties(); diff --git a/src/Lib/Security.php b/src/Lib/Security.php index 0bf9538a..2151e8d4 100644 --- a/src/Lib/Security.php +++ b/src/Lib/Security.php @@ -3,9 +3,13 @@ namespace SwaggerBake\Lib; use SwaggerBake\Lib\Annotation as SwagAnnotation; +use SwaggerBake\Lib\OpenApi\PathSecurity; class Security extends AbstractParameter { + /** + * @return PathSecurity[] + */ public function getPathSecurity() : array { $return = []; diff --git a/src/Lib/Swagger.php b/src/Lib/Swagger.php index dc83085d..b08e0ab9 100644 --- a/src/Lib/Swagger.php +++ b/src/Lib/Swagger.php @@ -6,6 +6,7 @@ use SwaggerBake\Lib\Exception\SwaggerBakeRunTimeException; use SwaggerBake\Lib\Factory as Factory; use SwaggerBake\Lib\Model\ExpressiveRoute; +use SwaggerBake\Lib\OpenApi\Content; use SwaggerBake\Lib\OpenApi\Path; use SwaggerBake\Lib\OpenApi\Response; use SwaggerBake\Lib\OpenApi\Schema; @@ -14,9 +15,16 @@ class Swagger { + /** @var array */ private $array = []; + + /** @var CakeModel */ private $cakeModel; + + /** @var CakeRoute */ private $cakeRoute; + + /** @var Configuration */ private $config; public function __construct(CakeModel $cakeModel) @@ -272,16 +280,14 @@ private function pathWithResponses(Path $path) : Path ; $this->pushSchema($schema); - $response = (new Response()) - ->setSchemaRef('#/components/schemas/' . $tag) - ->setCode(200); + $response = (new Response())->setCode(200); + $response = $this->responseWithContent($response, '#/components/schemas/' . $tag); $path->pushResponse($response); continue; } - $response = (new Response()) - ->setSchemaRef('#/components/schemas/' . $className) - ->setCode(200); + $response = (new Response())->setCode(200); + $response = $this->responseWithContent($response, '#/components/schemas/' . $className); $path->pushResponse($response); } @@ -299,13 +305,26 @@ private function pathWithResponses(Path $path) : Path continue; } $path->pushResponse( - $response->setSchemaRef('#/components/schemas/' . $exceptionSchema->getName()) + $this->responseWithContent($response, '#/components/schemas/' . $exceptionSchema->getName()) ); } return $path; } + /** + * @param Response $response + * @param string $schema + * @return Response + */ + private function responseWithContent(Response $response, string $schema) : Response + { + foreach ($this->config->getResponseContentTypes() as $mimeType) { + $response->pushContent((new Content())->setMimeType($mimeType)->setSchema($schema)); + } + return $response; + } + /** * Sets a request body on a path * @@ -328,6 +347,7 @@ private function pathWithRequestBody(Path $path, ExpressiveRoute $route) : Path private function buildFromDefaults() : void { $array = Yaml::parseFile($this->config->getYml()); + if (!isset($array['paths'])) { $array['paths'] = []; } @@ -340,7 +360,8 @@ private function buildFromDefaults() : void $schema = (new Schema()) ->setName($schemaName) ->setType($schemaVar['type']) - ->setDescription($schemaVar['description'] ?? ''); + ->setDescription($schemaVar['description'] ?? '') + ->setItems($schemaVar['items'] ?? []); $schemaVar['properties'] = $schemaVar['properties'] ?? []; diff --git a/src/Lib/Utility/AnnotationUtility.php b/src/Lib/Utility/AnnotationUtility.php index fe96583b..bf4c1245 100644 --- a/src/Lib/Utility/AnnotationUtility.php +++ b/src/Lib/Utility/AnnotationUtility.php @@ -37,7 +37,7 @@ public static function getClassAnnotations(string $namespace) : array } /** - * Gets method annotations from full namespace and method arguments + * Returns an array of Lib/Annotation objects that can be applied to methods * * @uses AnnotationReader * @uses ReflectionClass diff --git a/src/Lib/Utility/DocBlockUtility.php b/src/Lib/Utility/DocBlockUtility.php new file mode 100644 index 00000000..bf4e4c46 --- /dev/null +++ b/src/Lib/Utility/DocBlockUtility.php @@ -0,0 +1,100 @@ +methodName supplied + * + * @param object $instance + * @param string $methodName + * @return DocBlock|null + */ + public static function getMethodDocBlock(object $instance, string $methodName) : ?DocBlock + { + try { + $reflectionMethod = new ReflectionMethod(get_class($instance), $methodName); + } catch (\ReflectionException $e) { + return null; + } + + $comment = $reflectionMethod->getDocComment(); + + if (!$comment) { + return null; + } + + $docFactory = DocBlockFactory::createInstance(); + return $docFactory->create($comment); + } + + /** + * Returns key-value array of property name and ReflectedProperty + * + * @param object $instance + * @return ReflectionProperty[] + */ + public static function getProperties(object $instance) : ?array + { + try { + $reflectionClass = new ReflectionClass(get_class($instance)); + $reflectedProperties = $reflectionClass->getProperties(); + } catch (\ReflectionException $e) { + return null; + } + + if (empty($reflectedProperties)) { + return null; + } + + $return = []; + foreach ($reflectedProperties as $property) { + $return[$property->name] = $property; + } + + return $return ?? null; + } + + /** + * @param ReflectionProperty $property + * @return DocBlock + */ + public static function getPropertyDocBlock(ReflectionProperty $property) : DocBlock + { + $comment = $property->getDocComment(); + $docFactory = DocBlockFactory::createInstance(); + return $docFactory->create($comment); + } + + /** + * Returns string representation of Var_ data type. Can be either string, integer, boolean, or null if unknown + * + * @param Var_ $var + * @return string|null + */ + public static function getDocBlockConvertedVar(Var_ $var) : ?string + { + if ($var->getType() instanceof String_) { + return 'string'; + } + if ($var->getType() instanceof Integer) { + return 'integer'; + } + if ($var->getType() instanceof Boolean) { + return 'boolean'; + } + + return null; + } +} \ No newline at end of file diff --git a/templates/Swagger/redoc.php b/templates/Swagger/redoc.php index 96be4b80..fef8e7c6 100644 --- a/templates/Swagger/redoc.php +++ b/templates/Swagger/redoc.php @@ -1,2 +1,3 @@ - \ No newline at end of file + + \ No newline at end of file diff --git a/tests/TestCase/Lib/Annotations/SwagDtoTest.php b/tests/TestCase/Lib/Annotations/SwagDtoTest.php new file mode 100644 index 00000000..d378273b --- /dev/null +++ b/tests/TestCase/Lib/Annotations/SwagDtoTest.php @@ -0,0 +1,92 @@ +setExtensions(['json']); + $builder->resources('Employees', [ + 'only' => ['dtoPost','dtoQuery'], + 'map' => [ + 'dtoPost' => [ + 'action' => 'dtoPost', + 'method' => 'POST', + 'path' => 'dto-post' + ], + 'dtoQuery' => [ + 'action' => 'dtoQuery', + 'method' => 'GET', + 'path' => 'dto-query' + ], + ] + ]); + }); + $this->router = $router; + + $this->config = new Configuration([ + 'prefix' => '/api', + 'yml' => '/config/swagger-bare-bones.yml', + 'json' => '/webroot/swagger.json', + 'webPath' => '/swagger.json', + 'hotReload' => false, + 'exceptionSchema' => 'Exception', + 'requestAccepts' => ['application/x-www-form-urlencoded'], + 'responseContentTypes' => ['application/json'], + 'namespaces' => [ + 'controllers' => ['\SwaggerBakeTest\App\\'], + 'entities' => ['\SwaggerBakeTest\App\\'], + 'tables' => ['\SwaggerBakeTest\App\\'], + ] + ], SWAGGER_BAKE_TEST_APP); + + AnnotationLoader::load(); + } + + public function testSwagDtoQuery() + { + $cakeRoute = new CakeRoute($this->router, $this->config); + + $swagger = new Swagger(new CakeModel($cakeRoute, $this->config)); + $arr = json_decode($swagger->toString(), true); + + $operation = $arr['paths']['/employees/dto-query']['get']; + + $this->assertEquals('lastName', $operation['parameters'][0]['name']); + $this->assertEquals('firstName', $operation['parameters'][1]['name']); + } + + public function testSwagDtoPost() + { + $cakeRoute = new CakeRoute($this->router, $this->config); + + $swagger = new Swagger(new CakeModel($cakeRoute, $this->config)); + $arr = json_decode($swagger->toString(), true); + + $operation = $arr['paths']['/employees/dto-post']['post']; + $properties = $operation['requestBody']['content']['application/x-www-form-urlencoded']['schema']['properties']; + + $this->assertArrayHasKey('lastName', $properties); + $this->assertArrayHasKey('firstName', $properties); + } +} \ No newline at end of file diff --git a/tests/TestCase/Lib/Annotations/SwagEntityAttributeTest.php b/tests/TestCase/Lib/Annotations/SwagEntityAttributeTest.php new file mode 100644 index 00000000..a0fd6c54 --- /dev/null +++ b/tests/TestCase/Lib/Annotations/SwagEntityAttributeTest.php @@ -0,0 +1,69 @@ +setExtensions(['json']); + $builder->resources('Employees', function (RouteBuilder $routes) { + $routes->resources('EmployeeSalaries'); + }); + }); + $this->router = $router; + + $this->config = new Configuration([ + 'prefix' => '/api', + 'yml' => '/config/swagger-bare-bones.yml', + 'json' => '/webroot/swagger.json', + 'webPath' => '/swagger.json', + 'hotReload' => false, + 'exceptionSchema' => 'Exception', + 'requestAccepts' => ['application/x-www-form-urlencoded'], + 'responseContentTypes' => ['application/json'], + 'namespaces' => [ + 'controllers' => ['\SwaggerBakeTest\App\\'], + 'entities' => ['\SwaggerBakeTest\App\\'], + 'tables' => ['\SwaggerBakeTest\App\\'], + ] + ], SWAGGER_BAKE_TEST_APP); + + AnnotationLoader::load(); + } + + public function testEntityAttribute() + { + $cakeRoute = new CakeRoute($this->router, $this->config); + + $swagger = new Swagger(new CakeModel($cakeRoute, $this->config)); + + $arr = json_decode($swagger->toString(), true); + + $employee = $arr['components']['schemas']['Employee']; + + $this->assertTrue($employee['properties']['gender']['readOnly']); + } +} \ No newline at end of file diff --git a/tests/TestCase/Lib/SwaggerEntityTest.php b/tests/TestCase/Lib/Annotations/SwagEntityTest.php similarity index 91% rename from tests/TestCase/Lib/SwaggerEntityTest.php rename to tests/TestCase/Lib/Annotations/SwagEntityTest.php index ecd76c49..c65157e3 100644 --- a/tests/TestCase/Lib/SwaggerEntityTest.php +++ b/tests/TestCase/Lib/Annotations/SwagEntityTest.php @@ -1,7 +1,7 @@ '/swagger.json', 'hotReload' => false, 'exceptionSchema' => 'Exception', + 'requestAccepts' => ['application/x-www-form-urlencoded'], + 'responseContentTypes' => ['application/json'], 'namespaces' => [ 'controllers' => ['\SwaggerBakeTest\App\\'], 'entities' => ['\SwaggerBakeTest\App\\'], diff --git a/tests/TestCase/Lib/Annotations/SwagFormTest.php b/tests/TestCase/Lib/Annotations/SwagFormTest.php new file mode 100644 index 00000000..371f0263 --- /dev/null +++ b/tests/TestCase/Lib/Annotations/SwagFormTest.php @@ -0,0 +1,77 @@ +setExtensions(['json']); + $builder->resources('Employees', [ + 'map' => [ + 'customPost' => [ + 'action' => 'customPost', + 'method' => 'POST', + 'path' => 'custom-post' + ], + ] + ]); + }); + $this->router = $router; + + $this->config = new Configuration([ + 'prefix' => '/api', + 'yml' => '/config/swagger-bare-bones.yml', + 'json' => '/webroot/swagger.json', + 'webPath' => '/swagger.json', + 'hotReload' => false, + 'exceptionSchema' => 'Exception', + 'requestAccepts' => ['application/x-www-form-urlencoded'], + 'responseContentTypes' => ['application/json'], + 'namespaces' => [ + 'controllers' => ['\SwaggerBakeTest\App\\'], + 'entities' => ['\SwaggerBakeTest\App\\'], + 'tables' => ['\SwaggerBakeTest\App\\'], + ] + ], SWAGGER_BAKE_TEST_APP); + + AnnotationLoader::load(); + } + + public function testSwagForm() + { + $cakeRoute = new CakeRoute($this->router, $this->config); + + $swagger = new Swagger(new CakeModel($cakeRoute, $this->config)); + $arr = json_decode($swagger->toString(), true); + + $operation = $arr['paths']['/employees/custom-post']['post']; + + $this->assertArrayHasKey('schema', $operation['requestBody']['content']['application/x-www-form-urlencoded']); + + $properties = $operation['requestBody']['content']['application/x-www-form-urlencoded']['schema']['properties']; + + $this->assertCount(1, $properties); + $this->assertArrayHasKey('fieldName', $properties); + } + +} \ No newline at end of file diff --git a/tests/TestCase/Lib/Annotations/SwagHeaderTest.php b/tests/TestCase/Lib/Annotations/SwagHeaderTest.php new file mode 100644 index 00000000..97ba674e --- /dev/null +++ b/tests/TestCase/Lib/Annotations/SwagHeaderTest.php @@ -0,0 +1,78 @@ +setExtensions(['json']); + $builder->resources('Employees', [ + 'map' => [ + 'customGet' => [ + 'action' => 'customGet', + 'method' => 'GET', + 'path' => 'custom-get' + ], + ] + ]); + }); + $this->router = $router; + + $this->config = new Configuration([ + 'prefix' => '/api', + 'yml' => '/config/swagger-bare-bones.yml', + 'json' => '/webroot/swagger.json', + 'webPath' => '/swagger.json', + 'hotReload' => false, + 'exceptionSchema' => 'Exception', + 'requestAccepts' => ['application/x-www-form-urlencoded'], + 'responseContentTypes' => ['application/json'], + 'namespaces' => [ + 'controllers' => ['\SwaggerBakeTest\App\\'], + 'entities' => ['\SwaggerBakeTest\App\\'], + 'tables' => ['\SwaggerBakeTest\App\\'], + ] + ], SWAGGER_BAKE_TEST_APP); + + AnnotationLoader::load(); + } + + public function testSwagHeader() + { + $cakeRoute = new CakeRoute($this->router, $this->config); + + $swagger = new Swagger(new CakeModel($cakeRoute, $this->config)); + $arr = json_decode($swagger->toString(), true); + + + $this->assertArrayHasKey('/employees/custom-get', $arr['paths']); + $this->assertArrayHasKey('get', $arr['paths']['/employees/custom-get']); + $operation = $arr['paths']['/employees/custom-get']['get']; + + $this->assertEquals('custom-get summary', $operation['summary']); + + $this->assertCount(1, array_filter($operation['parameters'], function ($param) { + return $param['name'] == 'X-HEAD-ATTRIBUTE'; + })); + } +} \ No newline at end of file diff --git a/tests/TestCase/Lib/Annotations/SwagPaginatorTest.php b/tests/TestCase/Lib/Annotations/SwagPaginatorTest.php new file mode 100644 index 00000000..d4931056 --- /dev/null +++ b/tests/TestCase/Lib/Annotations/SwagPaginatorTest.php @@ -0,0 +1,76 @@ +setExtensions(['json']); + $builder->resources('Departments'); + }); + $this->router = $router; + + $this->config = new Configuration([ + 'prefix' => '/api', + 'yml' => '/config/swagger-bare-bones.yml', + 'json' => '/webroot/swagger.json', + 'webPath' => '/swagger.json', + 'hotReload' => false, + 'exceptionSchema' => 'Exception', + 'requestAccepts' => ['application/x-www-form-urlencoded'], + 'responseContentTypes' => ['application/json'], + 'namespaces' => [ + 'controllers' => ['\SwaggerBakeTest\App\\'], + 'entities' => ['\SwaggerBakeTest\App\\'], + 'tables' => ['\SwaggerBakeTest\App\\'], + ] + ], SWAGGER_BAKE_TEST_APP); + + AnnotationLoader::load(); + } + + public function testSwagPaginator() + { + $cakeRoute = new CakeRoute($this->router, $this->config); + + $swagger = new Swagger(new CakeModel($cakeRoute, $this->config)); + $arr = json_decode($swagger->toString(), true); + + $this->assertArrayHasKey('/departments', $arr['paths']); + $this->assertArrayHasKey('get', $arr['paths']['/departments']); + $operation = $arr['paths']['/departments']['get']; + + $this->assertCount(1, array_filter($operation['parameters'], function ($param) { + return isset($param['name']) && $param['name'] == 'page' && $param['schema']['type'] == 'integer'; + })); + $this->assertCount(1, array_filter($operation['parameters'], function ($param) { + return isset($param['name']) && $param['name'] == 'limit' && $param['schema']['type'] == 'integer'; + })); + $this->assertCount(1, array_filter($operation['parameters'], function ($param) { + return isset($param['name']) && $param['name'] == 'sort' && $param['schema']['type'] == 'string'; + })); + $this->assertCount(1, array_filter($operation['parameters'], function ($param) { + return isset($param['name']) && $param['name'] == 'direction' && $param['schema']['type'] == 'string'; + })); + } +} \ No newline at end of file diff --git a/tests/TestCase/Lib/SwaggerPathTest.php b/tests/TestCase/Lib/Annotations/SwagPathTest.php similarity index 89% rename from tests/TestCase/Lib/SwaggerPathTest.php rename to tests/TestCase/Lib/Annotations/SwagPathTest.php index 5e60cc0f..e91e4dee 100644 --- a/tests/TestCase/Lib/SwaggerPathTest.php +++ b/tests/TestCase/Lib/Annotations/SwagPathTest.php @@ -1,7 +1,7 @@ '/swagger.json', 'hotReload' => false, 'exceptionSchema' => 'Exception', + 'requestAccepts' => ['application/x-www-form-urlencoded'], + 'responseContentTypes' => ['application/json'], 'namespaces' => [ 'controllers' => ['\SwaggerBakeTest\App\\'], 'entities' => ['\SwaggerBakeTest\App\\'], diff --git a/tests/TestCase/Lib/Annotations/SwagQueryTest.php b/tests/TestCase/Lib/Annotations/SwagQueryTest.php new file mode 100644 index 00000000..cd2a7c18 --- /dev/null +++ b/tests/TestCase/Lib/Annotations/SwagQueryTest.php @@ -0,0 +1,67 @@ +setExtensions(['json']); + $builder->resources('Departments'); + }); + $this->router = $router; + + $this->config = new Configuration([ + 'prefix' => '/api', + 'yml' => '/config/swagger-bare-bones.yml', + 'json' => '/webroot/swagger.json', + 'webPath' => '/swagger.json', + 'hotReload' => false, + 'exceptionSchema' => 'Exception', + 'requestAccepts' => ['application/x-www-form-urlencoded'], + 'responseContentTypes' => ['application/json'], + 'namespaces' => [ + 'controllers' => ['\SwaggerBakeTest\App\\'], + 'entities' => ['\SwaggerBakeTest\App\\'], + 'tables' => ['\SwaggerBakeTest\App\\'], + ] + ], SWAGGER_BAKE_TEST_APP); + + AnnotationLoader::load(); + } + + public function testSwagQuery() + { + $cakeRoute = new CakeRoute($this->router, $this->config); + + $swagger = new Swagger(new CakeModel($cakeRoute, $this->config)); + $arr = json_decode($swagger->toString(), true); + + $this->assertArrayHasKey('/departments', $arr['paths']); + $this->assertArrayHasKey('get', $arr['paths']['/departments']); + $operation = $arr['paths']['/departments']['get']; + + $this->assertCount(1, array_filter($operation['parameters'], function ($param) { + return isset($param['name']) && $param['name'] == 'random'; + })); + } +} \ No newline at end of file diff --git a/tests/TestCase/Lib/Annotations/SwagRequestBodyContentTest.php b/tests/TestCase/Lib/Annotations/SwagRequestBodyContentTest.php new file mode 100644 index 00000000..cfbe761e --- /dev/null +++ b/tests/TestCase/Lib/Annotations/SwagRequestBodyContentTest.php @@ -0,0 +1,72 @@ +setExtensions(['json']); + $builder->resources('Employees', [ + 'map' => [ + 'customRequestBodyContent' => [ + 'action' => 'customRequestBodyContent', + 'method' => 'POST', + 'path' => 'custom-request-body-content' + ], + ] + ]); + }); + $this->router = $router; + + $this->config = new Configuration([ + 'prefix' => '/api', + 'yml' => '/config/swagger-bare-bones.yml', + 'json' => '/webroot/swagger.json', + 'webPath' => '/swagger.json', + 'hotReload' => false, + 'exceptionSchema' => 'Exception', + 'requestAccepts' => ['application/x-www-form-urlencoded'], + 'responseContentTypes' => ['application/json'], + 'namespaces' => [ + 'controllers' => ['\SwaggerBakeTest\App\\'], + 'entities' => ['\SwaggerBakeTest\App\\'], + 'tables' => ['\SwaggerBakeTest\App\\'], + ] + ], SWAGGER_BAKE_TEST_APP); + + AnnotationLoader::load(); + } + + public function testSwagRequestBodyContent() + { + $cakeRoute = new CakeRoute($this->router, $this->config); + + $swagger = new Swagger(new CakeModel($cakeRoute, $this->config)); + $arr = json_decode($swagger->toString(), true); + + $operation = $arr['paths']['/employees/custom-request-body-content']['post']; + + $this->assertArrayHasKey('schema', $operation['requestBody']['content']['text/plain']); + } + +} \ No newline at end of file diff --git a/tests/TestCase/Lib/Annotations/SwagRequestBodyTest.php b/tests/TestCase/Lib/Annotations/SwagRequestBodyTest.php new file mode 100644 index 00000000..1881b3f3 --- /dev/null +++ b/tests/TestCase/Lib/Annotations/SwagRequestBodyTest.php @@ -0,0 +1,75 @@ +setExtensions(['json']); + $builder->resources('Employees', [ + 'map' => [ + 'customPost' => [ + 'action' => 'customPost', + 'method' => 'POST', + 'path' => 'custom-post' + ], + ] + ]); + }); + $this->router = $router; + + $this->config = new Configuration([ + 'prefix' => '/api', + 'yml' => '/config/swagger-bare-bones.yml', + 'json' => '/webroot/swagger.json', + 'webPath' => '/swagger.json', + 'hotReload' => false, + 'exceptionSchema' => 'Exception', + 'requestAccepts' => ['application/x-www-form-urlencoded'], + 'responseContentTypes' => ['application/json'], + 'namespaces' => [ + 'controllers' => ['\SwaggerBakeTest\App\\'], + 'entities' => ['\SwaggerBakeTest\App\\'], + 'tables' => ['\SwaggerBakeTest\App\\'], + ] + ], SWAGGER_BAKE_TEST_APP); + + AnnotationLoader::load(); + } + + public function testSwagRequestBody() + { + $cakeRoute = new CakeRoute($this->router, $this->config); + + $swagger = new Swagger(new CakeModel($cakeRoute, $this->config)); + $arr = json_decode($swagger->toString(), true); + + $operation = $arr['paths']['/employees/custom-post']['post']; + $body = $operation['requestBody']; + + $this->assertEquals('Hello', $body['description']); + $this->assertTrue($body['required']); + $this->assertCount(1, $body['content']['application/x-www-form-urlencoded']['schema']['properties']); + } + +} \ No newline at end of file diff --git a/tests/TestCase/Lib/Annotations/SwagResponseSchemaTest.php b/tests/TestCase/Lib/Annotations/SwagResponseSchemaTest.php new file mode 100644 index 00000000..56c2ec17 --- /dev/null +++ b/tests/TestCase/Lib/Annotations/SwagResponseSchemaTest.php @@ -0,0 +1,77 @@ +setExtensions(['json']); + $builder->resources('Employees', [ + 'map' => [ + 'customResponseSchema' => [ + 'action' => 'customResponseSchema', + 'method' => 'GET', + 'path' => 'custom-response-schema' + ], + ] + ]); + }); + $this->router = $router; + + $this->config = new Configuration([ + 'prefix' => '/api', + 'yml' => '/config/swagger-with-existing.yml', + 'json' => '/webroot/swagger.json', + 'webPath' => '/swagger.json', + 'hotReload' => false, + 'exceptionSchema' => 'Exception', + 'requestAccepts' => ['application/x-www-form-urlencoded'], + 'responseContentTypes' => ['application/json'], + 'namespaces' => [ + 'controllers' => ['\SwaggerBakeTest\App\\'], + 'entities' => ['\SwaggerBakeTest\App\\'], + 'tables' => ['\SwaggerBakeTest\App\\'], + ] + ], SWAGGER_BAKE_TEST_APP); + + AnnotationLoader::load(); + } + + public function testSwagResponseSchema() + { + $cakeRoute = new CakeRoute($this->router, $this->config); + + $swagger = new Swagger(new CakeModel($cakeRoute, $this->config)); + $arr = json_decode($swagger->toString(), true); + + $operation = $arr['paths']['/employees/custom-response-schema']['get']; + + $this->assertArrayHasKey('$ref', $operation['responses'][200]['content']['application/json']['schema']); + $schema = $operation['responses'][200]['content']['application/json']['schema']; + $this->assertEquals('#/components/schemas/Pet', $schema['$ref']); + + $this->assertArrayHasKey(500, $operation['responses']); + $this->assertEquals('fatal error', $operation['responses'][500]['description']); + + } +} \ No newline at end of file diff --git a/tests/TestCase/Lib/Annotations/SwagSecurityTest.php b/tests/TestCase/Lib/Annotations/SwagSecurityTest.php new file mode 100644 index 00000000..17573109 --- /dev/null +++ b/tests/TestCase/Lib/Annotations/SwagSecurityTest.php @@ -0,0 +1,78 @@ +setExtensions(['json']); + $builder->resources('Employees', [ + 'map' => [ + 'customGet' => [ + 'action' => 'customGet', + 'method' => 'GET', + 'path' => 'custom-get' + ], + ] + ]); + }); + $this->router = $router; + + $this->config = new Configuration([ + 'prefix' => '/api', + 'yml' => '/config/swagger-bare-bones.yml', + 'json' => '/webroot/swagger.json', + 'webPath' => '/swagger.json', + 'hotReload' => false, + 'exceptionSchema' => 'Exception', + 'requestAccepts' => ['application/x-www-form-urlencoded'], + 'responseContentTypes' => ['application/json'], + 'namespaces' => [ + 'controllers' => ['\SwaggerBakeTest\App\\'], + 'entities' => ['\SwaggerBakeTest\App\\'], + 'tables' => ['\SwaggerBakeTest\App\\'], + ] + ], SWAGGER_BAKE_TEST_APP); + + AnnotationLoader::load(); + } + + public function testSwagHeader() + { + $cakeRoute = new CakeRoute($this->router, $this->config); + + $swagger = new Swagger(new CakeModel($cakeRoute, $this->config)); + $arr = json_decode($swagger->toString(), true); + + + $this->assertArrayHasKey('/employees/custom-get', $arr['paths']); + $this->assertArrayHasKey('get', $arr['paths']['/employees/custom-get']); + $operation = $arr['paths']['/employees/custom-get']['get']; + + $this->assertEquals('custom-get summary', $operation['summary']); + + $this->assertCount(1, array_filter($operation['security'], function ($param) { + return isset($param['BearerAuth']); + })); + } +} \ No newline at end of file diff --git a/tests/TestCase/Lib/SwaggerOperationTest.php b/tests/TestCase/Lib/SwaggerOperationTest.php index 6dc8a426..4dd6d98e 100644 --- a/tests/TestCase/Lib/SwaggerOperationTest.php +++ b/tests/TestCase/Lib/SwaggerOperationTest.php @@ -35,11 +35,6 @@ public function setUp(): void 'method' => 'GET', 'path' => 'custom-get' ], - 'customPost' => [ - 'action' => 'customPost', - 'method' => 'POST', - 'path' => 'custom-post' - ], 'customHidden' => [ 'action' => 'customHidden', 'method' => 'GET', @@ -60,6 +55,8 @@ public function setUp(): void 'webPath' => '/swagger.json', 'hotReload' => false, 'exceptionSchema' => 'Exception', + 'requestAccepts' => ['application/x-www-form-urlencoded'], + 'responseContentTypes' => ['application/json'], 'namespaces' => [ 'controllers' => ['\SwaggerBakeTest\App\\'], 'entities' => ['\SwaggerBakeTest\App\\'], @@ -70,59 +67,6 @@ public function setUp(): void AnnotationLoader::load(); } - public function testCustomGetRouteWithAnnotations() - { - $cakeRoute = new CakeRoute($this->router, $this->config); - - $swagger = new Swagger(new CakeModel($cakeRoute, $this->config)); - $arr = json_decode($swagger->toString(), true); - - - $this->assertArrayHasKey('/employees/custom-get', $arr['paths']); - $this->assertArrayHasKey('get', $arr['paths']['/employees/custom-get']); - $operation = $arr['paths']['/employees/custom-get']['get']; - - $this->assertEquals('custom-get summary', $operation['summary']); - - $this->assertCount(1, array_filter($operation['parameters'], function ($param) { - return $param['name'] == 'X-HEAD-ATTRIBUTE'; - })); - - $this->assertCount(1, array_filter($operation['parameters'], function ($param) { - return $param['name'] == 'page'; - })); - - $this->assertCount(1, array_filter($operation['parameters'], function ($param) { - return $param['name'] == 'queryParamName'; - })); - - $this->assertCount(1, array_filter($operation['security'], function ($param) { - return isset($param['BearerAuth']); - })); - - $this->assertCount(1, array_filter($operation['responses'], function ($response) { - return isset($response['description']) && $response['description'] == 'hello world'; - })); - - } - - public function testCustomPostRouteWithAnnotations() - { - $cakeRoute = new CakeRoute($this->router, $this->config); - - $swagger = new Swagger(new CakeModel($cakeRoute, $this->config)); - $arr = json_decode($swagger->toString(), true); - - $operation = $arr['paths']['/employees/custom-post']['post']; - - $this->assertArrayHasKey('schema', $operation['requestBody']['content']['application/x-www-form-urlencoded']); - - $properties = $operation['requestBody']['content']['application/x-www-form-urlencoded']['schema']['properties']; - - $this->assertCount(1, $properties); - $this->assertArrayHasKey('fieldName', $properties); - } - public function testHiddenOperation() { $cakeRoute = new CakeRoute($this->router, $this->config); diff --git a/tests/TestCase/Lib/SwaggerSchemaTest.php b/tests/TestCase/Lib/SwaggerSchemaTest.php index cbc656a5..20104b37 100644 --- a/tests/TestCase/Lib/SwaggerSchemaTest.php +++ b/tests/TestCase/Lib/SwaggerSchemaTest.php @@ -36,6 +36,8 @@ public function setUp(): void 'webPath' => '/swagger.json', 'hotReload' => false, 'exceptionSchema' => 'Exception', + 'requestAccepts' => ['application/x-www-form-urlencoded'], + 'responseContentTypes' => ['application/json'], 'namespaces' => [ 'controllers' => ['\SwaggerBakeTest\App\\'], 'entities' => ['\SwaggerBakeTest\App\\'], diff --git a/tests/TestCase/Lib/SwaggerTest.php b/tests/TestCase/Lib/SwaggerTest.php index a1a29c8b..9ba3851e 100644 --- a/tests/TestCase/Lib/SwaggerTest.php +++ b/tests/TestCase/Lib/SwaggerTest.php @@ -57,6 +57,8 @@ public function setUp(): void 'webPath' => '/swagger.json', 'hotReload' => false, 'exceptionSchema' => 'Exception', + 'requestAccepts' => ['application/x-www-form-urlencoded'], + 'responseContentTypes' => ['application/json'], 'namespaces' => [ 'controllers' => ['\SwaggerBakeTest\App\\'], 'entities' => ['\SwaggerBakeTest\App\\'], diff --git a/tests/test_app/src/Controller/EmployeesController.php b/tests/test_app/src/Controller/EmployeesController.php index 1e27e831..63834d61 100644 --- a/tests/test_app/src/Controller/EmployeesController.php +++ b/tests/test_app/src/Controller/EmployeesController.php @@ -134,4 +134,45 @@ public function customHidden() $this->set(compact('hello')); $this->viewBuilder()->setOption('serialize', ['hello']); } + + /** + * @Swag\SwagRequestBodyContent(refEntity="", mimeType="text/plain") + */ + public function customRequestBodyContent() + { + $hello = 'world'; + $this->set(compact('hello')); + $this->viewBuilder()->setOption('serialize', ['hello']); + } + + /** + * @Swag\SwagResponseSchema(refEntity="#/components/schemas/Pet", mimeType="application/json") + * @Swag\SwagResponseSchema(refEntity="", description="fatal error", httpCode=500) + */ + public function customResponseSchema() + { + $hello = 'world'; + $this->set(compact('hello')); + $this->viewBuilder()->setOption('serialize', ['hello']); + } + + /** + * @Swag\SwagDto(class="\SwaggerBakeTest\App\Dto\EmployeeData") + */ + public function dtoQuery() + { + $hello = 'world'; + $this->set(compact('hello')); + $this->viewBuilder()->setOption('serialize', ['hello']); + } + + /** + * @Swag\SwagDto(class="\SwaggerBakeTest\App\Dto\EmployeeData") + */ + public function dtoPost() + { + $hello = 'world'; + $this->set(compact('hello')); + $this->viewBuilder()->setOption('serialize', ['hello']); + } } diff --git a/tests/test_app/src/Dto/EmployeeData.php b/tests/test_app/src/Dto/EmployeeData.php new file mode 100644 index 00000000..8fba713e --- /dev/null +++ b/tests/test_app/src/Dto/EmployeeData.php @@ -0,0 +1,52 @@ +firstName; + } + + /** + * @param string $firstName + * @return EmployeeData + */ + public function setFirstName(string $firstName): EmployeeData + { + $this->firstName = $firstName; + return $this; + } + + /** + * @return string + */ + public function getLastName(): string + { + return $this->lastName; + } + + /** + * @param string $lastName + * @return EmployeeData + */ + public function setLastName(string $lastName): EmployeeData + { + $this->lastName = $lastName; + return $this; + } +} diff --git a/webroot/redoc.standalone.js b/webroot/redoc.standalone.js new file mode 100644 index 00000000..39560dca --- /dev/null +++ b/webroot/redoc.standalone.js @@ -0,0 +1,137 @@ +/*! + * ReDoc - OpenAPI/Swagger-generated API Reference Documentation + * ------------------------------------------------------------- + * Version: "2.0.0-rc.28" + * Repo: https://github.com/Redocly/redoc + */ +!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t(require("null"),function(){try{return require("esprima")}catch(e){}}()):"function"==typeof define&&define.amd?define(["null","esprima"],t):"object"==typeof exports?exports.Redoc=t(require("null"),function(){try{return require("esprima")}catch(e){}}()):e.Redoc=t(e.null,e.esprima)}(this,(function(e,t){return function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}return n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=162)}([function(e,t,n){"use strict";e.exports=n(226)},function(e,t,n){"use strict";n.r(t),n.d(t,"__extends",(function(){return o})),n.d(t,"__assign",(function(){return i})),n.d(t,"__rest",(function(){return a})),n.d(t,"__decorate",(function(){return s})),n.d(t,"__param",(function(){return l})),n.d(t,"__metadata",(function(){return c})),n.d(t,"__awaiter",(function(){return u})),n.d(t,"__generator",(function(){return p})),n.d(t,"__exportStar",(function(){return f})),n.d(t,"__values",(function(){return d})),n.d(t,"__read",(function(){return h})),n.d(t,"__spread",(function(){return m})),n.d(t,"__spreadArrays",(function(){return g})),n.d(t,"__await",(function(){return y})),n.d(t,"__asyncGenerator",(function(){return v})),n.d(t,"__asyncDelegator",(function(){return b})),n.d(t,"__asyncValues",(function(){return x})),n.d(t,"__makeTemplateObject",(function(){return w})),n.d(t,"__importStar",(function(){return k})),n.d(t,"__importDefault",(function(){return O})),n.d(t,"__classPrivateFieldGet",(function(){return _})),n.d(t,"__classPrivateFieldSet",(function(){return E})); + /*! ***************************************************************************** + Copyright (c) Microsoft Corporation. All rights reserved. + Licensed under the Apache License, Version 2.0 (the "License"); you may not use + this file except in compliance with the License. You may obtain a copy of the + License at http://www.apache.org/licenses/LICENSE-2.0 + + THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED + WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, + MERCHANTABLITY OR NON-INFRINGEMENT. + + See the Apache Version 2.0 License for specific language governing permissions + and limitations under the License. + ***************************************************************************** */ + var r=function(e,t){return(r=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(e,t)};function o(e,t){function n(){this.constructor=e}r(e,t),e.prototype=null===t?Object.create(t):(n.prototype=t.prototype,new n)}var i=function(){return(i=Object.assign||function(e){for(var t,n=1,r=arguments.length;n=0;s--)(o=e[s])&&(a=(i<3?o(a):i>3?o(t,n,a):o(t,n))||a);return i>3&&a&&Object.defineProperty(t,n,a),a}function l(e,t){return function(n,r){t(n,r,e)}}function c(e,t){if("object"==typeof Reflect&&"function"==typeof Reflect.metadata)return Reflect.metadata(e,t)}function u(e,t,n,r){return new(n||(n=Promise))((function(o,i){function a(e){try{l(r.next(e))}catch(e){i(e)}}function s(e){try{l(r.throw(e))}catch(e){i(e)}}function l(e){var t;e.done?o(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(a,s)}l((r=r.apply(e,t||[])).next())}))}function p(e,t){var n,r,o,i,a={label:0,sent:function(){if(1&o[0])throw o[1];return o[1]},trys:[],ops:[]};return i={next:s(0),throw:s(1),return:s(2)},"function"==typeof Symbol&&(i[Symbol.iterator]=function(){return this}),i;function s(i){return function(s){return function(i){if(n)throw new TypeError("Generator is already executing.");for(;a;)try{if(n=1,r&&(o=2&i[0]?r.return:i[0]?r.throw||((o=r.return)&&o.call(r),0):r.next)&&!(o=o.call(r,i[1])).done)return o;switch(r=0,o&&(i=[2&i[0],o.value]),i[0]){case 0:case 1:o=i;break;case 4:return a.label++,{value:i[1],done:!1};case 5:a.label++,r=i[1],i=[0];continue;case 7:i=a.ops.pop(),a.trys.pop();continue;default:if(!(o=(o=a.trys).length>0&&o[o.length-1])&&(6===i[0]||2===i[0])){a=0;continue}if(3===i[0]&&(!o||i[1]>o[0]&&i[1]=e.length&&(e=void 0),{value:e&&e[r++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")}function h(e,t){var n="function"==typeof Symbol&&e[Symbol.iterator];if(!n)return e;var r,o,i=n.call(e),a=[];try{for(;(void 0===t||t-- >0)&&!(r=i.next()).done;)a.push(r.value)}catch(e){o={error:e}}finally{try{r&&!r.done&&(n=i.return)&&n.call(i)}finally{if(o)throw o.error}}return a}function m(){for(var e=[],t=0;t1||s(e,t)}))})}function s(e,t){try{(n=o[e](t)).value instanceof y?Promise.resolve(n.value.v).then(l,c):u(i[0][2],n)}catch(e){u(i[0][3],e)}var n}function l(e){s("next",e)}function c(e){s("throw",e)}function u(e,t){e(t),i.shift(),i.length&&s(i[0][0],i[0][1])}}function b(e){var t,n;return t={},r("next"),r("throw",(function(e){throw e})),r("return"),t[Symbol.iterator]=function(){return this},t;function r(r,o){t[r]=e[r]?function(t){return(n=!n)?{value:y(e[r](t)),done:"return"===r}:o?o(t):t}:o}}function x(e){if(!Symbol.asyncIterator)throw new TypeError("Symbol.asyncIterator is not defined.");var t,n=e[Symbol.asyncIterator];return n?n.call(e):(e=d(e),t={},r("next"),r("throw"),r("return"),t[Symbol.asyncIterator]=function(){return this},t);function r(n){t[n]=e[n]&&function(t){return new Promise((function(r,o){(function(e,t,n,r){Promise.resolve(r).then((function(t){e({value:t,done:n})}),t)})(r,o,(t=e[n](t)).done,t.value)}))}}}function w(e,t){return Object.defineProperty?Object.defineProperty(e,"raw",{value:t}):e.raw=t,e}function k(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)Object.hasOwnProperty.call(e,n)&&(t[n]=e[n]);return t.default=e,t}function O(e){return e&&e.__esModule?e:{default:e}}function _(e,t){if(!t.has(e))throw new TypeError("attempted to get private field on non-instance");return t.get(e)}function E(e,t,n){if(!t.has(e))throw new TypeError("attempted to set private field on non-instance");return t.set(e,n),n}},function(e,t,n){"use strict";(function(e,r){n.d(t,"a",(function(){return pn})),n.d(t,"b",(function(){return qe})),n.d(t,"c",(function(){return Se})),n.d(t,"d",(function(){return ot})),n.d(t,"e",(function(){return le})),n.d(t,"f",(function(){return ft})),n.d(t,"g",(function(){return L})),n.d(t,"h",(function(){return ht})),n.d(t,"i",(function(){return $t})),n.d(t,"j",(function(){return Vt})),n.d(t,"k",(function(){return rn})),n.d(t,"l",(function(){return ne})),n.d(t,"m",(function(){return bt})),n.d(t,"n",(function(){return it})),n.d(t,"o",(function(){return et})),n.d(t,"p",(function(){return wt})),n.d(t,"q",(function(){return me})); + /*! ***************************************************************************** + Copyright (c) Microsoft Corporation. All rights reserved. + Licensed under the Apache License, Version 2.0 (the "License"); you may not use + this file except in compliance with the License. You may obtain a copy of the + License at http://www.apache.org/licenses/LICENSE-2.0 + + THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED + WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, + MERCHANTABLITY OR NON-INFRINGEMENT. + + See the Apache Version 2.0 License for specific language governing permissions + and limitations under the License. + ***************************************************************************** */ + var o=function(e,t){return(o=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(e,t)};function i(e,t){function n(){this.constructor=e}o(e,t),e.prototype=null===t?Object.create(t):(n.prototype=t.prototype,new n)}var a=function(){return(a=Object.assign||function(e){for(var t,n=1,r=arguments.length;n0)&&!(r=i.next()).done;)a.push(r.value)}catch(e){o={error:e}}finally{try{r&&!r.done&&(n=i.return)&&n.call(i)}finally{if(o)throw o.error}}return a}function l(){for(var e=[],t=0;t2&&re("box");var n=G(t);return new Ce(e,X(n),n.name,!0,n.equals)},shallowBox:function(e,t){return arguments.length>2&&re("shallowBox"),ne.box(e,{name:t,deep:!1})},array:function(e,t){arguments.length>2&&re("array");var n=G(t);return new Mt(e,X(n),n.name)},shallowArray:function(e,t){return arguments.length>2&&re("shallowArray"),ne.array(e,{name:t,deep:!1})},map:function(e,t){arguments.length>2&&re("map");var n=G(t);return new Wt(e,X(n),n.name)},shallowMap:function(e,t){return arguments.length>2&&re("shallowMap"),ne.map(e,{name:t,deep:!1})},set:function(e,t){arguments.length>2&&re("set");var n=G(t);return new Qt(e,X(n),n.name)},object:function(e,t,n){"string"==typeof arguments[1]&&re("object");var r=G(n);return dt({},e,t,r)},shallowObject:function(e,t){return"string"==typeof arguments[1]&&re("shallowObject"),ne.object(e,{},{name:t,deep:!1})},ref:J,shallow:Z,deep:K,struct:ee},ne=function(e,t,n){if("string"==typeof arguments[1])return K.apply(null,arguments);if(vt(e))return e;var r=b(e)?ne.object(e,t,n):Array.isArray(e)?ne.array(e,t):O(e)?ne.map(e,t):_(e)?ne.set(e,t):e;if(r!==e)return r;h(!1)};function re(e){h("Expected one or two arguments to observable."+e+". Did you accidentally try to use observable."+e+" as decorator?")}Object.keys(te).forEach((function(e){return ne[e]=te[e]}));var oe,ie,ae=$(!1,(function(e,t,n,r,o){var i=n.get,s=n.set,l=o[0]||{};!function(e,t,n){var r=Kt(e);n.name=r.name+"."+t,n.context=e,r.values[t]=new Ie(n),Object.defineProperty(e,t,function(e){return en[e]||(en[e]={configurable:Le.computedConfigurable,enumerable:!1,get:function(){return tn(this).read(this,e)},set:function(t){tn(this).write(this,e,t)}})}(t))}(e,t,a({get:i,set:s},l))})),se=ae({equals:D.structural}),le=function(e,t,n){if("string"==typeof t)return ae.apply(null,arguments);if(null!==e&&"object"==typeof e&&1===arguments.length)return ae.apply(null,arguments);var r="object"==typeof t?t:{};return r.get=e,r.set="function"==typeof t?t:r.set,r.name=r.name||e.name||"",new Ie(r)};le.struct=se,function(e){e[e.NOT_TRACKING=-1]="NOT_TRACKING",e[e.UP_TO_DATE=0]="UP_TO_DATE",e[e.POSSIBLY_STALE=1]="POSSIBLY_STALE",e[e.STALE=2]="STALE"}(oe||(oe={})),function(e){e[e.NONE=0]="NONE",e[e.LOG=1]="LOG",e[e.BREAK=2]="BREAK"}(ie||(ie={}));var ce=function(e){this.cause=e};function ue(e){return e instanceof ce}function pe(e){switch(e.dependenciesState){case oe.UP_TO_DATE:return!1;case oe.NOT_TRACKING:case oe.STALE:return!0;case oe.POSSIBLY_STALE:for(var t=ve(!0),n=ge(),r=e.observing,o=r.length,i=0;i0;Le.computationDepth>0&&t&&h(!1),Le.allowStateChanges||!t&&"strict"!==Le.enforceActions||h(!1)}function de(e,t,n){var r=ve(!0);xe(e),e.newObserving=new Array(e.observing.length+100),e.unboundDepsCount=0,e.runId=++Le.runId;var o,i=Le.trackingDerivation;if(Le.trackingDerivation=e,!0===Le.disableErrorBoundaries)o=t.call(n);else try{o=t.call(n)}catch(e){o=new ce(e)}return Le.trackingDerivation=i,function(e){for(var t=e.observing,n=e.observing=e.newObserving,r=oe.UP_TO_DATE,o=0,i=e.unboundDepsCount,a=0;ar&&(r=s.dependenciesState)}n.length=o,e.newObserving=null,i=t.length;for(;i--;){0===(s=t[i]).diffValue&&De(s,e),s.diffValue=0}for(;o--;){var s;1===(s=n[o]).diffValue&&(s.diffValue=0,Me(s,e))}r!==oe.UP_TO_DATE&&(e.dependenciesState=r,e.onBecomeStale())}(e),e.observing.length,be(r),o}function he(e){var t=e.observing;e.observing=[];for(var n=t.length;n--;)De(t[n],e);e.dependenciesState=oe.NOT_TRACKING}function me(e){var t=ge(),n=e();return ye(t),n}function ge(){var e=Le.trackingDerivation;return Le.trackingDerivation=null,e}function ye(e){Le.trackingDerivation=e}function ve(e){var t=Le.allowStateReads;return Le.allowStateReads=e,t}function be(e){Le.allowStateReads=e}function xe(e){if(e.dependenciesState!==oe.UP_TO_DATE){e.dependenciesState=oe.UP_TO_DATE;for(var t=e.observing,n=t.length;n--;)t[n].lowestObserverState=oe.UP_TO_DATE}}var we=0,ke=1,Oe=Object.getOwnPropertyDescriptor((function(){}),"name");Oe&&Oe.configurable;function _e(e,t){var n=function(){return Ee(e,t,this,arguments)};return n.isMobxAction=!0,n}function Ee(e,t,n,r){var o=function(e,t,n){var r=Ge()&&!!e,o=0;if(r){o=Date.now();var i=n&&n.length||0,a=new Array(i);if(i>0)for(var s=0;s0&&!e.__mobxGlobals&&(Re=!1),e.__mobxGlobals&&e.__mobxGlobals.version!==(new Pe).version&&(Re=!1),Re?e.__mobxGlobals?(e.__mobxInstanceCount+=1,e.__mobxGlobals.UNCHANGED||(e.__mobxGlobals.UNCHANGED={}),e.__mobxGlobals):(e.__mobxInstanceCount=1,e.__mobxGlobals=new Pe):(setTimeout((function(){Ne||h("There are multiple, different versions of MobX active. Make sure MobX is loaded only once or use `configure({ isolateGlobalState: true })`")}),1),new Pe)}();function Me(e,t){var n=e.observers.length;n&&(e.observersIndexes[t.__mapid]=n),e.observers[n]=t,e.lowestObserverState>t.dependenciesState&&(e.lowestObserverState=t.dependenciesState)}function De(e,t){if(1===e.observers.length)e.observers.length=0,Fe(e);else{var n=e.observers,r=e.observersIndexes,o=n.pop();if(o!==t){var i=r[t.__mapid]||0;i?r[o.__mapid]=i:delete r[o.__mapid],n[i]=o}delete r[t.__mapid]}}function Fe(e){!1===e.isPendingUnobservation&&(e.isPendingUnobservation=!0,Le.pendingUnobservations.push(e))}function ze(){Le.inBatch++}function Ue(){if(0==--Le.inBatch){He();for(var e=Le.pendingUnobservations,t=0;t0&&Fe(e),!1)}function $e(e,t){if(console.log("[mobx.trace] '"+e.name+"' is invalidated due to a change in: '"+t.name+"'"),e.isTracing===ie.BREAK){var n=[];!function e(t,n,r){if(n.length>=1e3)return void n.push("(and many more)");n.push(""+new Array(r).join("\t")+t.name),t.dependencies&&t.dependencies.forEach((function(t){return e(t,n,r+1)}))}(ht(e),n,1),new Function("debugger;\n/*\nTracing '"+e.name+"'\n\nYou are entering this break point because derivation '"+e.name+"' is being traced and '"+t.name+"' is now forcing it to update.\nJust follow the stacktrace you should now see in the devtools to see precisely what piece of your code is causing this update\nThe stackframe you are looking for is at least ~6-8 stack-frames up.\n\n"+(e instanceof Ie?e.derivation.toString().replace(/[*]\//g,"/"):"")+"\n\nThe dependencies for this derivation are:\n\n"+n.join("\n")+"\n*/\n ")()}}var qe=function(){function e(e,t,n,r){void 0===e&&(e="Reaction@"+d()),void 0===r&&(r=!1),this.name=e,this.onInvalidate=t,this.errorHandler=n,this.requiresObservable=r,this.observing=[],this.newObserving=[],this.dependenciesState=oe.NOT_TRACKING,this.diffValue=0,this.runId=0,this.unboundDepsCount=0,this.__mapid="#"+d(),this.isDisposed=!1,this._isScheduled=!1,this._isTrackPending=!1,this._isRunning=!1,this.isTracing=ie.NONE}return e.prototype.onBecomeStale=function(){this.schedule()},e.prototype.schedule=function(){this._isScheduled||(this._isScheduled=!0,Le.pendingReactions.push(this),He())},e.prototype.isScheduled=function(){return this._isScheduled},e.prototype.runReaction=function(){if(!this.isDisposed){if(ze(),this._isScheduled=!1,pe(this)){this._isTrackPending=!0;try{this.onInvalidate(),this._isTrackPending&&Ge()&&Xe({name:this.name,type:"scheduled-reaction"})}catch(e){this.reportExceptionInDerivation(e)}}Ue()}},e.prototype.track=function(e){ze();var t,n=Ge();n&&(t=Date.now(),Ke({name:this.name,type:"reaction"})),this._isRunning=!0;var r=de(this,e,void 0);this._isRunning=!1,this._isTrackPending=!1,this.isDisposed&&he(this),ue(r)&&this.reportExceptionInDerivation(r.cause),n&&Je({time:Date.now()-t}),Ue()},e.prototype.reportExceptionInDerivation=function(e){var t=this;if(this.errorHandler)this.errorHandler(e,this);else{if(Le.disableErrorBoundaries)throw e;var n="[mobx] Encountered an uncaught exception that was thrown by a reaction or observer component, in: '"+this+"'";Le.suppressReactionErrors?console.warn("[mobx] (error in reaction '"+this.name+"' suppressed, fix error of causing action below)"):console.error(n,e),Ge()&&Xe({type:"error",name:this.name,message:n,error:""+e}),Le.globalReactionErrorHandlers.forEach((function(n){return n(e,t)}))}},e.prototype.dispose=function(){this.isDisposed||(this.isDisposed=!0,this._isRunning||(ze(),he(this),Ue()))},e.prototype.getDisposer=function(){var e=this.dispose.bind(this);return e.$mobx=this,e},e.prototype.toString=function(){return"Reaction["+this.name+"]"},e.prototype.trace=function(e){void 0===e&&(e=!1),function(){for(var e=[],t=0;t0||Le.isRunningReactions||We(Ve)}function Ve(){Le.isRunningReactions=!0;for(var e=Le.pendingReactions,t=0;e.length>0;){100==++t&&(console.error("Reaction doesn't converge to a stable state after 100 iterations. Probably there is a cycle in the reactive function: "+e[0]),e.splice(0));for(var n=e.splice(0),r=0,o=n.length;r",e):2===arguments.length&&"function"==typeof t?_e(e,t):1===arguments.length&&"string"==typeof e?nt(e):!0!==r?nt(t).apply(null,arguments):void(e[t]=_e(e.name||t,n.value))};function it(e,t){return Ee("string"==typeof e?e:e.name||"","function"==typeof e?e:t,this,void 0)}function at(e,t,n){x(e,t,_e(t,n.bind(e)))}function st(e,t){void 0===t&&(t=u);var n,r=t&&t.name||e.name||"Autorun@"+d();if(!t.scheduler&&!t.delay)n=new qe(r,(function(){this.track(a)}),t.onError,t.requiresObservable);else{var o=ct(t),i=!1;n=new qe(r,(function(){i||(i=!0,o((function(){i=!1,n.isDisposed||n.track(a)})))}),t.onError,t.requiresObservable)}function a(){e(n)}return n.schedule(),n.getDisposer()}ot.bound=function(e,t,n,r){return!0===r?(at(e,t,n.value),null):n?{configurable:!0,enumerable:!1,get:function(){return at(this,t,n.value||n.initializer.call(this)),this[t]},set:tt}:{enumerable:!1,configurable:!0,set:function(e){at(this,t,e)},get:function(){}}};var lt=function(e){return e()};function ct(e){return e.scheduler?e.scheduler:e.delay?function(t){return setTimeout(t,e.delay)}:lt}function ut(e,t,n){return pt("onBecomeUnobserved",e,t,n)}function pt(e,t,n,r){var o="function"==typeof r?on(t,n):on(t),i="function"==typeof r?r:n,a=o[e];return"function"!=typeof a?h(!1):(o[e]=function(){a.call(this),i.call(this)},function(){o[e]=a})}function ft(e){var t=e.enforceActions,n=e.computedRequiresReaction,r=e.computedConfigurable,o=e.disableErrorBoundaries,i=e.arrayBuffer,a=e.reactionScheduler,s=e.reactionRequiresObservable,l=e.observableRequiresReaction;if(!0===e.isolateGlobalState&&((Le.pendingReactions.length||Le.inBatch||Le.isRunningReactions)&&h("isolateGlobalState should be called before MobX is running any reactions"),Ne=!0,Re&&(0==--f().__mobxInstanceCount&&(f().__mobxGlobals=void 0),Le=new Pe)),void 0!==t){var c=void 0;switch(t){case!0:case"observed":c=!0;break;case!1:case"never":c=!1;break;case"strict":case"always":c="strict";break;default:h("Invalid value for 'enforceActions': '"+t+"', expected 'never', 'always' or 'observed'")}Le.enforceActions=c,Le.allowStateChanges=!0!==c&&"strict"!==c}void 0!==n&&(Le.computedRequiresReaction=!!n),void 0!==s&&(Le.reactionRequiresObservable=!!s),void 0!==l&&(Le.observableRequiresReaction=!!l,Le.allowStateReads=!Le.observableRequiresReaction),void 0!==r&&(Le.computedConfigurable=!!r),void 0!==o&&(!0===o&&console.warn("WARNING: Debug feature only. MobX will NOT recover from errors if this is on."),Le.disableErrorBoundaries=!!o),"number"==typeof i&&Ut(i),a&&Qe(a)}function dt(e,t,n,r){var o=(r=G(r)).defaultDecorator||(!1===r.deep?J:K);B(e),Kt(e,r.name,o.enhancer),ze();try{for(var i in t){var a=Object.getOwnPropertyDescriptor(t,i);0;var s=(n&&i in n?n[i]:a.get?ae:o)(e,i,a,!0);s&&Object.defineProperty(e,i,s)}}finally{Ue()}return e}function ht(e,t){return mt(on(e,t))}function mt(e){var t,n,r={name:e.name};return e.observing&&e.observing.length>0&&(r.dependencies=(t=e.observing,n=[],t.forEach((function(e){-1===n.indexOf(e)&&n.push(e)})),n).map(mt)),r}function gt(){this.message="FLOW_CANCELLED"}function yt(e,t){if(null==e)return!1;if(void 0!==t){if(rn(e)){var n=e.$mobx;return n.values&&!!n.values[t]}return!1}return rn(e)||!!e.$mobx||N(e)||Ye(e)||Ae(e)}function vt(e){return 1!==arguments.length&&h(!1),yt(e)}function bt(e,t,n,r){return"function"==typeof n?function(e,t,n,r){return an(e,t).observe(n,r)}(e,t,n,r):function(e,t,n){return an(e).observe(t,n)}(e,t,n)}gt.prototype=Object.create(Error.prototype);function xt(e){switch(e.length){case 0:return Le.trackingDerivation;case 1:return on(e[0]);case 2:return on(e[0],e[1])}}function wt(e,t){void 0===t&&(t=void 0),ze();try{return e.apply(t)}finally{Ue()}}function kt(e){return void 0!==e.interceptors&&e.interceptors.length>0}function Ot(e,t){var n=e.interceptors||(e.interceptors=[]);return n.push(t),g((function(){var e=n.indexOf(t);-1!==e&&n.splice(e,1)}))}function _t(e,t){var n=ge();try{var r=e.interceptors;if(r)for(var o=0,i=r.length;o0}function St(e,t){var n=e.changeListeners||(e.changeListeners=[]);return n.push(t),g((function(){var e=n.indexOf(t);-1!==e&&n.splice(e,1)}))}function Tt(e,t){var n=ge(),r=e.changeListeners;if(r){for(var o=0,i=(r=r.slice()).length;o0?e.map(this.dehancer):e},e.prototype.intercept=function(e){return Ot(this,e)},e.prototype.observe=function(e,t){return void 0===t&&(t=!1),t&&e({object:this.array,type:"splice",index:0,added:this.values.slice(),addedCount:this.values.length,removed:[],removedCount:0}),St(this,e)},e.prototype.getArrayLength=function(){return this.atom.reportObserved(),this.values.length},e.prototype.setArrayLength=function(e){if("number"!=typeof e||e<0)throw new Error("[mobx.array] Out of range: "+e);var t=this.values.length;if(e!==t)if(e>t){for(var n=new Array(e-t),r=0;r0&&e+t+1>Rt&&Ut(e+t+1)},e.prototype.spliceWithArray=function(e,t,n){var r=this;fe(this.atom);var o=this.values.length;if(void 0===e?e=0:e>o?e=o:e<0&&(e=Math.max(0,o+e)),t=1===arguments.length?o-e:null==t?0:Math.max(0,Math.min(t,o-e)),void 0===n&&(n=c),kt(this)){var i=_t(this,{object:this.array,type:"splice",index:e,removedCount:t,added:n});if(!i)return c;t=i.removedCount,n=i.added}var a=(n=0===n.length?n:n.map((function(e){return r.enhancer(e,void 0)}))).length-t;this.updateArrayLength(o,a);var s=this.spliceItemsIntoValues(e,t,n);return 0===t&&0===n.length||this.notifyArraySplice(e,n,s),this.dehanceValues(s)},e.prototype.spliceItemsIntoValues=function(e,t,n){var r;if(n.length<1e4)return(r=this.values).splice.apply(r,l([e,t],n));var o=this.values.slice(e,e+t);return this.values=this.values.slice(0,e).concat(n,this.values.slice(e+t)),o},e.prototype.notifyArrayChildUpdate=function(e,t,n){var r=!this.owned&&Ge(),o=Et(this),i=o||r?{object:this.array,type:"update",index:e,newValue:t,oldValue:n}:null;r&&Ke(a(a({},i),{name:this.atom.name})),this.atom.reportChanged(),o&&Tt(this,i),r&&Je()},e.prototype.notifyArraySplice=function(e,t,n){var r=!this.owned&&Ge(),o=Et(this),i=o||r?{object:this.array,type:"splice",index:e,removed:n,added:t,removedCount:n.length,addedCount:t.length}:null;r&&Ke(a(a({},i),{name:this.atom.name})),this.atom.reportChanged(),o&&Tt(this,i),r&&Je()},e}(),Mt=function(e){function t(t,n,r,o){void 0===r&&(r="ObservableArray@"+d()),void 0===o&&(o=!1);var i=e.call(this)||this,a=new Lt(r,n,i,o);if(w(i,"$mobx",a),t&&t.length){var s=Te(!0);i.spliceWithArray(0,0,t),je(s)}return Pt&&Object.defineProperty(a.array,"0",Dt),i}return i(t,e),t.prototype.intercept=function(e){return this.$mobx.intercept(e)},t.prototype.observe=function(e,t){return void 0===t&&(t=!1),this.$mobx.observe(e,t)},t.prototype.clear=function(){return this.splice(0)},t.prototype.concat=function(){for(var e=[],t=0;t-1&&(this.splice(t,1),!0)},t.prototype.move=function(e,t){function n(e){if(e<0)throw new Error("[mobx.array] Index out of bounds: "+e+" is negative");var t=this.$mobx.values.length;if(e>=t)throw new Error("[mobx.array] Index out of bounds: "+e+" is not smaller than "+t)}if(n.call(this,e),n.call(this,t),e!==t){var r,o=this.$mobx.values;r=e0;)r[o]=arguments[o+2];t.locks++;try{var i;return null!=e&&(i=e.apply(this,r)),i}finally{t.locks--,0===t.locks&&t.methods.forEach((function(e){e.apply(n,r)}))}}function y(e,t){return function(){for(var n=[],r=arguments.length;r--;)n[r]=arguments[r];g.call.apply(g,[this,e,t].concat(n))}}function v(e,t,n){var r=function(e,t){var n=e[h]=e[h]||{},r=n[t]=n[t]||{};return r.locks=r.locks||0,r.methods=r.methods||[],r}(e,t);r.methods.indexOf(n)<0&&r.methods.push(n);var o=Object.getOwnPropertyDescriptor(e,t);if(!o||!o[m]){var i=e[t],a=function e(t,n,r,o,i){var a,s=y(i,o);return(a={})[m]=!0,a.get=function(){return s},a.set=function(i){if(this===t)s=y(i,o);else{var a=e(this,n,r,o,i);Object.defineProperty(this,n,a)}},a.configurable=!0,a.enumerable=r,a}(e,t,o?o.enumerable:void 0,r,i);Object.defineProperty(e,t,a)}}var b=s.a||"$mobx",x=u("isUnmounted"),w=u("skipRender"),k=u("isForcingUpdate");function O(e){var t=e.prototype;if(t.componentWillReact)throw new Error("The componentWillReact life-cycle event is no longer supported");if(e.__proto__!==i.PureComponent)if(t.shouldComponentUpdate){if(t.shouldComponentUpdate!==E)throw new Error("It is not allowed to use shouldComponentUpdate in observer based components.")}else t.shouldComponentUpdate=E;S(t,"props"),S(t,"state");var n=t.render;return t.render=function(){return _.call(this,n)},v(t,"componentWillUnmount",(function(){if(!0!==Object(o.b)()){if(this.render[b])this.render[b].dispose();else;this[x]=!0}})),e}function _(e){var t=this;if(!0===Object(o.b)())return e.call(this);d(this,w,!1),d(this,k,!1);var n,r=(n=this).displayName||n.name||n.constructor&&(n.constructor.displayName||n.constructor.name)||"",a=e.bind(this),l=!1,c=new s.b(r+".render()",(function(){if(!l&&(l=!0,!0!==t[x])){var e=!0;try{d(t,k,!0),t[w]||i.Component.prototype.forceUpdate.call(t),e=!1}finally{d(t,k,!1),e&&c.dispose()}}}));function u(){l=!1;var e=void 0,t=void 0;if(c.track((function(){try{t=Object(s.c)(!1,a)}catch(t){e=t}})),e)throw e;return t}return c.reactComponent=this,u[b]=c,this.render=u,u.call(this)}function E(e,t){return Object(o.b)()&&console.warn("[mobx-react] It seems that a re-rendering of a React component is triggered while in static (server-side) mode. Please make sure components are rendered only once server-side."),this.state!==t||!p(this.props,e)}function S(e,t){var n=u("reactProp_"+t+"_valueHolder"),r=u("reactProp_"+t+"_atomHolder");function o(){return this[r]||d(this,r,Object(s.g)("reactive "+t)),this[r]}Object.defineProperty(e,t,{configurable:!0,enumerable:!0,get:function(){return o.call(this).reportObserved(),this[n]},set:function(e){this[k]||p(this[n],e)?d(this,n,e):(d(this,n,e),d(this,w,!0),o.call(this).reportChanged(),d(this,w,!1))}})}var T="function"==typeof Symbol&&Symbol.for,j=T?Symbol.for("react.forward_ref"):"function"==typeof i.forwardRef&&Object(i.forwardRef)((function(){})).$$typeof,C=T?Symbol.for("react.memo"):"function"==typeof i.memo&&Object(i.memo)((function(){})).$$typeof;function I(e){if(!0===e.isMobxInjector&&console.warn("Mobx observer: You are trying to use 'observer' on a component that already has 'inject'. Please apply 'observer' before applying 'inject'"),C&&e.$$typeof===C)throw new Error("Mobx observer: You are trying to use 'observer' on function component wrapped to either another observer or 'React.memo'. The observer already applies 'React.memo' for you.");if(j&&e.$$typeof===j){var t=e.render;if("function"!=typeof t)throw new Error("render property of ForwardRef was not a function");return Object(i.forwardRef)((function(){var e=arguments;return a.a.createElement(o.a,null,(function(){return t.apply(void 0,e)}))}))}return"function"!=typeof e||e.prototype&&e.prototype.render||e.isReactClass||Object.prototype.isPrototypeOf.call(i.Component,e)?O(e):Object(o.c)(e)}a.a.createContext({});u("disposeOnUnmountProto"),u("disposeOnUnmountInst");function A(e){function t(t,n,r,o,i,a){for(var l=[],c=arguments.length-6;c-- >0;)l[c]=arguments[c+6];return Object(s.q)((function(){if(o=o||"<>",a=a||r,null==n[r]){if(t){var s=null===n[r]?"null":"undefined";return new Error("The "+i+" `"+a+"` is marked as required in `"+o+"`, but its value is `"+s+"`.")}return null}return e.apply(void 0,[n,r,o,i,a].concat(l))}))}var n=t.bind(null,!1);return n.isRequired=t.bind(null,!0),n}function P(e){var t=typeof e;return Array.isArray(e)?"array":e instanceof RegExp?"object":function(e,t){return"symbol"===e||("Symbol"===t["@@toStringTag"]||"function"==typeof Symbol&&t instanceof Symbol)}(t,e)?"symbol":t}function R(e,t){return A((function(n,r,o,i,a){return Object(s.q)((function(){if(e&&P(n[r])===t.toLowerCase())return null;var i;switch(t){case"Array":i=s.i;break;case"Object":i=s.k;break;case"Map":i=s.j;break;default:throw new Error("Unexpected mobxType: "+t)}var l=n[r];if(!i(l)){var c=function(e){var t=P(e);if("object"===t){if(e instanceof Date)return"date";if(e instanceof RegExp)return"regexp"}return t}(l),u=e?" or javascript `"+t.toLowerCase()+"`":"";return new Error("Invalid prop `"+a+"` of type `"+c+"` supplied to `"+o+"`, expected `mobx.Observable"+t+"`"+u+".")}return null}))}))}function N(e,t){return A((function(n,r,o,i,a){for(var l=[],c=arguments.length-5;c-- >0;)l[c]=arguments[c+5];return Object(s.q)((function(){if("function"!=typeof t)return new Error("Property `"+a+"` of component `"+o+"` has invalid PropType notation.");var s=R(e,"Array")(n,r,o);if(s instanceof Error)return s;for(var c=n[r],u=0;u",'"',"`"," ","\r","\n","\t"]),u=["'"].concat(c),p=["%","/","?",";","#"].concat(u),f=["/","?","#"],d=/^[+a-z0-9A-Z_-]{0,63}$/,h=/^([+a-z0-9A-Z_-]{0,63})(.*)$/,m={javascript:!0,"javascript:":!0},g={javascript:!0,"javascript:":!0},y={http:!0,https:!0,ftp:!0,gopher:!0,file:!0,"http:":!0,"https:":!0,"ftp:":!0,"gopher:":!0,"file:":!0},v=n(235);function b(e,t,n){if(e&&o.isObject(e)&&e instanceof i)return e;var r=new i;return r.parse(e,t,n),r}i.prototype.parse=function(e,t,n){if(!o.isString(e))throw new TypeError("Parameter 'url' must be a string, not "+typeof e);var i=e.indexOf("?"),s=-1!==i&&i127?R+="x":R+=P[N];if(!R.match(d)){var M=I.slice(0,T),D=I.slice(T+1),F=P.match(h);F&&(M.push(F[1]),D.unshift(F[2])),D.length&&(b="/"+D.join(".")+b),this.hostname=M.join(".");break}}}this.hostname.length>255?this.hostname="":this.hostname=this.hostname.toLowerCase(),C||(this.hostname=r.toASCII(this.hostname));var z=this.port?":"+this.port:"",U=this.hostname||"";this.host=U+z,this.href+=this.host,C&&(this.hostname=this.hostname.substr(1,this.hostname.length-2),"/"!==b[0]&&(b="/"+b))}if(!m[k])for(T=0,A=u.length;T0)&&n.host.split("@"))&&(n.auth=C.shift(),n.host=n.hostname=C.shift());return n.search=e.search,n.query=e.query,o.isNull(n.pathname)&&o.isNull(n.search)||(n.path=(n.pathname?n.pathname:"")+(n.search?n.search:"")),n.href=n.format(),n}if(!O.length)return n.pathname=null,n.search?n.path="/"+n.search:n.path=null,n.href=n.format(),n;for(var E=O.slice(-1)[0],S=(n.host||e.host||O.length>1)&&("."===E||".."===E)||""===E,T=0,j=O.length;j>=0;j--)"."===(E=O[j])?O.splice(j,1):".."===E?(O.splice(j,1),T++):T&&(O.splice(j,1),T--);if(!w&&!k)for(;T--;T)O.unshift("..");!w||""===O[0]||O[0]&&"/"===O[0].charAt(0)||O.unshift(""),S&&"/"!==O.join("/").substr(-1)&&O.push("");var C,I=""===O[0]||O[0]&&"/"===O[0].charAt(0);_&&(n.hostname=n.host=I?"":O.length?O.shift():"",(C=!!(n.host&&n.host.indexOf("@")>0)&&n.host.split("@"))&&(n.auth=C.shift(),n.host=n.hostname=C.shift()));return(w=w||n.host&&O.length)&&!I&&O.unshift(""),O.length?n.pathname=O.join("/"):(n.pathname=null,n.path=null),o.isNull(n.pathname)&&o.isNull(n.search)||(n.path=(n.pathname?n.pathname:"")+(n.search?n.search:"")),n.auth=e.auth||n.auth,n.slashes=n.slashes||e.slashes,n.href=n.format(),n},i.prototype.parseHost=function(){var e=this.host,t=s.exec(e);t&&(":"!==(t=t[0])&&(this.port=t.substr(1)),e=e.substr(0,e.length-t.length)),e&&(this.hostname=e)}},function(e,t){var n={}.hasOwnProperty;e.exports=function(e,t){return n.call(e,t)}},function(e,t,n){var r=n(37),o=n(11),i=n(129),a=n(16).f;e.exports=function(e){var t=r.Symbol||(r.Symbol={});o(t,e)||a(t,e,{value:i.f(e)})}},function(e,t){var n,r,o=e.exports={};function i(){throw new Error("setTimeout has not been defined")}function a(){throw new Error("clearTimeout has not been defined")}function s(e){if(n===setTimeout)return setTimeout(e,0);if((n===i||!n)&&setTimeout)return n=setTimeout,setTimeout(e,0);try{return n(e,0)}catch(t){try{return n.call(null,e,0)}catch(t){return n.call(this,e,0)}}}!function(){try{n="function"==typeof setTimeout?setTimeout:i}catch(e){n=i}try{r="function"==typeof clearTimeout?clearTimeout:a}catch(e){r=a}}();var l,c=[],u=!1,p=-1;function f(){u&&l&&(u=!1,l.length?c=l.concat(c):p=-1,c.length&&d())}function d(){if(!u){var e=s(f);u=!0;for(var t=c.length;t;){for(l=c,c=[];++p1)for(var n=1;n + * @license MIT + */ + var r=n(239),o=n(240),i=n(131);function a(){return l.TYPED_ARRAY_SUPPORT?2147483647:1073741823}function s(e,t){if(a()=a())throw new RangeError("Attempt to allocate Buffer larger than maximum size: 0x"+a().toString(16)+" bytes");return 0|e}function h(e,t){if(l.isBuffer(e))return e.length;if("undefined"!=typeof ArrayBuffer&&"function"==typeof ArrayBuffer.isView&&(ArrayBuffer.isView(e)||e instanceof ArrayBuffer))return e.byteLength;"string"!=typeof e&&(e=""+e);var n=e.length;if(0===n)return 0;for(var r=!1;;)switch(t){case"ascii":case"latin1":case"binary":return n;case"utf8":case"utf-8":case void 0:return U(e).length;case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return 2*n;case"hex":return n>>>1;case"base64":return B(e).length;default:if(r)return U(e).length;t=(""+t).toLowerCase(),r=!0}}function m(e,t,n){var r=!1;if((void 0===t||t<0)&&(t=0),t>this.length)return"";if((void 0===n||n>this.length)&&(n=this.length),n<=0)return"";if((n>>>=0)<=(t>>>=0))return"";for(e||(e="utf8");;)switch(e){case"hex":return C(this,t,n);case"utf8":case"utf-8":return S(this,t,n);case"ascii":return T(this,t,n);case"latin1":case"binary":return j(this,t,n);case"base64":return E(this,t,n);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return I(this,t,n);default:if(r)throw new TypeError("Unknown encoding: "+e);e=(e+"").toLowerCase(),r=!0}}function g(e,t,n){var r=e[t];e[t]=e[n],e[n]=r}function y(e,t,n,r,o){if(0===e.length)return-1;if("string"==typeof n?(r=n,n=0):n>2147483647?n=2147483647:n<-2147483648&&(n=-2147483648),n=+n,isNaN(n)&&(n=o?0:e.length-1),n<0&&(n=e.length+n),n>=e.length){if(o)return-1;n=e.length-1}else if(n<0){if(!o)return-1;n=0}if("string"==typeof t&&(t=l.from(t,r)),l.isBuffer(t))return 0===t.length?-1:v(e,t,n,r,o);if("number"==typeof t)return t&=255,l.TYPED_ARRAY_SUPPORT&&"function"==typeof Uint8Array.prototype.indexOf?o?Uint8Array.prototype.indexOf.call(e,t,n):Uint8Array.prototype.lastIndexOf.call(e,t,n):v(e,[t],n,r,o);throw new TypeError("val must be string, number or Buffer")}function v(e,t,n,r,o){var i,a=1,s=e.length,l=t.length;if(void 0!==r&&("ucs2"===(r=String(r).toLowerCase())||"ucs-2"===r||"utf16le"===r||"utf-16le"===r)){if(e.length<2||t.length<2)return-1;a=2,s/=2,l/=2,n/=2}function c(e,t){return 1===a?e[t]:e.readUInt16BE(t*a)}if(o){var u=-1;for(i=n;is&&(n=s-l),i=n;i>=0;i--){for(var p=!0,f=0;fo&&(r=o):r=o;var i=t.length;if(i%2!=0)throw new TypeError("Invalid hex string");r>i/2&&(r=i/2);for(var a=0;a>8,o=n%256,i.push(o),i.push(r);return i}(t,e.length-n),e,n,r)}function E(e,t,n){return 0===t&&n===e.length?r.fromByteArray(e):r.fromByteArray(e.slice(t,n))}function S(e,t,n){n=Math.min(e.length,n);for(var r=[],o=t;o239?4:c>223?3:c>191?2:1;if(o+p<=n)switch(p){case 1:c<128&&(u=c);break;case 2:128==(192&(i=e[o+1]))&&(l=(31&c)<<6|63&i)>127&&(u=l);break;case 3:i=e[o+1],a=e[o+2],128==(192&i)&&128==(192&a)&&(l=(15&c)<<12|(63&i)<<6|63&a)>2047&&(l<55296||l>57343)&&(u=l);break;case 4:i=e[o+1],a=e[o+2],s=e[o+3],128==(192&i)&&128==(192&a)&&128==(192&s)&&(l=(15&c)<<18|(63&i)<<12|(63&a)<<6|63&s)>65535&&l<1114112&&(u=l)}null===u?(u=65533,p=1):u>65535&&(u-=65536,r.push(u>>>10&1023|55296),u=56320|1023&u),r.push(u),o+=p}return function(e){var t=e.length;if(t<=4096)return String.fromCharCode.apply(String,e);var n="",r=0;for(;r0&&(e=this.toString("hex",0,n).match(/.{2}/g).join(" "),this.length>n&&(e+=" ... ")),""},l.prototype.compare=function(e,t,n,r,o){if(!l.isBuffer(e))throw new TypeError("Argument must be a Buffer");if(void 0===t&&(t=0),void 0===n&&(n=e?e.length:0),void 0===r&&(r=0),void 0===o&&(o=this.length),t<0||n>e.length||r<0||o>this.length)throw new RangeError("out of range index");if(r>=o&&t>=n)return 0;if(r>=o)return-1;if(t>=n)return 1;if(this===e)return 0;for(var i=(o>>>=0)-(r>>>=0),a=(n>>>=0)-(t>>>=0),s=Math.min(i,a),c=this.slice(r,o),u=e.slice(t,n),p=0;po)&&(n=o),e.length>0&&(n<0||t<0)||t>this.length)throw new RangeError("Attempt to write outside buffer bounds");r||(r="utf8");for(var i=!1;;)switch(r){case"hex":return b(this,e,t,n);case"utf8":case"utf-8":return x(this,e,t,n);case"ascii":return w(this,e,t,n);case"latin1":case"binary":return k(this,e,t,n);case"base64":return O(this,e,t,n);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return _(this,e,t,n);default:if(i)throw new TypeError("Unknown encoding: "+r);r=(""+r).toLowerCase(),i=!0}},l.prototype.toJSON=function(){return{type:"Buffer",data:Array.prototype.slice.call(this._arr||this,0)}};function T(e,t,n){var r="";n=Math.min(e.length,n);for(var o=t;or)&&(n=r);for(var o="",i=t;in)throw new RangeError("Trying to access beyond buffer length")}function P(e,t,n,r,o,i){if(!l.isBuffer(e))throw new TypeError('"buffer" argument must be a Buffer instance');if(t>o||te.length)throw new RangeError("Index out of range")}function R(e,t,n,r){t<0&&(t=65535+t+1);for(var o=0,i=Math.min(e.length-n,2);o>>8*(r?o:1-o)}function N(e,t,n,r){t<0&&(t=4294967295+t+1);for(var o=0,i=Math.min(e.length-n,4);o>>8*(r?o:3-o)&255}function L(e,t,n,r,o,i){if(n+r>e.length)throw new RangeError("Index out of range");if(n<0)throw new RangeError("Index out of range")}function M(e,t,n,r,i){return i||L(e,0,n,4),o.write(e,t,n,r,23,4),n+4}function D(e,t,n,r,i){return i||L(e,0,n,8),o.write(e,t,n,r,52,8),n+8}l.prototype.slice=function(e,t){var n,r=this.length;if((e=~~e)<0?(e+=r)<0&&(e=0):e>r&&(e=r),(t=void 0===t?r:~~t)<0?(t+=r)<0&&(t=0):t>r&&(t=r),t0&&(o*=256);)r+=this[e+--t]*o;return r},l.prototype.readUInt8=function(e,t){return t||A(e,1,this.length),this[e]},l.prototype.readUInt16LE=function(e,t){return t||A(e,2,this.length),this[e]|this[e+1]<<8},l.prototype.readUInt16BE=function(e,t){return t||A(e,2,this.length),this[e]<<8|this[e+1]},l.prototype.readUInt32LE=function(e,t){return t||A(e,4,this.length),(this[e]|this[e+1]<<8|this[e+2]<<16)+16777216*this[e+3]},l.prototype.readUInt32BE=function(e,t){return t||A(e,4,this.length),16777216*this[e]+(this[e+1]<<16|this[e+2]<<8|this[e+3])},l.prototype.readIntLE=function(e,t,n){e|=0,t|=0,n||A(e,t,this.length);for(var r=this[e],o=1,i=0;++i=(o*=128)&&(r-=Math.pow(2,8*t)),r},l.prototype.readIntBE=function(e,t,n){e|=0,t|=0,n||A(e,t,this.length);for(var r=t,o=1,i=this[e+--r];r>0&&(o*=256);)i+=this[e+--r]*o;return i>=(o*=128)&&(i-=Math.pow(2,8*t)),i},l.prototype.readInt8=function(e,t){return t||A(e,1,this.length),128&this[e]?-1*(255-this[e]+1):this[e]},l.prototype.readInt16LE=function(e,t){t||A(e,2,this.length);var n=this[e]|this[e+1]<<8;return 32768&n?4294901760|n:n},l.prototype.readInt16BE=function(e,t){t||A(e,2,this.length);var n=this[e+1]|this[e]<<8;return 32768&n?4294901760|n:n},l.prototype.readInt32LE=function(e,t){return t||A(e,4,this.length),this[e]|this[e+1]<<8|this[e+2]<<16|this[e+3]<<24},l.prototype.readInt32BE=function(e,t){return t||A(e,4,this.length),this[e]<<24|this[e+1]<<16|this[e+2]<<8|this[e+3]},l.prototype.readFloatLE=function(e,t){return t||A(e,4,this.length),o.read(this,e,!0,23,4)},l.prototype.readFloatBE=function(e,t){return t||A(e,4,this.length),o.read(this,e,!1,23,4)},l.prototype.readDoubleLE=function(e,t){return t||A(e,8,this.length),o.read(this,e,!0,52,8)},l.prototype.readDoubleBE=function(e,t){return t||A(e,8,this.length),o.read(this,e,!1,52,8)},l.prototype.writeUIntLE=function(e,t,n,r){(e=+e,t|=0,n|=0,r)||P(this,e,t,n,Math.pow(2,8*n)-1,0);var o=1,i=0;for(this[t]=255&e;++i=0&&(i*=256);)this[t+o]=e/i&255;return t+n},l.prototype.writeUInt8=function(e,t,n){return e=+e,t|=0,n||P(this,e,t,1,255,0),l.TYPED_ARRAY_SUPPORT||(e=Math.floor(e)),this[t]=255&e,t+1},l.prototype.writeUInt16LE=function(e,t,n){return e=+e,t|=0,n||P(this,e,t,2,65535,0),l.TYPED_ARRAY_SUPPORT?(this[t]=255&e,this[t+1]=e>>>8):R(this,e,t,!0),t+2},l.prototype.writeUInt16BE=function(e,t,n){return e=+e,t|=0,n||P(this,e,t,2,65535,0),l.TYPED_ARRAY_SUPPORT?(this[t]=e>>>8,this[t+1]=255&e):R(this,e,t,!1),t+2},l.prototype.writeUInt32LE=function(e,t,n){return e=+e,t|=0,n||P(this,e,t,4,4294967295,0),l.TYPED_ARRAY_SUPPORT?(this[t+3]=e>>>24,this[t+2]=e>>>16,this[t+1]=e>>>8,this[t]=255&e):N(this,e,t,!0),t+4},l.prototype.writeUInt32BE=function(e,t,n){return e=+e,t|=0,n||P(this,e,t,4,4294967295,0),l.TYPED_ARRAY_SUPPORT?(this[t]=e>>>24,this[t+1]=e>>>16,this[t+2]=e>>>8,this[t+3]=255&e):N(this,e,t,!1),t+4},l.prototype.writeIntLE=function(e,t,n,r){if(e=+e,t|=0,!r){var o=Math.pow(2,8*n-1);P(this,e,t,n,o-1,-o)}var i=0,a=1,s=0;for(this[t]=255&e;++i>0)-s&255;return t+n},l.prototype.writeIntBE=function(e,t,n,r){if(e=+e,t|=0,!r){var o=Math.pow(2,8*n-1);P(this,e,t,n,o-1,-o)}var i=n-1,a=1,s=0;for(this[t+i]=255&e;--i>=0&&(a*=256);)e<0&&0===s&&0!==this[t+i+1]&&(s=1),this[t+i]=(e/a>>0)-s&255;return t+n},l.prototype.writeInt8=function(e,t,n){return e=+e,t|=0,n||P(this,e,t,1,127,-128),l.TYPED_ARRAY_SUPPORT||(e=Math.floor(e)),e<0&&(e=255+e+1),this[t]=255&e,t+1},l.prototype.writeInt16LE=function(e,t,n){return e=+e,t|=0,n||P(this,e,t,2,32767,-32768),l.TYPED_ARRAY_SUPPORT?(this[t]=255&e,this[t+1]=e>>>8):R(this,e,t,!0),t+2},l.prototype.writeInt16BE=function(e,t,n){return e=+e,t|=0,n||P(this,e,t,2,32767,-32768),l.TYPED_ARRAY_SUPPORT?(this[t]=e>>>8,this[t+1]=255&e):R(this,e,t,!1),t+2},l.prototype.writeInt32LE=function(e,t,n){return e=+e,t|=0,n||P(this,e,t,4,2147483647,-2147483648),l.TYPED_ARRAY_SUPPORT?(this[t]=255&e,this[t+1]=e>>>8,this[t+2]=e>>>16,this[t+3]=e>>>24):N(this,e,t,!0),t+4},l.prototype.writeInt32BE=function(e,t,n){return e=+e,t|=0,n||P(this,e,t,4,2147483647,-2147483648),e<0&&(e=4294967295+e+1),l.TYPED_ARRAY_SUPPORT?(this[t]=e>>>24,this[t+1]=e>>>16,this[t+2]=e>>>8,this[t+3]=255&e):N(this,e,t,!1),t+4},l.prototype.writeFloatLE=function(e,t,n){return M(this,e,t,!0,n)},l.prototype.writeFloatBE=function(e,t,n){return M(this,e,t,!1,n)},l.prototype.writeDoubleLE=function(e,t,n){return D(this,e,t,!0,n)},l.prototype.writeDoubleBE=function(e,t,n){return D(this,e,t,!1,n)},l.prototype.copy=function(e,t,n,r){if(n||(n=0),r||0===r||(r=this.length),t>=e.length&&(t=e.length),t||(t=0),r>0&&r=this.length)throw new RangeError("sourceStart out of bounds");if(r<0)throw new RangeError("sourceEnd out of bounds");r>this.length&&(r=this.length),e.length-t=0;--o)e[o+t]=this[o+n];else if(i<1e3||!l.TYPED_ARRAY_SUPPORT)for(o=0;o>>=0,n=void 0===n?this.length:n>>>0,e||(e=0),"number"==typeof e)for(i=t;i55295&&n<57344){if(!o){if(n>56319){(t-=3)>-1&&i.push(239,191,189);continue}if(a+1===r){(t-=3)>-1&&i.push(239,191,189);continue}o=n;continue}if(n<56320){(t-=3)>-1&&i.push(239,191,189),o=n;continue}n=65536+(o-55296<<10|n-56320)}else o&&(t-=3)>-1&&i.push(239,191,189);if(o=null,n<128){if((t-=1)<0)break;i.push(n)}else if(n<2048){if((t-=2)<0)break;i.push(n>>6|192,63&n|128)}else if(n<65536){if((t-=3)<0)break;i.push(n>>12|224,n>>6&63|128,63&n|128)}else{if(!(n<1114112))throw new Error("Invalid code point");if((t-=4)<0)break;i.push(n>>18|240,n>>12&63|128,n>>6&63|128,63&n|128)}}return i}function B(e){return r.toByteArray(function(e){if((e=function(e){return e.trim?e.trim():e.replace(/^\s+|\s+$/g,"")}(e).replace(F,"")).length<2)return"";for(;e.length%4!=0;)e+="=";return e}(e))}function $(e,t,n,r){for(var o=0;o=t.length||o>=e.length);++o)t[o+n]=e[o];return o}}).call(this,n(7))},function(e,t,n){"use strict";n.d(t,"a",(function(){return g})),n.d(t,"b",(function(){return a})),n.d(t,"c",(function(){return h}));var r=n(2),o=n(0);if(!o.useState)throw new Error("mobx-react-lite requires React with Hooks support");if(!r.o)throw new Error("mobx-react-lite requires mobx at least version 4 to be available");var i=!1;function a(){return i} + /*! ***************************************************************************** + Copyright (c) Microsoft Corporation. All rights reserved. + Licensed under the Apache License, Version 2.0 (the "License"); you may not use + this file except in compliance with the License. You may obtain a copy of the + License at http://www.apache.org/licenses/LICENSE-2.0 + + THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED + WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, + MERCHANTABLITY OR NON-INFRINGEMENT. + + See the Apache Version 2.0 License for specific language governing permissions + and limitations under the License. + ***************************************************************************** */var s=function(){return(s=Object.assign||function(e){for(var t,n=1,r=arguments.length;n0)&&!(r=i.next()).done;)a.push(r.value)}catch(e){o={error:e}}finally{try{r&&!r.done&&(n=i.return)&&n.call(i)}finally{if(o)throw o.error}}return a}function c(e){return e.current?Object(r.h)(e.current):""}var u=[];function p(){var e=l(Object(o.useState)(0),2)[1];return Object(o.useCallback)((function(){e((function(e){return e+1}))}),[])}var f={};function d(e,t,n){if(void 0===t&&(t="observed"),void 0===n&&(n=f),a())return e();var i=(n.useForceUpdate||p)(),s=Object(o.useRef)(null);s.current||(s.current=new r.b("observer("+t+")",(function(){i()})));var l,d,h=function(){s.current&&!s.current.isDisposed&&(s.current.dispose(),s.current=null)};if(Object(o.useDebugValue)(s,c),function(e){Object(o.useEffect)((function(){return e}),u)}((function(){h()})),s.current.track((function(){try{l=e()}catch(e){d=e}})),d)throw h(),d;return l}function h(e,t){if(a())return e;var n,r,i,l=s({forwardRef:!1},t),c=e.displayName||e.name,u=function(t,n){return d((function(){return e(t,n)}),c)};return u.displayName=c,n=l.forwardRef?Object(o.memo)(Object(o.forwardRef)(u)):Object(o.memo)(u),r=e,i=n,Object.keys(r).forEach((function(e){r.hasOwnProperty(e)&&!m[e]&&Object.defineProperty(i,e,Object.getOwnPropertyDescriptor(r,e))})),n.displayName=c,n}var m={$$typeof:!0,render:!0,compare:!0,type:!0};function g(e){var t=e.children,n=e.render,r=t||n;return"function"!=typeof r?null:d(r)}function y(e,t,n,r,o){var i="children"===t?"render":"children",a="function"==typeof e[t],s="function"==typeof e[i];return a&&s?new Error("MobX Observer: Do not use children and render in the same time in`"+n):a||s?null:new Error("Invalid prop `"+o+"` of type `"+typeof e[t]+"` supplied to `"+n+"`, expected `function`.")}g.propTypes={children:y,render:y},g.displayName="Observer"},function(e,t,n){var r=n(18),o=n(102),i=n(21),a=n(54),s=Object.defineProperty;t.f=r?s:function(e,t,n){if(i(e),t=a(t,!0),i(n),o)try{return s(e,t,n)}catch(e){}if("get"in n||"set"in n)throw TypeError("Accessors not supported");return"value"in n&&(e[t]=n.value),e}},function(e,t,n){var r=n(4),o=n(35).f,i=n(24),a=n(25),s=n(71),l=n(106),c=n(82);e.exports=function(e,t){var n,u,p,f,d,h=e.target,m=e.global,g=e.stat;if(n=m?r:g?r[h]||s(h,{}):(r[h]||{}).prototype)for(u in t){if(f=t[u],p=e.noTargetGet?(d=o(n,u))&&d.value:n[u],!c(m?u:h+(g?".":"#")+u,e.forced)&&void 0!==p){if(typeof f==typeof p)continue;l(f,p)}(e.sham||p&&p.sham)&&i(f,"sham",!0),a(n,u,f,e)}}},function(e,t,n){var r=n(8);e.exports=!r((function(){return 7!=Object.defineProperty({},1,{get:function(){return 7}})[1]}))},function(e,t,n){e.exports=n(230)()},function(e,t,n){var r; + /*! + Copyright (c) 2017 Jed Watson. + Licensed under the MIT License (MIT), see + http://jedwatson.github.io/classnames + */!function(){"use strict";var n={}.hasOwnProperty;function o(){for(var e=[],t=0;t=0?e.substr(t).toLowerCase():""},t.getHash=function(e){var t=e.indexOf("#");return t>=0?e.substr(t):"#"},t.stripHash=function(e){var t=e.indexOf("#");return t>=0&&(e=e.substr(0,t)),e},t.isHttp=function(e){var t=s.getProtocol(e);return"http"===t||"https"===t||void 0===t&&r.browser},t.isFileSystemPath=function(e){if(r.browser)return!1;var t=s.getProtocol(e);return void 0===t||"file"===t},t.fromFileSystemPath=function(e){o&&(e=e.replace(/\\/g,"/")),e=encodeURI(e);for(var t=0;t2?r:e).apply(void 0,o)}}e.memoize=a,e.debounce=s,e.bind=l,e.default={memoize:a,debounce:s,bind:l}})?r.apply(t,o):r)||(e.exports=i)},function(e,t){var n={}.toString;e.exports=function(e){return n.call(e).slice(8,-1)}},function(e,t,n){var r=n(37),o=n(4),i=function(e){return"function"==typeof e?e:void 0};e.exports=function(e,t){return arguments.length<2?i(r[e])||i(o[e]):r[e]&&r[e][t]||o[e]&&o[e][t]}},function(e,t,n){var r=n(16).f,o=n(11),i=n(5)("toStringTag");e.exports=function(e,t,n){e&&!o(e=n?e:e.prototype,i)&&r(e,i,{configurable:!0,value:t})}},function(e,t,n){"use strict";var r=n(265),o=Array.prototype.slice,i=["name","message","stack"],a=["name","message","description","number","code","fileName","lineNumber","columnNumber","sourceURL","line","column","stack"];function s(t){return function(n,r,i,a){var s=[],p="";"string"==typeof n?(s=o.call(arguments),n=r=void 0):"string"==typeof r?(s=o.call(arguments,1),r=void 0):"string"==typeof i&&(s=o.call(arguments,2)),s.length>0&&(p=e.exports.formatter.apply(null,s)),n&&n.message&&(p+=(p?" \n":"")+n.message);var f=new t(p);return l(f,n),c(f),u(f,r),f}}function l(e,t){!function(e,t){!function(e){if(!m)return!1;var t=Object.getOwnPropertyDescriptor(e,"stack");if(!t)return!1;return"function"==typeof t.get}(e)?e.stack=t?d(e.stack,t.stack):h(e.stack):t?function(e,t){var n=Object.getOwnPropertyDescriptor(e,"stack");Object.defineProperty(e,"stack",{get:function(){return d(n.get.apply(e),t.stack)},enumerable:!1,configurable:!0})}(e,t):(n=e,r=Object.getOwnPropertyDescriptor(n,"stack"),Object.defineProperty(n,"stack",{get:function(){return h(r.get.apply(n))},enumerable:!1,configurable:!0}));var n,r}(e,t),u(e,t)}function c(e){e.toJSON=p,e.inspect=f}function u(e,t){if(t&&"object"==typeof t)for(var n=Object.keys(t),r=0;r=0))try{e[o]=t[o]}catch(e){}}}function p(){var e={},t=Object.keys(this);t=t.concat(a);for(var n=0;n=0)return t.splice(n,1),t.join("\n")}return e}}e.exports=s(Error),e.exports.error=s(Error),e.exports.eval=s(EvalError),e.exports.range=s(RangeError),e.exports.reference=s(ReferenceError),e.exports.syntax=s(SyntaxError),e.exports.type=s(TypeError),e.exports.uri=s(URIError),e.exports.formatter=r;var m=!(!Object.getOwnPropertyDescriptor||!Object.defineProperty||"undefined"!=typeof navigator&&/Android/.test(navigator.userAgent))},function(e,t){"function"==typeof Object.create?e.exports=function(e,t){t&&(e.super_=t,e.prototype=Object.create(t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}))}:e.exports=function(e,t){if(t){e.super_=t;var n=function(){};n.prototype=t.prototype,e.prototype=new n,e.prototype.constructor=e}}},function(e,t,n){"use strict";!function e(){if("undefined"!=typeof __REACT_DEVTOOLS_GLOBAL_HOOK__&&"function"==typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE){0;try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(e)}catch(e){console.error(e)}}}(),e.exports=n(227)},function(e,t,n){var r,o,i,a=n(165),s=n(4),l=n(9),c=n(24),u=n(11),p=n(56),f=n(43),d=s.WeakMap;if(a){var h=new d,m=h.get,g=h.has,y=h.set;r=function(e,t){return y.call(h,e,t),t},o=function(e){return m.call(h,e)||{}},i=function(e){return g.call(h,e)}}else{var v=p("state");f[v]=!0,r=function(e,t){return c(e,v,t),t},o=function(e){return u(e,v)?e[v]:{}},i=function(e){return u(e,v)}}e.exports={set:r,get:o,has:i,enforce:function(e){return i(e)?o(e):r(e,{})},getterFor:function(e){return function(t){var n;if(!l(t)||(n=o(t)).type!==e)throw TypeError("Incompatible receiver, "+e+" required");return n}}}},function(e,t,n){var r=n(18),o=n(77),i=n(42),a=n(36),s=n(54),l=n(11),c=n(102),u=Object.getOwnPropertyDescriptor;t.f=r?u:function(e,t){if(e=a(e),t=s(t,!0),c)try{return u(e,t)}catch(e){}if(l(e,t))return i(!o.f.call(e,t),e[t])}},function(e,t,n){var r=n(78),o=n(44);e.exports=function(e){return r(o(e))}},function(e,t,n){var r=n(4);e.exports=r},function(e,t,n){var r=n(75),o=Math.min;e.exports=function(e){return e>0?o(r(e),9007199254740991):0}},function(e,t,n){"use strict";var r=n(49),o=n(59),i=n(6);function a(e,t,n){var r=[];return e.include.forEach((function(e){n=a(e,t,n)})),e[t].forEach((function(e){n.forEach((function(t,n){t.tag===e.tag&&t.kind===e.kind&&r.push(n)})),n.push(e)})),n.filter((function(e,t){return-1===r.indexOf(t)}))}function s(e){this.include=e.include||[],this.implicit=e.implicit||[],this.explicit=e.explicit||[],this.implicit.forEach((function(e){if(e.loadKind&&"scalar"!==e.loadKind)throw new o("There is a non-scalar type in the implicit list of a schema. Implicit resolving of such types is not supported.")})),this.compiledImplicit=a(this,"implicit",[]),this.compiledExplicit=a(this,"explicit",[]),this.compiledTypeMap=function(){var e,t,n={scalar:{},sequence:{},mapping:{},fallback:{}};function r(e){n[e.kind][e.tag]=n.fallback[e.tag]=e}for(e=0,t=arguments.length;ee.length)return;if(!(w instanceof o)){if(m&&b!=t.length-1){if(f.lastIndex=x,!(T=f.exec(e)))break;for(var k=T.index+(h&&T[1]?T[1].length:0),O=T.index+T[0].length,_=b,E=x,S=t.length;_=(E+=t[_].length)&&(++b,x=E);if(t[b]instanceof o)continue;j=_-b,w=e.slice(x,E),T.index-=x}else{f.lastIndex=0;var T=f.exec(w),j=1}if(T){h&&(g=T[1]?T[1].length:0);O=(k=T.index+g)+(T=T[0].slice(g)).length;var C=w.slice(0,k),I=w.slice(O),A=[b,j];C&&(++b,x+=C.length,A.push(C));var P=new o(c,d?r.tokenize(T,d):T,y,T,m);if(A.push(P),I&&A.push(I),Array.prototype.splice.apply(t,A),1!=j&&r.matchGrammar(e,t,n,b,x,!0,c+","+p),s)break}else if(s)break}}}}},tokenize:function(e,t){var n=[e],o=t.rest;if(o){for(var i in o)t[i]=o[i];delete t.rest}return r.matchGrammar(e,n,t,0,0,!1),n},hooks:{all:{},add:function(e,t){var n=r.hooks.all;n[e]=n[e]||[],n[e].push(t)},run:function(e,t){var n=r.hooks.all[e];if(n&&n.length)for(var o,i=0;o=n[i++];)o(t)}},Token:o};function o(e,t,n,r,o){this.type=e,this.content=t,this.alias=n,this.length=0|(r||"").length,this.greedy=!!o}if(e.Prism=r,o.stringify=function(e,t){if("string"==typeof e)return e;if(Array.isArray(e))return e.map((function(e){return o.stringify(e,t)})).join("");var n={type:e.type,content:o.stringify(e.content,t),tag:"span",classes:["token",e.type],attributes:{},language:t};if(e.alias){var i=Array.isArray(e.alias)?e.alias:[e.alias];Array.prototype.push.apply(n.classes,i)}r.hooks.run("wrap",n);var a=Object.keys(n.attributes).map((function(e){return e+'="'+(n.attributes[e]||"").replace(/"/g,""")+'"'})).join(" ");return"<"+n.tag+' class="'+n.classes.join(" ")+'"'+(a?" "+a:"")+">"+n.content+""},!e.document)return e.addEventListener?(r.disableWorkerMessageHandler||e.addEventListener("message",(function(t){var n=JSON.parse(t.data),o=n.language,i=n.code,a=n.immediateClose;e.postMessage(r.highlight(i,r.languages[o],o)),a&&e.close()}),!1),r):r;var i=r.util.currentScript();if(i&&(r.filename=i.src,i.hasAttribute("data-manual")&&(r.manual=!0)),!r.manual){function a(){r.manual||r.highlightAll()}var s=document.readyState;"loading"===s||"interactive"===s&&i&&i.defer?document.addEventListener("DOMContentLoaded",a):window.requestAnimationFrame?window.requestAnimationFrame(a):window.setTimeout(a,16)}return r}("undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{});e.exports&&(e.exports=n),void 0!==t&&(t.Prism=n),n.languages.markup={comment://,prolog:/<\?[\s\S]+?\?>/,doctype:{pattern:/"'[\]]|"[^"]*"|'[^']*')+(?:\[(?:(?!)*\]\s*)?>/i,greedy:!0},cdata://i,tag:{pattern:/<\/?(?!\d)[^\s>\/=$<%]+(?:\s(?:\s*[^\s>\/=]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))|(?=[\s/>])))+)?\s*\/?>/i,greedy:!0,inside:{tag:{pattern:/^<\/?[^\s>\/]+/i,inside:{punctuation:/^<\/?/,namespace:/^[^\s>\/:]+:/}},"attr-value":{pattern:/=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+)/i,inside:{punctuation:[/^=/,{pattern:/^(\s*)["']|["']$/,lookbehind:!0}]}},punctuation:/\/?>/,"attr-name":{pattern:/[^\s>\/]+/,inside:{namespace:/^[^\s>\/:]+:/}}}},entity:/&#?[\da-z]{1,8};/i},n.languages.markup.tag.inside["attr-value"].inside.entity=n.languages.markup.entity,n.hooks.add("wrap",(function(e){"entity"===e.type&&(e.attributes.title=e.content.replace(/&/,"&"))})),Object.defineProperty(n.languages.markup.tag,"addInlined",{value:function(e,t){var r={};r["language-"+t]={pattern:/(^$)/i,lookbehind:!0,inside:n.languages[t]},r.cdata=/^$/i;var o={"included-cdata":{pattern://i,inside:r}};o["language-"+t]={pattern:/[\s\S]+/,inside:n.languages[t]};var i={};i[e]={pattern:RegExp(/(<__[\s\S]*?>)(?:\s*|[\s\S])*?(?=<\/__>)/.source.replace(/__/g,e),"i"),lookbehind:!0,greedy:!0,inside:o},n.languages.insertBefore("markup","cdata",i)}}),n.languages.xml=n.languages.extend("markup",{}),n.languages.html=n.languages.markup,n.languages.mathml=n.languages.markup,n.languages.svg=n.languages.markup,function(e){var t=/("|')(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/;e.languages.css={comment:/\/\*[\s\S]*?\*\//,atrule:{pattern:/@[\w-]+[\s\S]*?(?:;|(?=\s*\{))/,inside:{rule:/@[\w-]+/}},url:{pattern:RegExp("url\\((?:"+t.source+"|[^\n\r()]*)\\)","i"),inside:{function:/^url/i,punctuation:/^\(|\)$/}},selector:RegExp("[^{}\\s](?:[^{};\"']|"+t.source+")*?(?=\\s*\\{)"),string:{pattern:t,greedy:!0},property:/[-_a-z\xA0-\uFFFF][-\w\xA0-\uFFFF]*(?=\s*:)/i,important:/!important\b/i,function:/[-a-z0-9]+(?=\()/i,punctuation:/[(){};:,]/},e.languages.css.atrule.inside.rest=e.languages.css;var n=e.languages.markup;n&&(n.tag.addInlined("style","css"),e.languages.insertBefore("inside","attr-value",{"style-attr":{pattern:/\s*style=("|')(?:\\[\s\S]|(?!\1)[^\\])*\1/i,inside:{"attr-name":{pattern:/^\s*style/i,inside:n.tag.inside},punctuation:/^\s*=\s*['"]|['"]\s*$/,"attr-value":{pattern:/.+/i,inside:e.languages.css}},alias:"language-css"}},n.tag))}(n),n.languages.clike={comment:[{pattern:/(^|[^\\])\/\*[\s\S]*?(?:\*\/|$)/,lookbehind:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0,greedy:!0}],string:{pattern:/(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},"class-name":{pattern:/(\b(?:class|interface|extends|implements|trait|instanceof|new)\s+|\bcatch\s+\()[\w.\\]+/i,lookbehind:!0,inside:{punctuation:/[.\\]/}},keyword:/\b(?:if|else|while|do|for|return|in|instanceof|function|new|try|throw|catch|finally|null|break|continue)\b/,boolean:/\b(?:true|false)\b/,function:/\w+(?=\()/,number:/\b0x[\da-f]+\b|(?:\b\d+\.?\d*|\B\.\d+)(?:e[+-]?\d+)?/i,operator:/[<>]=?|[!=]=?=?|--?|\+\+?|&&?|\|\|?|[?*/~^%]/,punctuation:/[{}[\];(),.:]/},n.languages.javascript=n.languages.extend("clike",{"class-name":[n.languages.clike["class-name"],{pattern:/(^|[^$\w\xA0-\uFFFF])[_$A-Z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\.(?:prototype|constructor))/,lookbehind:!0}],keyword:[{pattern:/((?:^|})\s*)(?:catch|finally)\b/,lookbehind:!0},{pattern:/(^|[^.]|\.\.\.\s*)\b(?:as|async(?=\s*(?:function\b|\(|[$\w\xA0-\uFFFF]|$))|await|break|case|class|const|continue|debugger|default|delete|do|else|enum|export|extends|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)\b/,lookbehind:!0}],number:/\b(?:(?:0[xX](?:[\dA-Fa-f](?:_[\dA-Fa-f])?)+|0[bB](?:[01](?:_[01])?)+|0[oO](?:[0-7](?:_[0-7])?)+)n?|(?:\d(?:_\d)?)+n|NaN|Infinity)\b|(?:\b(?:\d(?:_\d)?)+\.?(?:\d(?:_\d)?)*|\B\.(?:\d(?:_\d)?)+)(?:[Ee][+-]?(?:\d(?:_\d)?)+)?/,function:/#?[_$a-zA-Z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\s*(?:\.\s*(?:apply|bind|call)\s*)?\()/,operator:/--|\+\+|\*\*=?|=>|&&|\|\||[!=]==|<<=?|>>>?=?|[-+*/%&|^!=<>]=?|\.{3}|\?[.?]?|[~:]/}),n.languages.javascript["class-name"][0].pattern=/(\b(?:class|interface|extends|implements|instanceof|new)\s+)[\w.\\]+/,n.languages.insertBefore("javascript","keyword",{regex:{pattern:/((?:^|[^$\w\xA0-\uFFFF."'\])\s])\s*)\/(?:\[(?:[^\]\\\r\n]|\\.)*]|\\.|[^/\\\[\r\n])+\/[gimyus]{0,6}(?=(?:\s|\/\*[\s\S]*?\*\/)*(?:$|[\r\n,.;:})\]]|\/\/))/,lookbehind:!0,greedy:!0},"function-variable":{pattern:/#?[_$a-zA-Z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\s*[=:]\s*(?:async\s*)?(?:\bfunction\b|(?:\((?:[^()]|\([^()]*\))*\)|[_$a-zA-Z\xA0-\uFFFF][$\w\xA0-\uFFFF]*)\s*=>))/,alias:"function"},parameter:[{pattern:/(function(?:\s+[_$A-Za-z\xA0-\uFFFF][$\w\xA0-\uFFFF]*)?\s*\(\s*)(?!\s)(?:[^()]|\([^()]*\))+?(?=\s*\))/,lookbehind:!0,inside:n.languages.javascript},{pattern:/[_$a-z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\s*=>)/i,inside:n.languages.javascript},{pattern:/(\(\s*)(?!\s)(?:[^()]|\([^()]*\))+?(?=\s*\)\s*=>)/,lookbehind:!0,inside:n.languages.javascript},{pattern:/((?:\b|\s|^)(?!(?:as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)(?![$\w\xA0-\uFFFF]))(?:[_$A-Za-z\xA0-\uFFFF][$\w\xA0-\uFFFF]*\s*)\(\s*)(?!\s)(?:[^()]|\([^()]*\))+?(?=\s*\)\s*\{)/,lookbehind:!0,inside:n.languages.javascript}],constant:/\b[A-Z](?:[A-Z_]|\dx?)*\b/}),n.languages.insertBefore("javascript","string",{"template-string":{pattern:/`(?:\\[\s\S]|\${(?:[^{}]|{(?:[^{}]|{[^}]*})*})+}|(?!\${)[^\\`])*`/,greedy:!0,inside:{"template-punctuation":{pattern:/^`|`$/,alias:"string"},interpolation:{pattern:/((?:^|[^\\])(?:\\{2})*)\${(?:[^{}]|{(?:[^{}]|{[^}]*})*})+}/,lookbehind:!0,inside:{"interpolation-punctuation":{pattern:/^\${|}$/,alias:"punctuation"},rest:n.languages.javascript}},string:/[\s\S]+/}}}),n.languages.markup&&n.languages.markup.tag.addInlined("script","javascript"),n.languages.js=n.languages.javascript,"undefined"!=typeof self&&self.Prism&&self.document&&document.querySelector&&(self.Prism.fileHighlight=function(e){e=e||document;var t={js:"javascript",py:"python",rb:"ruby",ps1:"powershell",psm1:"powershell",sh:"bash",bat:"batch",h:"c",tex:"latex"};Array.prototype.slice.call(e.querySelectorAll("pre[data-src]")).forEach((function(e){if(!e.hasAttribute("data-src-loaded")){for(var r,o=e.getAttribute("data-src"),i=e,a=/\blang(?:uage)?-([\w-]+)\b/i;i&&!a.test(i.className);)i=i.parentNode;if(i&&(r=(e.className.match(a)||[,""])[1]),!r){var s=(o.match(/\.(\w+)$/)||[,""])[1];r=t[s]||s}var l=document.createElement("code");l.className="language-"+r,e.textContent="",l.textContent="Loading…",e.appendChild(l);var c=new XMLHttpRequest;c.open("GET",o,!0),c.onreadystatechange=function(){4==c.readyState&&(c.status<400&&c.responseText?(l.textContent=c.responseText,n.highlightElement(l),e.setAttribute("data-src-loaded","")):c.status>=400?l.textContent="✖ Error "+c.status+" while fetching file: "+c.statusText:l.textContent="✖ Error: File does not exist or is empty")},c.send(null)}}))},document.addEventListener("DOMContentLoaded",(function(){self.Prism.fileHighlight()})))}).call(this,n(7))},function(e,t){e.exports=function(e,t){return{enumerable:!(1&e),configurable:!(2&e),writable:!(4&e),value:t}}},function(e,t){e.exports={}},function(e,t){e.exports=function(e){if(null==e)throw TypeError("Can't call method on "+e);return e}},function(e,t,n){var r=n(44);e.exports=function(e){return Object(r(e))}},function(e,t){e.exports={}},function(e,t){e.exports=function(e){if("function"!=typeof e)throw TypeError(String(e)+" is not a function");return e}},function(e,t,n){var r=n(47);e.exports=function(e,t,n){if(r(e),void 0===t)return e;switch(n){case 0:return function(){return e.call(t)};case 1:return function(n){return e.call(t,n)};case 2:return function(n,r){return e.call(t,n,r)};case 3:return function(n,r,o){return e.call(t,n,r,o)}}return function(){return e.apply(t,arguments)}}},function(e,t,n){"use strict";function r(e){return null==e}e.exports.isNothing=r,e.exports.isObject=function(e){return"object"==typeof e&&null!==e},e.exports.toArray=function(e){return Array.isArray(e)?e:r(e)?[]:[e]},e.exports.repeat=function(e,t){var n,r="";for(n=0;n=0;r--){var o=e[r];"."===o?e.splice(r,1):".."===o?(e.splice(r,1),n++):n&&(e.splice(r,1),n--)}if(t)for(;n--;n)e.unshift("..");return e}function r(e,t){if(e.filter)return e.filter(t);for(var n=[],r=0;r=-1&&!o;i--){var a=i>=0?arguments[i]:e.cwd();if("string"!=typeof a)throw new TypeError("Arguments to path.resolve must be strings");a&&(t=a+"/"+t,o="/"===a.charAt(0))}return(o?"/":"")+(t=n(r(t.split("/"),(function(e){return!!e})),!o).join("/"))||"."},t.normalize=function(e){var i=t.isAbsolute(e),a="/"===o(e,-1);return(e=n(r(e.split("/"),(function(e){return!!e})),!i).join("/"))||i||(e="."),e&&a&&(e+="/"),(i?"/":"")+e},t.isAbsolute=function(e){return"/"===e.charAt(0)},t.join=function(){var e=Array.prototype.slice.call(arguments,0);return t.normalize(r(e,(function(e,t){if("string"!=typeof e)throw new TypeError("Arguments to path.join must be strings");return e})).join("/"))},t.relative=function(e,n){function r(e){for(var t=0;t=0&&""===e[n];n--);return t>n?[]:e.slice(t,n-t+1)}e=t.resolve(e).substr(1),n=t.resolve(n).substr(1);for(var o=r(e.split("/")),i=r(n.split("/")),a=Math.min(o.length,i.length),s=a,l=0;l=1;--i)if(47===(t=e.charCodeAt(i))){if(!o){r=i;break}}else o=!1;return-1===r?n?"/":".":n&&1===r?"/":e.slice(0,r)},t.basename=function(e,t){var n=function(e){"string"!=typeof e&&(e+="");var t,n=0,r=-1,o=!0;for(t=e.length-1;t>=0;--t)if(47===e.charCodeAt(t)){if(!o){n=t+1;break}}else-1===r&&(o=!1,r=t+1);return-1===r?"":e.slice(n,r)}(e);return t&&n.substr(-1*t.length)===t&&(n=n.substr(0,n.length-t.length)),n},t.extname=function(e){"string"!=typeof e&&(e+="");for(var t=-1,n=0,r=-1,o=!0,i=0,a=e.length-1;a>=0;--a){var s=e.charCodeAt(a);if(47!==s)-1===r&&(o=!1,r=a+1),46===s?-1===t?t=a:1!==i&&(i=1):-1!==t&&(i=-1);else if(!o){n=a+1;break}}return-1===t||-1===r||0===i||1===i&&t===r-1&&t===n+1?"":e.slice(t,r)};var o="b"==="ab".substr(-1)?function(e,t,n){return e.substr(t,n)}:function(e,t,n){return t<0&&(t=e.length+t),e.substr(t,n)}}).call(this,n(13))},function(e,t,n){(function(t){!function(t){"use strict";var n={newline:/^\n+/,code:/^( {4}[^\n]+\n*)+/,fences:/^ {0,3}(`{3,}|~{3,})([^`~\n]*)\n(?:|([\s\S]*?)\n)(?: {0,3}\1[~`]* *(?:\n+|$)|$)/,hr:/^ {0,3}((?:- *){3,}|(?:_ *){3,}|(?:\* *){3,})(?:\n+|$)/,heading:/^ {0,3}(#{1,6}) +([^\n]*?)(?: +#+)? *(?:\n+|$)/,blockquote:/^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/,list:/^( {0,3})(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,html:"^ {0,3}(?:<(script|pre|style)[\\s>][\\s\\S]*?(?:[^\\n]*\\n+|$)|comment[^\\n]*(\\n+|$)|<\\?[\\s\\S]*?\\?>\\n*|\\n*|\\n*|)[\\s\\S]*?(?:\\n{2,}|$)|<(?!script|pre|style)([a-z][\\w-]*)(?:attribute)*? */?>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:\\n{2,}|$)|(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:\\n{2,}|$))",def:/^ {0,3}\[(label)\]: *\n? *]+)>?(?:(?: +\n? *| *\n *)(title))? *(?:\n+|$)/,nptable:g,table:g,lheading:/^([^\n]+)\n {0,3}(=+|-+) *(?:\n+|$)/,_paragraph:/^([^\n]+(?:\n(?!hr|heading|lheading|blockquote|fences|list|html)[^\n]+)*)/,text:/^[^\n]+/};function r(e){this.tokens=[],this.tokens.links=Object.create(null),this.options=e||k.defaults,this.rules=n.normal,this.options.pedantic?this.rules=n.pedantic:this.options.gfm&&(this.rules=n.gfm)}n._label=/(?!\s*\])(?:\\[\[\]]|[^\[\]])+/,n._title=/(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/,n.def=f(n.def).replace("label",n._label).replace("title",n._title).getRegex(),n.bullet=/(?:[*+-]|\d{1,9}\.)/,n.item=/^( *)(bull) ?[^\n]*(?:\n(?!\1bull ?)[^\n]*)*/,n.item=f(n.item,"gm").replace(/bull/g,n.bullet).getRegex(),n.list=f(n.list).replace(/bull/g,n.bullet).replace("hr","\\n+(?=\\1?(?:(?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$))").replace("def","\\n+(?="+n.def.source+")").getRegex(),n._tag="address|article|aside|base|basefont|blockquote|body|caption|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option|p|param|section|source|summary|table|tbody|td|tfoot|th|thead|title|tr|track|ul",n._comment=//,n.html=f(n.html,"i").replace("comment",n._comment).replace("tag",n._tag).replace("attribute",/ +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/).getRegex(),n.paragraph=f(n._paragraph).replace("hr",n.hr).replace("heading"," {0,3}#{1,6} +").replace("|lheading","").replace("blockquote"," {0,3}>").replace("fences"," {0,3}(?:`{3,}|~{3,})[^`\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html",")|<(?:script|pre|style|!--)").replace("tag",n._tag).getRegex(),n.blockquote=f(n.blockquote).replace("paragraph",n.paragraph).getRegex(),n.normal=y({},n),n.gfm=y({},n.normal,{nptable:/^ *([^|\n ].*\|.*)\n *([-:]+ *\|[-| :]*)(?:\n((?:.*[^>\n ].*(?:\n|$))*)\n*|$)/,table:/^ *\|(.+)\n *\|?( *[-:]+[-| :]*)(?:\n((?: *[^>\n ].*(?:\n|$))*)\n*|$)/}),n.pedantic=y({},n.normal,{html:f("^ *(?:comment *(?:\\n|\\s*$)|<(tag)[\\s\\S]+? *(?:\\n{2,}|\\s*$)|\\s]*)*?/?> *(?:\\n{2,}|\\s*$))").replace("comment",n._comment).replace(/tag/g,"(?!(?:a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)\\b)\\w+(?!:|[^\\w\\s@]*@)\\b").getRegex(),def:/^ *\[([^\]]+)\]: *]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/,heading:/^ *(#{1,6}) *([^\n]+?) *(?:#+ *)?(?:\n+|$)/,fences:g,paragraph:f(n.normal._paragraph).replace("hr",n.hr).replace("heading"," *#{1,6} *[^\n]").replace("lheading",n.lheading).replace("blockquote"," {0,3}>").replace("|fences","").replace("|list","").replace("|html","").getRegex()}),r.rules=n,r.lex=function(e,t){return new r(t).lex(e)},r.prototype.lex=function(e){return e=e.replace(/\r\n|\r/g,"\n").replace(/\t/g," ").replace(/\u00a0/g," ").replace(/\u2424/g,"\n"),this.token(e,!0)},r.prototype.token=function(e,t){var r,o,i,a,s,l,c,p,f,d,h,m,g,y,x,w;for(e=e.replace(/^ +$/gm,"");e;)if((i=this.rules.newline.exec(e))&&(e=e.substring(i[0].length),i[0].length>1&&this.tokens.push({type:"space"})),i=this.rules.code.exec(e)){var k=this.tokens[this.tokens.length-1];e=e.substring(i[0].length),k&&"paragraph"===k.type?k.text+="\n"+i[0].trimRight():(i=i[0].replace(/^ {4}/gm,""),this.tokens.push({type:"code",codeBlockStyle:"indented",text:this.options.pedantic?i:b(i,"\n")}))}else if(i=this.rules.fences.exec(e))e=e.substring(i[0].length),this.tokens.push({type:"code",lang:i[2]?i[2].trim():i[2],text:i[3]||""});else if(i=this.rules.heading.exec(e))e=e.substring(i[0].length),this.tokens.push({type:"heading",depth:i[1].length,text:i[2]});else if((i=this.rules.nptable.exec(e))&&(l={type:"table",header:v(i[1].replace(/^ *| *\| *$/g,"")),align:i[2].replace(/^ *|\| *$/g,"").split(/ *\| */),cells:i[3]?i[3].replace(/\n$/,"").split("\n"):[]}).header.length===l.align.length){for(e=e.substring(i[0].length),h=0;h ?/gm,""),this.token(i,t),this.tokens.push({type:"blockquote_end"});else if(i=this.rules.list.exec(e)){for(e=e.substring(i[0].length),c={type:"list_start",ordered:y=(a=i[2]).length>1,start:y?+a:"",loose:!1},this.tokens.push(c),p=[],r=!1,g=(i=i[0].match(this.rules.item)).length,h=0;h1?1===s.length:s.length>1||this.options.smartLists&&s!==a)&&(e=i.slice(h+1).join("\n")+e,h=g-1)),o=r||/\n\n(?!\s*$)/.test(l),h!==g-1&&(r="\n"===l.charAt(l.length-1),o||(o=r)),o&&(c.loose=!0),w=void 0,(x=/^\[[ xX]\] /.test(l))&&(w=" "!==l[1],l=l.replace(/^\[[ xX]\] +/,"")),f={type:"list_item_start",task:x,checked:w,loose:o},p.push(f),this.tokens.push(f),this.token(l,!1),this.tokens.push({type:"list_item_end"});if(c.loose)for(g=p.length,h=0;h?@\[\]\\^_`{|}~])/,autolink:/^<(scheme:[^\s\x00-\x1f<>]*|email)>/,url:g,tag:"^comment|^|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>|^<\\?[\\s\\S]*?\\?>|^|^",link:/^!?\[(label)\]\(\s*(href)(?:\s+(title))?\s*\)/,reflink:/^!?\[(label)\]\[(?!\s*\])((?:\\[\[\]]?|[^\[\]\\])+)\]/,nolink:/^!?\[(?!\s*\])((?:\[[^\[\]]*\]|\\[\[\]]|[^\[\]])*)\](?:\[\])?/,strong:/^__([^\s_])__(?!_)|^\*\*([^\s*])\*\*(?!\*)|^__([^\s][\s\S]*?[^\s])__(?!_)|^\*\*([^\s][\s\S]*?[^\s])\*\*(?!\*)/,em:/^_([^\s_])_(?!_)|^\*([^\s*<\[])\*(?!\*)|^_([^\s<][\s\S]*?[^\s_])_(?!_|[^\spunctuation])|^_([^\s_<][\s\S]*?[^\s])_(?!_|[^\spunctuation])|^\*([^\s<"][\s\S]*?[^\s\*])\*(?!\*|[^\spunctuation])|^\*([^\s*"<\[][\s\S]*?[^\s])\*(?!\*)/,code:/^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/,br:/^( {2,}|\\)\n(?!\s*$)/,del:g,text:/^(`+|[^`])(?:[\s\S]*?(?:(?=[\\?@\\[^_{|}~",o.em=f(o.em).replace(/punctuation/g,o._punctuation).getRegex(),o._escapes=/\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/g,o._scheme=/[a-zA-Z][a-zA-Z0-9+.-]{1,31}/,o._email=/[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+(@)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?![-_])/,o.autolink=f(o.autolink).replace("scheme",o._scheme).replace("email",o._email).getRegex(),o._attribute=/\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/,o.tag=f(o.tag).replace("comment",n._comment).replace("attribute",o._attribute).getRegex(),o._label=/(?:\[[^\[\]]*\]|\\.|`[^`]*`|[^\[\]\\`])*?/,o._href=/<(?:\\[<>]?|[^\s<>\\])*>|[^\s\x00-\x1f]*/,o._title=/"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/,o.link=f(o.link).replace("label",o._label).replace("href",o._href).replace("title",o._title).getRegex(),o.reflink=f(o.reflink).replace("label",o._label).getRegex(),o.normal=y({},o),o.pedantic=y({},o.normal,{strong:/^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,em:/^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/,link:f(/^!?\[(label)\]\((.*?)\)/).replace("label",o._label).getRegex(),reflink:f(/^!?\[(label)\]\s*\[([^\]]*)\]/).replace("label",o._label).getRegex()}),o.gfm=y({},o.normal,{escape:f(o.escape).replace("])","~|])").getRegex(),_extended_email:/[A-Za-z0-9._+-]+(@)[a-zA-Z0-9-_]+(?:\.[a-zA-Z0-9-_]*[a-zA-Z0-9])+(?![-_])/,url:/^((?:ftp|https?):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/,_backpedal:/(?:[^?!.,:;*_~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_~)]+(?!$))+/,del:/^~+(?=\S)([\s\S]*?\S)~+/,text:/^(`+|[^`])(?:[\s\S]*?(?:(?=[\\/i.test(a[0])&&(this.inLink=!1),!this.inRawBlock&&/^<(pre|code|kbd|script)(\s|>)/i.test(a[0])?this.inRawBlock=!0:this.inRawBlock&&/^<\/(pre|code|kbd|script)(\s|>)/i.test(a[0])&&(this.inRawBlock=!1),e=e.substring(a[0].length),l+=this.options.sanitize?this.options.sanitizer?this.options.sanitizer(a[0]):u(a[0]):a[0];else if(a=this.rules.link.exec(e)){var c=x(a[2],"()");if(c>-1){var p=4+a[1].length+c;a[2]=a[2].substring(0,c),a[0]=a[0].substring(0,p).trim(),a[3]=""}e=e.substring(a[0].length),this.inLink=!0,r=a[2],this.options.pedantic?(t=/^([^'"]*[^\s])\s+(['"])(.*)\2/.exec(r))?(r=t[1],o=t[3]):o="":o=a[3]?a[3].slice(1,-1):"",r=r.trim().replace(/^<([\s\S]*)>$/,"$1"),l+=this.outputLink(a,{href:i.escapes(r),title:i.escapes(o)}),this.inLink=!1}else if((a=this.rules.reflink.exec(e))||(a=this.rules.nolink.exec(e))){if(e=e.substring(a[0].length),t=(a[2]||a[1]).replace(/\s+/g," "),!(t=this.links[t.toLowerCase()])||!t.href){l+=a[0].charAt(0),e=a[0].substring(1)+e;continue}this.inLink=!0,l+=this.outputLink(a,t),this.inLink=!1}else if(a=this.rules.strong.exec(e))e=e.substring(a[0].length),l+=this.renderer.strong(this.output(a[4]||a[3]||a[2]||a[1]));else if(a=this.rules.em.exec(e))e=e.substring(a[0].length),l+=this.renderer.em(this.output(a[6]||a[5]||a[4]||a[3]||a[2]||a[1]));else if(a=this.rules.code.exec(e))e=e.substring(a[0].length),l+=this.renderer.codespan(u(a[2].trim(),!0));else if(a=this.rules.br.exec(e))e=e.substring(a[0].length),l+=this.renderer.br();else if(a=this.rules.del.exec(e))e=e.substring(a[0].length),l+=this.renderer.del(this.output(a[1]));else if(a=this.rules.autolink.exec(e))e=e.substring(a[0].length),r="@"===a[2]?"mailto:"+(n=u(this.mangle(a[1]))):n=u(a[1]),l+=this.renderer.link(r,null,n);else if(this.inLink||!(a=this.rules.url.exec(e))){if(a=this.rules.text.exec(e))e=e.substring(a[0].length),this.inRawBlock?l+=this.renderer.text(this.options.sanitize?this.options.sanitizer?this.options.sanitizer(a[0]):u(a[0]):a[0]):l+=this.renderer.text(u(this.smartypants(a[0])));else if(e)throw new Error("Infinite loop on byte: "+e.charCodeAt(0))}else{if("@"===a[2])r="mailto:"+(n=u(a[0]));else{do{s=a[0],a[0]=this.rules._backpedal.exec(a[0])[0]}while(s!==a[0]);n=u(a[0]),r="www."===a[1]?"http://"+n:n}e=e.substring(a[0].length),l+=this.renderer.link(r,null,n)}return l},i.escapes=function(e){return e?e.replace(i.rules._escapes,"$1"):e},i.prototype.outputLink=function(e,t){var n=t.href,r=t.title?u(t.title):null;return"!"!==e[0].charAt(0)?this.renderer.link(n,r,this.output(e[1])):this.renderer.image(n,r,u(e[1]))},i.prototype.smartypants=function(e){return this.options.smartypants?e.replace(/---/g,"—").replace(/--/g,"–").replace(/(^|[-\u2014/(\[{"\s])'/g,"$1‘").replace(/'/g,"’").replace(/(^|[-\u2014/(\[{\u2018\s])"/g,"$1“").replace(/"/g,"”").replace(/\.{3}/g,"…"):e},i.prototype.mangle=function(e){if(!this.options.mangle)return e;for(var t,n="",r=e.length,o=0;o.5&&(t="x"+t.toString(16)),n+="&#"+t+";";return n},a.prototype.code=function(e,t,n){var r=(t||"").match(/\S*/)[0];if(this.options.highlight){var o=this.options.highlight(e,r);null!=o&&o!==e&&(n=!0,e=o)}return r?'
'+(n?e:u(e,!0))+"
\n":"
"+(n?e:u(e,!0))+"
"},a.prototype.blockquote=function(e){return"
\n"+e+"
\n"},a.prototype.html=function(e){return e},a.prototype.heading=function(e,t,n,r){return this.options.headerIds?"'+e+"\n":""+e+"\n"},a.prototype.hr=function(){return this.options.xhtml?"
\n":"
\n"},a.prototype.list=function(e,t,n){var r=t?"ol":"ul";return"<"+r+(t&&1!==n?' start="'+n+'"':"")+">\n"+e+"\n"},a.prototype.listitem=function(e){return"
  • "+e+"
  • \n"},a.prototype.checkbox=function(e){return" "},a.prototype.paragraph=function(e){return"

    "+e+"

    \n"},a.prototype.table=function(e,t){return t&&(t=""+t+""),"\n\n"+e+"\n"+t+"
    \n"},a.prototype.tablerow=function(e){return"\n"+e+"\n"},a.prototype.tablecell=function(e,t){var n=t.header?"th":"td";return(t.align?"<"+n+' align="'+t.align+'">':"<"+n+">")+e+"\n"},a.prototype.strong=function(e){return""+e+""},a.prototype.em=function(e){return""+e+""},a.prototype.codespan=function(e){return""+e+""},a.prototype.br=function(){return this.options.xhtml?"
    ":"
    "},a.prototype.del=function(e){return""+e+""},a.prototype.link=function(e,t,n){if(null===(e=d(this.options.sanitize,this.options.baseUrl,e)))return n;var r='"},a.prototype.image=function(e,t,n){if(null===(e=d(this.options.sanitize,this.options.baseUrl,e)))return n;var r=''+n+'":">"},a.prototype.text=function(e){return e},s.prototype.strong=s.prototype.em=s.prototype.codespan=s.prototype.del=s.prototype.text=function(e){return e},s.prototype.link=s.prototype.image=function(e,t,n){return""+n},s.prototype.br=function(){return""},l.parse=function(e,t){return new l(t).parse(e)},l.prototype.parse=function(e){this.inline=new i(e.links,this.options),this.inlineText=new i(e.links,y({},this.options,{renderer:new s})),this.tokens=e.reverse();for(var t="";this.next();)t+=this.tok();return t},l.prototype.next=function(){return this.token=this.tokens.pop(),this.token},l.prototype.peek=function(){return this.tokens[this.tokens.length-1]||0},l.prototype.parseText=function(){for(var e=this.token.text;"text"===this.peek().type;)e+="\n"+this.next().text;return this.inline.output(e)},l.prototype.tok=function(){switch(this.token.type){case"space":return"";case"hr":return this.renderer.hr();case"heading":return this.renderer.heading(this.inline.output(this.token.text),this.token.depth,p(this.inlineText.output(this.token.text)),this.slugger);case"code":return this.renderer.code(this.token.text,this.token.lang,this.token.escaped);case"table":var e,t,n,r,o="",i="";for(n="",e=0;e?@[\]^`{|}~]/g,"").replace(/\s/g,"-");if(this.seen.hasOwnProperty(t)){var n=t;do{this.seen[n]++,t=n+"-"+this.seen[n]}while(this.seen.hasOwnProperty(t))}return this.seen[t]=0,t},u.escapeTest=/[&<>"']/,u.escapeReplace=/[&<>"']/g,u.replacements={"&":"&","<":"<",">":">",'"':""","'":"'"},u.escapeTestNoEncode=/[<>"']|&(?!#?\w+;)/,u.escapeReplaceNoEncode=/[<>"']|&(?!#?\w+;)/g;var h={},m=/^$|^[a-z][a-z0-9+.-]*:|^[?#]/i;function g(){}function y(e){for(var t,n,r=1;r=0&&"\\"===n[o];)r=!r;return r?"|":" |"})).split(/ \|/),r=0;if(n.length>t)n.splice(t);else for(;n.lengthAn error occurred:

    "+u(e.message+"",!0)+"
    ";throw e}}g.exec=g,k.options=k.setOptions=function(e){return y(k.defaults,e),k},k.getDefaults=function(){return{baseUrl:null,breaks:!1,gfm:!0,headerIds:!0,headerPrefix:"",highlight:null,langPrefix:"language-",mangle:!0,pedantic:!1,renderer:new a,sanitize:!1,sanitizer:null,silent:!1,smartLists:!1,smartypants:!1,xhtml:!1}},k.defaults=k.getDefaults(),k.Parser=l,k.parser=l.parse,k.Renderer=a,k.TextRenderer=s,k.Lexer=r,k.lexer=r.lex,k.InlineLexer=i,k.inlineLexer=i.output,k.Slugger=c,k.parse=k,e.exports=k}(this||"undefined"!=typeof window&&window)}).call(this,n(7))},function(e,t,n){var r=n(9);e.exports=function(e,t){if(!r(e))return e;var n,o;if(t&&"function"==typeof(n=e.toString)&&!r(o=n.call(e)))return o;if("function"==typeof(n=e.valueOf)&&!r(o=n.call(e)))return o;if(!t&&"function"==typeof(n=e.toString)&&!r(o=n.call(e)))return o;throw TypeError("Can't convert object to primitive value")}},function(e,t){var n=0,r=Math.random();e.exports=function(e){return"Symbol("+String(void 0===e?"":e)+")_"+(++n+r).toString(36)}},function(e,t,n){var r=n(70),o=n(55),i=r("keys");e.exports=function(e){return i[e]||(i[e]=o(e))}},function(e,t,n){var r,o=n(21),i=n(173),a=n(80),s=n(43),l=n(110),c=n(72),u=n(56),p=u("IE_PROTO"),f=function(){},d=function(e){return"