Normalize and standardize Laravel API response data structure. - 规范化和标准化 Laravel API 响应数据结构。
guanguans/laravel-api-response is a Laravel package for normalize and standardize laravel api response data structure. - 规范化和标准化 laravel api 响应数据结构。.
It currently has 48 GitHub stars and 6.235 downloads on Packagist (latest version 4.0.1).
Install it with composer require guanguans/laravel-api-response.
Discover more Laravel packages by guanguans
or browse all Laravel packages to compare alternatives.
Last updated
Normalize and standardize Laravel API response data structure. - 规范化和标准化 Laravel API 响应数据结构。
composer require guanguans/laravel-api-response --ansi -v
php artisan vendor:publish --provider="Guanguans\\LaravelApiResponse\\ServiceProvider" --ansi -v
<?php
namespace App\Http\Controllers\Api;
use Guanguans\LaravelApiResponse\Support\Traits\ApiResponseFactory;
use Illuminate\Http\JsonResponse;
use Symfony\Component\HttpKernel\Exception\HttpException;
class Controller extends \App\Http\Controllers\Controller
{
use ApiResponseFactory;
public function examples(): JsonResponse
{
// return $this->apiResponse()->error($message);
// return $this->apiResponse()->badRequest($message);
// return $this->apiResponse()->unauthorized($message);
// return $this->apiResponse()->exception($exception);
// throw new \RuntimeException('This is a runtime exception.');
// throw new HttpException(400);
// return $this->apiResponse()->success($data);
// return $this->apiResponse()->noContent($message);
// ...
return $this->apiResponse()->ok();
}
}
{
"status": "bool",
"code": "int",
"message": "string",
"data": "mixed",
"error": "array<string, mixed>"
}
<?php
namespace App\Http\Controllers\Api;
use Guanguans\LaravelApiResponse\Support\Traits\ApiResponseFactory;
use Workbench\App\Models\User;
use Illuminate\Http\JsonResponse;
class Controller extends \App\Http\Controllers\Controller
{
use ApiResponseFactory;
public function example(): JsonResponse
{
$user = User::query()->with(['country', 'posts'])->first();
return $this->apiResponse()->success($user);
}
}
{
"status": true,
"code": 200,
"message": "OK",
"data": {
"id": 1,
"name": "John",
"country_id": 1,
"created_at": "2024-01-01 00:00:01",
"updated_at": "2024-01-01 00:00:01",
"country": {
"id": 1,
"name": "China",
"created_at": "2024-01-01 00:00:01",
"updated_at": "2024-01-01 00:00:01"
},
"posts": [
{
"id": 1,
"title": "PHP is the best language!",
"user_id": 1,
"created_at": "2024-01-01 00:00:01",
"updated_at": "2024-01-01 00:00:01"
},
{
"id": 2,
"title": "JAVA is the best language!",
"user_id": 1,
"created_at": "2024-01-01 00:00:02",
"updated_at": "2024-01-01 00:00:02"
},
{
"id": 3,
"title": "Python is the best language!",
"user_id": 1,
"created_at": "2024-01-01 00:00:03",
"updated_at": "2024-01-01 00:00:03"
}
]
},
"error": {}
}
<?php
namespace App\Http\Controllers\Api;
use Guanguans\LaravelApiResponse\Support\Traits\ApiResponseFactory;
use Workbench\App\Models\User;
use Illuminate\Http\JsonResponse;
class Controller extends \App\Http\Controllers\Controller
{
use ApiResponseFactory;
public function example(): JsonResponse
{
$users = User::query()->with(['country', 'posts'])->get();
return $this->apiResponse()->success($users);
}
}
{
"status": true,
"code": 200,
"message": "OK",
"data": [
{
"id": 1,
"name": "John",
"country_id": 1,
"created_at": "2024-01-01 00:00:01",
"updated_at": "2024-01-01 00:00:01",
"country": {
"id": 1,
"name": "China",
"created_at": "2024-01-01 00:00:01",
"updated_at": "2024-01-01 00:00:01"
},
"posts": [
{
"id": 1,
"title": "PHP is the best language!",
"user_id": 1,
"created_at": "2024-01-01 00:00:01",
"updated_at": "2024-01-01 00:00:01"
},
{
"id": 2,
"title": "JAVA is the best language!",
"user_id": 1,
"created_at": "2024-01-01 00:00:02",
"updated_at": "2024-01-01 00:00:02"
},
{
"id": 3,
"title": "Python is the best language!",
"user_id": 1,
"created_at": "2024-01-01 00:00:03",
"updated_at": "2024-01-01 00:00:03"
}
]
},
{
"id": 2,
"name": "Tom",
"country_id": 2,
"created_at": "2024-01-01 00:00:02",
"updated_at": "2024-01-01 00:00:02",
"country": {
"id": 2,
"name": "USA",
"created_at": "2024-01-01 00:00:02",
"updated_at": "2024-01-01 00:00:02"
},
"posts": [
{
"id": 4,
"title": "Go is the best language!",
"user_id": 2,
"created_at": "2024-01-01 00:00:04",
"updated_at": "2024-01-01 00:00:04"
},
{
"id": 5,
"title": "JavaScript is the best language!",
"user_id": 2,
"created_at": "2024-01-01 00:00:05",
"updated_at": "2024-01-01 00:00:05"
},
{
"id": 6,
"title": "Ruby is the best language!",
"user_id": 2,
"created_at": "2024-01-01 00:00:06",
"updated_at": "2024-01-01 00:00:06"
}
]
},
{
"id": 3,
"name": "Jerry",
"country_id": 3,
"created_at": "2024-01-01 00:00:03",
"updated_at": "2024-01-01 00:00:03",
"country": {
"id": 3,
"name": "Japan",
"created_at": "2024-01-01 00:00:03",
"updated_at": "2024-01-01 00:00:03"
},
"posts": [
{
"id": 7,
"title": "C is the best language!",
"user_id": 3,
"created_at": "2024-01-01 00:00:07",
"updated_at": "2024-01-01 00:00:07"
}
]
},
{
"id": 4,
"name": "Jack",
"country_id": 4,
"created_at": "2024-01-01 00:00:04",
"updated_at": "2024-01-01 00:00:04",
"country": {
"id": 4,
"name": "Korea",
"created_at": "2024-01-01 00:00:04",
"updated_at": "2024-01-01 00:00:04"
},
"posts": []
},
{
"id": 5,
"name": "Rose",
"country_id": 5,
"created_at": "2024-01-01 00:00:05",
"updated_at": "2024-01-01 00:00:05",
"country": {
"id": 5,
"name": "UK",
"created_at": "2024-01-01 00:00:05",
"updated_at": "2024-01-01 00:00:05"
},
"posts": []
},
{
"id": 6,
"name": "Lucy",
"country_id": 6,
"created_at": "2024-01-01 00:00:06",
"updated_at": "2024-01-01 00:00:06",
"country": {
"id": 6,
"name": "France",
"created_at": "2024-01-01 00:00:06",
"updated_at": "2024-01-01 00:00:06"
},
"posts": []
},
{
"id": 7,
"name": "Lily",
"country_id": 7,
"created_at": "2024-01-01 00:00:07",
"updated_at": "2024-01-01 00:00:07",
"country": {
"id": 7,
"name": "Germany",
"created_at": "2024-01-01 00:00:07",
"updated_at": "2024-01-01 00:00:07"
},
"posts": []
}
],
"error": {}
}
<?php
namespace App\Http\Controllers\Api;
use Guanguans\LaravelApiResponse\Support\Traits\ApiResponseFactory;
use Workbench\App\Models\User;
use Illuminate\Http\JsonResponse;
class Controller extends \App\Http\Controllers\Controller
{
use ApiResponseFactory;
public function example(): JsonResponse
{
$simplePaginate = User::query()->with(['country', 'posts'])->simplePaginate(3);
return $this->apiResponse()->success($simplePaginate);
}
}
{
"status": true,
"code": 200,
"message": "OK",
"data": {
"data": [
{
"id": 1,
"name": "John",
"country_id": 1,
"created_at": "2024-01-01 00:00:01",
"updated_at": "2024-01-01 00:00:01",
"country": {
"id": 1,
"name": "China",
"created_at": "2024-01-01 00:00:01",
"updated_at": "2024-01-01 00:00:01"
},
"posts": [
{
"id": 1,
"title": "PHP is the best language!",
"user_id": 1,
"created_at": "2024-01-01 00:00:01",
"updated_at": "2024-01-01 00:00:01"
},
{
"id": 2,
"title": "JAVA is the best language!",
"user_id": 1,
"created_at": "2024-01-01 00:00:02",
"updated_at": "2024-01-01 00:00:02"
},
{
"id": 3,
"title": "Python is the best language!",
"user_id": 1,
"created_at": "2024-01-01 00:00:03",
"updated_at": "2024-01-01 00:00:03"
}
]
},
{
"id": 2,
"name": "Tom",
"country_id": 2,
"created_at": "2024-01-01 00:00:02",
"updated_at": "2024-01-01 00:00:02",
"country": {
"id": 2,
"name": "USA",
"created_at": "2024-01-01 00:00:02",
"updated_at": "2024-01-01 00:00:02"
},
"posts": [
{
"id": 4,
"title": "Go is the best language!",
"user_id": 2,
"created_at": "2024-01-01 00:00:04",
"updated_at": "2024-01-01 00:00:04"
},
{
"id": 5,
"title": "JavaScript is the best language!",
"user_id": 2,
"created_at": "2024-01-01 00:00:05",
"updated_at": "2024-01-01 00:00:05"
},
{
"id": 6,
"title": "Ruby is the best language!",
"user_id": 2,
"created_at": "2024-01-01 00:00:06",
"updated_at": "2024-01-01 00:00:06"
}
]
},
{
"id": 3,
"name": "Jerry",
"country_id": 3,
"created_at": "2024-01-01 00:00:03",
"updated_at": "2024-01-01 00:00:03",
"country": {
"id": 3,
"name": "Japan",
"created_at": "2024-01-01 00:00:03",
"updated_at": "2024-01-01 00:00:03"
},
"posts": [
{
"id": 7,
"title": "C is the best language!",
"user_id": 3,
"created_at": "2024-01-01 00:00:07",
"updated_at": "2024-01-01 00:00:07"
}
]
}
],
"links": {
"first": "http:\/\/localhost?page=1",
"last": null,
"prev": null,
"next": "http:\/\/localhost?page=2"
},
"meta": {
"current_page": 1,
"from": 1,
"path": "http:\/\/localhost",
"per_page": 3,
"to": 3
}
},
"error": {}
}
<?php
namespace App\Http\Controllers\Api;
use Guanguans\LaravelApiResponse\Support\Traits\ApiResponseFactory;
use Workbench\App\Models\User;
use Workbench\App\Http\Resources\UserResource;
use Illuminate\Http\JsonResponse;
class Controller extends \App\Http\Controllers\Controller
{
use ApiResponseFactory;
public function example(): JsonResponse
{
$userResource = UserResource::make(User::query()->with(['country', 'posts'])->first());
return $this->apiResponse()->success($userResource);
}
}
{
"status": true,
"code": 200,
"message": "OK",
"data": {
"id": 1,
"name": "John",
"country_id": 1,
"created_at": "2024-01-01 00:00:01",
"updated_at": "2024-01-01 00:00:01",
"country": {
"id": 1,
"name": "China",
"created_at": "2024-01-01 00:00:01",
"updated_at": "2024-01-01 00:00:01"
},
"posts": [
{
"id": 1,
"title": "PHP is the best language!",
"user_id": 1,
"created_at": "2024-01-01 00:00:01",
"updated_at": "2024-01-01 00:00:01"
},
{
"id": 2,
"title": "JAVA is the best language!",
"user_id": 1,
"created_at": "2024-01-01 00:00:02",
"updated_at": "2024-01-01 00:00:02"
},
{
"id": 3,
"title": "Python is the best language!",
"user_id": 1,
"created_at": "2024-01-01 00:00:03",
"updated_at": "2024-01-01 00:00:03"
}
]
},
"error": {}
}
<?php
namespace App\Http\Controllers\Api;
use Guanguans\LaravelApiResponse\Support\Traits\ApiResponseFactory;
use Workbench\App\Models\User;
use Workbench\App\Http\Resources\UserCollection;
use Illuminate\Http\JsonResponse;
class Controller extends \App\Http\Controllers\Controller
{
use ApiResponseFactory;
public function example(): JsonResponse
{
$userCollection = UserCollection::make(User::query()->with(['country', 'posts'])->get());
return $this->apiResponse()->success($userCollection);
}
}
{
"status": true,
"code": 200,
"message": "OK",
"data": [
{
"id": 1,
"name": "John",
"country_id": 1,
"created_at": "2024-01-01 00:00:01",
"updated_at": "2024-01-01 00:00:01",
"country": {
"id": 1,
"name": "China",
"created_at": "2024-01-01 00:00:01",
"updated_at": "2024-01-01 00:00:01"
},
"posts": [
{
"id": 1,
"title": "PHP is the best language!",
"user_id": 1,
"created_at": "2024-01-01 00:00:01",
"updated_at": "2024-01-01 00:00:01"
},
{
"id": 2,
"title": "JAVA is the best language!",
"user_id": 1,
"created_at": "2024-01-01 00:00:02",
"updated_at": "2024-01-01 00:00:02"
},
{
"id": 3,
"title": "Python is the best language!",
"user_id": 1,
"created_at": "2024-01-01 00:00:03",
"updated_at": "2024-01-01 00:00:03"
}
]
},
{
"id": 2,
"name": "Tom",
"country_id": 2,
"created_at": "2024-01-01 00:00:02",
"updated_at": "2024-01-01 00:00:02",
"country": {
"id": 2,
"name": "USA",
"created_at": "2024-01-01 00:00:02",
"updated_at": "2024-01-01 00:00:02"
},
"posts": [
{
"id": 4,
"title": "Go is the best language!",
"user_id": 2,
"created_at": "2024-01-01 00:00:04",
"updated_at": "2024-01-01 00:00:04"
},
{
"id": 5,
"title": "JavaScript is the best language!",
"user_id": 2,
"created_at": "2024-01-01 00:00:05",
"updated_at": "2024-01-01 00:00:05"
},
{
"id": 6,
"title": "Ruby is the best language!",
"user_id": 2,
"created_at": "2024-01-01 00:00:06",
"updated_at": "2024-01-01 00:00:06"
}
]
},
{
"id": 3,
"name": "Jerry",
"country_id": 3,
"created_at": "2024-01-01 00:00:03",
"updated_at": "2024-01-01 00:00:03",
"country": {
"id": 3,
"name": "Japan",
"created_at": "2024-01-01 00:00:03",
"updated_at": "2024-01-01 00:00:03"
},
"posts": [
{
"id": 7,
"title": "C is the best language!",
"user_id": 3,
"created_at": "2024-01-01 00:00:07",
"updated_at": "2024-01-01 00:00:07"
}
]
},
{
"id": 4,
"name": "Jack",
"country_id": 4,
"created_at": "2024-01-01 00:00:04",
"updated_at": "2024-01-01 00:00:04",
"country": {
"id": 4,
"name": "Korea",
"created_at": "2024-01-01 00:00:04",
"updated_at": "2024-01-01 00:00:04"
},
"posts": []
},
{
"id": 5,
"name": "Rose",
"country_id": 5,
"created_at": "2024-01-01 00:00:05",
"updated_at": "2024-01-01 00:00:05",
"country": {
"id": 5,
"name": "UK",
"created_at": "2024-01-01 00:00:05",
"updated_at": "2024-01-01 00:00:05"
},
"posts": []
},
{
"id": 6,
"name": "Lucy",
"country_id": 6,
"created_at": "2024-01-01 00:00:06",
"updated_at": "2024-01-01 00:00:06",
"country": {
"id": 6,
"name": "France",
"created_at": "2024-01-01 00:00:06",
"updated_at": "2024-01-01 00:00:06"
},
"posts": []
},
{
"id": 7,
"name": "Lily",
"country_id": 7,
"created_at": "2024-01-01 00:00:07",
"updated_at": "2024-01-01 00:00:07",
"country": {
"id": 7,
"name": "Germany",
"created_at": "2024-01-01 00:00:07",
"updated_at": "2024-01-01 00:00:07"
},
"posts": []
}
],
"error": {}
}
<?php
namespace App\Http\Controllers\Api;
use Guanguans\LaravelApiResponse\Support\Traits\ApiResponseFactory;
use Illuminate\Http\JsonResponse;
class Controller extends \App\Http\Controllers\Controller
{
use ApiResponseFactory;
public function example(): JsonResponse
{
return $this->apiResponse()->error('This is an error.');
}
}
{
"status": false,
"code": 400,
"message": "This is an error.",
"data": null,
"error": {}
}
<?php
namespace App\Http\Controllers\Api;
use Guanguans\LaravelApiResponse\Support\Traits\ApiResponseFactory;
use Illuminate\Http\JsonResponse;
class Controller extends \App\Http\Controllers\Controller
{
use ApiResponseFactory;
public function example(): JsonResponse
{
config()->set('app.debug', false);
$runtimeException = new \RuntimeException('This is a runtime exception.');
return $this->apiResponse()->exception($runtimeException);
}
}
{
"status": false,
"code": 500,
"message": "Internal Server Error",
"data": null,
"error": {
"message": "Internal Server Error"
}
}
<?php
namespace App\Http\Controllers\Api;
use Guanguans\LaravelApiResponse\Support\Traits\ApiResponseFactory;
use Illuminate\Http\JsonResponse;
class Controller extends \App\Http\Controllers\Controller
{
use ApiResponseFactory;
public function example(): JsonResponse
{
config()->set('app.debug', true);
$runtimeException = new \RuntimeException('This is a runtime exception.');
return $this->apiResponse()->exception($runtimeException);
}
}
{
"status": false,
"code": 500,
"message": "This is a runtime exception.",
"data": null,
"error": {
"message": "This is a runtime exception.",
"exception": "RuntimeException",
"file": "\/tests\/Feature\/ExceptionDataTypesTest.php",
"line": 45,
"trace": [
{
"function": "{closure}",
"class": "P\\Tests\\Feature\\ExceptionDataTypesTest",
"type": "->"
},
{
"file": "\/vendor\/pestphp\/pest\/src\/Factories\/TestCaseFactory.php",
"line": 151,
"function": "call_user_func"
},
{
"function": "Pest\\Factories\\{closure}",
"class": "P\\Tests\\Feature\\ExceptionDataTypesTest",
"type": "->"
},
{
"file": "\/vendor\/pestphp\/pest\/src\/Concerns\/Testable.php",
"line": 301,
"function": "call_user_func_array"
},
{
"file": "\/vendor\/pestphp\/pest\/src\/Support\/ExceptionTrace.php",
"line": 29,
"function": "Pest\\Concerns\\{closure}",
"class": "P\\Tests\\Feature\\ExceptionDataTypesTest",
"type": "->"
},
{
"file": "\/vendor\/pestphp\/pest\/src\/Concerns\/Testable.php",
"line": 302,
"function": "ensure",
"class": "Pest\\Support\\ExceptionTrace",
"type": "::"
},
{
"file": "\/vendor\/pestphp\/pest\/src\/Concerns\/Testable.php",
"line": 278,
"function": "__callClosure",
"class": "P\\Tests\\Feature\\ExceptionDataTypesTest",
"type": "->"
},
{
"file": "\/vendor\/phpunit\/phpunit\/src\/Framework\/TestCase.php",
"line": 1617,
"function": "__test",
"class": "P\\Tests\\Feature\\ExceptionDataTypesTest",
"type": "->"
},
{
"file": "\/vendor\/phpunit\/phpunit\/src\/Framework\/TestCase.php",
"line": 1223,
"function": "runTest",
"class": "PHPUnit\\Framework\\TestCase",
"type": "->"
},
{
"file": "\/vendor\/phpunit\/phpunit\/src\/Framework\/TestResult.php",
"line": 729,
"function": "runBare",
"class": "PHPUnit\\Framework\\TestCase",
"type": "->"
},
{
"file": "\/vendor\/phpunit\/phpunit\/src\/Framework\/TestCase.php",
"line": 973,
"function": "run",
"class": "PHPUnit\\Framework\\TestResult",
"type": "->"
},
{
"file": "\/vendor\/phpunit\/phpunit\/src\/Framework\/TestSuite.php",
"line": 685,
"function": "run",
"class": "PHPUnit\\Framework\\TestCase",
"type": "->"
},
{
"file": "\/vendor\/phpunit\/phpunit\/src\/Framework\/TestSuite.php",
"line": 685,
"function": "run",
"class": "PHPUnit\\Framework\\TestSuite",
"type": "->"
},
{
"file": "\/vendor\/phpunit\/phpunit\/src\/TextUI\/TestRunner.php",
"line": 651,
"function": "run",
"class": "PHPUnit\\Framework\\TestSuite",
"type": "->"
},
{
"file": "\/vendor\/phpunit\/phpunit\/src\/TextUI\/Command.php",
"line": 146,
"function": "run",
"class": "PHPUnit\\TextUI\\TestRunner",
"type": "->"
},
{
"file": "\/vendor\/pestphp\/pest\/src\/Console\/Command.php",
"line": 119,
"function": "run",
"class": "PHPUnit\\TextUI\\Command",
"type": "->"
},
{
"file": "\/vendor\/pestphp\/pest\/bin\/pest",
"line": 61,
"function": "run",
"class": "Pest\\Console\\Command",
"type": "->"
},
{
"file": "\/vendor\/pestphp\/pest\/bin\/pest",
"line": 62,
"function": "{closure}"
},
{
"file": "\/vendor\/bin\/pest",
"line": 115,
"function": "include"
}
]
}
}
<?php
namespace App\Http\Controllers\Api;
use Guanguans\LaravelApiResponse\Middleware\SetJsonAcceptHeader;
use Illuminate\Http\JsonResponse;
class Controller extends \App\Http\Controllers\Controller
{
public function __construct()
{
$this->middleware(SetJsonAcceptHeader::class)->only('example');
}
public function example(): JsonResponse
{
config()->set('app.debug', false);
throw new \RuntimeException('This is a runtime exception.');
}
}
{
"status": false,
"code": 500,
"message": "Internal Server Error",
"data": null,
"error": {
"message": "Internal Server Error"
}
}
<?php
namespace App\Http\Controllers\Api;
use Guanguans\LaravelApiResponse\Support\Traits\ApiResponseFactory;
use Illuminate\Http\JsonResponse;
class Controller extends \App\Http\Controllers\Controller
{
use ApiResponseFactory;
public function example(): JsonResponse
{
config()->set('app.debug', false);
config()->set('app.locale', 'zh_CN');
$runtimeException = new \RuntimeException('This is a runtime exception.');
return $this->apiResponse()->exception($runtimeException);
}
}
resources/lang/zh_CN.json
{
"...": "...",
"Unauthenticated.": "未认证。",
"This is a runtime exception.": "这是一个运行时异常。"
}
{
"status": false,
"code": 500,
"message": "这是一个运行时异常。",
"data": null,
"error": {
"message": "这是一个运行时异常。"
}
}
<?php
namespace App\Http\Controllers\Api;
use Guanguans\LaravelApiResponse\Contracts\ApiResponseContract;
use Guanguans\LaravelApiResponse\Facades\ApiResponseFacade;
use Guanguans\LaravelApiResponse\Support\Traits\ApiResponseFactory;
use Illuminate\Http\JsonResponse;
class Controller extends \App\Http\Controllers\Controller
{
use ApiResponseFactory;
public function getInstance(): JsonResponse
{
/** @var \Guanguans\LaravelApiResponse\ApiResponse $apiResponse */
// $apiResponse = ApiResponseFacade::getFacadeRoot();
// $apiResponse = resolve(ApiResponseContract::class);
// $apiResponse = app(ApiResponseContract::class);
$apiResponse = $this->apiResponse();
return $apiResponse->ok();
}
}
api-response.render_usingapi-response.pipes.StatusCodePipecomposer require --dev laravel-lang/http-statuses or create lang files resources/lang/***/http-statuses.php]resources/lang/***.json]<?php
static function (array $structure, \Closure $next): JsonResponse {
if ($structure['data'] instanceof \Traversable) {
$structure['data'] = iterator_to_array($structure['data']);
}
return $next($structure);
};
<?php
namespace App\Http\Controllers\Api;
use Guanguans\LaravelApiResponse\Support\Traits\ApiResponseFactory;
use Illuminate\Http\JsonResponse;
class Controller extends \App\Http\Controllers\Controller
{
use ApiResponseFactory;
public function example(): JsonResponse
{
return $this
->apiResponse()
// ->unshiftPipes(...$pipes)
// ->pushPipes(...$pipes)
// ->beforePipes($findPipe, ...$pipes)
// ->afterPipes($findPipe, ...$pipes)
// ->removePipes(...$findPipes)
// ->extendPipes($callback)
// ->tapPipes($callback)
->success($data);
}
}
composer checks:required
composer php-cs-fixer:fix
composer test
Please see CHANGELOG for more information on what has changed recently.
Please see CONTRIBUTING for details.
Please review our security policy on how to report security vulnerabilities.
The MIT License (MIT). Please see License File for more information.