PHPackages                             agelxnash/laravel-queue-payload - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. agelxnash/laravel-queue-payload

ActiveLibrary

agelxnash/laravel-queue-payload
===============================

Cross-platform RabbitMQ message format for Laravel microservices with RPC support

00PHPCI failing

Since Apr 5Pushed 1mo agoCompare

[ Source](https://github.com/AgelxNash/laravel-queue-payload)[ Packagist](https://packagist.org/packages/agelxnash/laravel-queue-payload)[ RSS](/packages/agelxnash-laravel-queue-payload/feed)WikiDiscussions main Synced 1mo ago

READMEChangelogDependenciesVersions (1)Used By (0)

Laravel Queue Payload
=====================

[](#laravel-queue-payload)

Пакет для Laravel, обеспечивающий удобное и прозрачное межсервисное взаимодействие (RPC / Event Messaging) через RabbitMQ.

Стандартные очереди Laravel жёстко привязаны к внутренним классам фреймворка (сериализация объектов через `CallQueuedHandler`). Это делает невозможным чтение и отправку таких очередей из других языков — Go, Node.js, Python.

**Этот пакет решает проблему обмена данными**, преобразовывая сложный нативный payload Laravel в простой кроссплатформенный JSON, а также добавляет поддержку **Request-Response (RPC) поверх очередей**.

[Статья на Хабре: Очереди в Laravel: заглядываем под капот и строим микросервисы](https://habr.com/ru/articles/748288/)

---

Содержание
----------

[](#содержание)

- [В чём разница форматов](#%D0%B2-%D1%87%D1%91%D0%BC-%D1%80%D0%B0%D0%B7%D0%BD%D0%B8%D1%86%D0%B0-%D1%84%D0%BE%D1%80%D0%BC%D0%B0%D1%82%D0%BE%D0%B2)
- [Требования](#%D1%82%D1%80%D0%B5%D0%B1%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F)
- [Установка](#%D1%83%D1%81%D1%82%D0%B0%D0%BD%D0%BE%D0%B2%D0%BA%D0%B0)
- [Архитектура: воркеры и роли](#%D0%B0%D1%80%D1%85%D0%B8%D1%82%D0%B5%D0%BA%D1%82%D1%83%D1%80%D0%B0-%D0%B2%D0%BE%D1%80%D0%BA%D0%B5%D1%80%D1%8B-%D0%B8-%D1%80%D0%BE%D0%BB%D0%B8)
- [Конфигурация RabbitMQ](#%D0%BA%D0%BE%D0%BD%D1%84%D0%B8%D0%B3%D1%83%D1%80%D0%B0%D1%86%D0%B8%D1%8F-rabbitmq)
- [Конфигурация пакета](#%D0%BA%D0%BE%D0%BD%D1%84%D0%B8%D0%B3%D1%83%D1%80%D0%B0%D1%86%D0%B8%D1%8F-%D0%BF%D0%B0%D0%BA%D0%B5%D1%82%D0%B0)
    - [Режимы маршрутизации ответов](#%D1%80%D0%B5%D0%B6%D0%B8%D0%BC%D1%8B-%D0%BC%D0%B0%D1%80%D1%88%D1%80%D1%83%D1%82%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8-%D0%BE%D1%82%D0%B2%D0%B5%D1%82%D0%BE%D0%B2)
    - [Allowlist job-классов](#allowlist-job-%D0%BA%D0%BB%D0%B0%D1%81%D1%81%D0%BE%D0%B2)
    - [HMAC-подпись](#hmac-%D0%BF%D0%BE%D0%B4%D0%BF%D0%B8%D1%81%D1%8C)
    - [Circuit Breaker](#circuit-breaker)
- [Использование](#%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5)
    - [RPC — ожидание ответа](#rpc--%D0%BE%D0%B6%D0%B8%D0%B4%D0%B0%D0%BD%D0%B8%D0%B5-%D0%BE%D1%82%D0%B2%D0%B5%D1%82%D0%B0)
    - [Fire-and-Forget — без ожидания](#fire-and-forget--%D0%B1%D0%B5%D0%B7-%D0%BE%D0%B6%D0%B8%D0%B4%D0%B0%D0%BD%D0%B8%D1%8F)
    - [Fluent Builder](#fluent-builder)
    - [Получение задач и отправка ответа](#%D0%BF%D0%BE%D0%BB%D1%83%D1%87%D0%B5%D0%BD%D0%B8%D0%B5-%D0%B7%D0%B0%D0%B4%D0%B0%D1%87-%D0%B8-%D0%BE%D1%82%D0%BF%D1%80%D0%B0%D0%B2%D0%BA%D0%B0-%D0%BE%D1%82%D0%B2%D0%B5%D1%82%D0%B0)
    - [Event Broadcasting](#event-broadcasting)
    - [DTO для параметров](#dto-%D0%B4%D0%BB%D1%8F-%D0%BF%D0%B0%D1%80%D0%B0%D0%BC%D0%B5%D1%82%D1%80%D0%BE%D0%B2)
    - [Кастомная сериализация при dispatch()](#%D0%BA%D0%B0%D1%81%D1%82%D0%BE%D0%BC%D0%BD%D0%B0%D1%8F-%D1%81%D0%B5%D1%80%D0%B8%D0%B0%D0%BB%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F-%D0%BF%D1%80%D0%B8-dispatch)
- [Observability](#observability)
- [Безопасность](#%D0%B1%D0%B5%D0%B7%D0%BE%D0%BF%D0%B0%D1%81%D0%BD%D0%BE%D1%81%D1%82%D1%8C)
- [Миграция shared → per\_request](#%D0%BC%D0%B8%D0%B3%D1%80%D0%B0%D1%86%D0%B8%D1%8F-shared--per_request)
- [Troubleshooting](#troubleshooting)
- [Документация / Wiki](#%D0%B4%D0%BE%D0%BA%D1%83%D0%BC%D0%B5%D0%BD%D1%82%D0%B0%D1%86%D0%B8%D1%8F--wiki)
- [Лицензия](#%D0%BB%D0%B8%D1%86%D0%B5%D0%BD%D0%B7%D0%B8%D1%8F)

---

В чём разница форматов
----------------------

[](#в-чём-разница-форматов)

Главная задача пакета — перехватывать входящие/исходящие задачи и формировать понятный JSON.

❌ **Стандартный Payload Laravel (Native):**

```
{
  "uuid": "59f3007b-e63c-4c71-b298-885563664cd6",
  "displayName": "App\\Jobs\\CheckUserTariffJob",
  "job": "Illuminate\\Queue\\CallQueuedHandler@call",
  "data": {
    "commandName": "App\\Jobs\\CheckUserTariffJob",
    "command": "O:28:\"App\\Jobs\\CheckUserTariffJob\":1:{s:6:\"userId\";i:12345;}"
  }
}
```

*Минусы: жёсткая привязка к PHP-сериализации. Микросервис на Go/Python это распарсить не сможет.*

✅ **Кроссплатформенный Payload (этот пакет):**

```
{
  "uuid": "59f3007b-e63c-4c71-b298-885563664cd3",
  "id": "123e4567-e89b-12d3-a456-426614174000",
  "job": "external",
  "data": {
    "type": "TASK_CHECK_TARIFF",
    "response": "auth-clients:response",
    "params": {
      "userId": 12345
    }
  }
}
```

*Плюсы: никаких сериализованных объектов. Любой внешний сервис может прислать такой JSON. Сигналом для Laravel является ключ `"job": "external"`.*

---

Требования
----------

[](#требования)

ЗависимостьВерсияPHP^8.2Laravel (illuminate/queue)^10.0 | ^11.0 | ^12.0vladimir-yuldashev/laravel-queue-rabbitmq^13.3 | ^14.0> **Важно:** режимы `per_request` и `direct_reply_to` требуют драйвер `RabbitMQQueue` (пакет `vladimir-yuldashev/laravel-queue-rabbitmq`). Режим `shared` работает с любым драйвером.

---

Установка
---------

[](#установка)

```
composer require agelxnash/laravel-queue-payload
```

Опубликуйте конфиг:

```
php artisan vendor:publish --provider="AgelxNash\LaravelQueuePayload\ServiceProvider"
```

---

Архитектура: воркеры и роли
---------------------------

[](#архитектура-воркеры-и-роли)

### Сервис-Получатель (выполняет работу)

[](#сервис-получатель-выполняет-работу)

Постоянно слушает очередь штатным демоном Laravel:

```
php artisan queue:work request
```

Получает упрощённый JSON, находит локальную Job по алиасу из `type`, мапит параметры и выполняет бизнес-логику.

### Сервис-Отправитель (запрашивает работу)

[](#сервис-отправитель-запрашивает-работу)

Дополнительные фоновые воркеры **не нужны**. Класс `ExternalJob` — встроенный хелпер пакета. При RPC-вызове `ExternalJob` автоматически ожидает ответ, используя `ResponseWorker` с поддержкой Fiber (не блокируя PHP-процесс целиком).

---

Конфигурация RabbitMQ
---------------------

[](#конфигурация-rabbitmq)

В каждом микросервисе в `config/queue.php` должны быть **2 соединения**:

```
use AgelxNash\LaravelQueuePayload\Enums\QueueConnections;

[
    QueueConnections::REQUEST->value => [
        'driver' => 'rabbitmq',
        'hosts' => [[
            'host'     => env('RABBITMQ_HOST', 'rabbit'),
            'port'     => env('RABBITMQ_PORT', 5672),
            'user'     => env('RABBITMQ_USER', 'guest'),
            'password' => env('RABBITMQ_PASSWORD', 'guest'),
            'vhost'    => env('RABBITMQ_VHOST', '/'),
        ]],
        'queue' => 'billing-service:' . QueueConnections::REQUEST->value,
    ],
    QueueConnections::RESPONSE->value => [
        'driver' => 'rabbitmq',
        'hosts' => [[
            'host'     => env('RABBITMQ_HOST', 'rabbit'),
            'port'     => env('RABBITMQ_PORT', 5672),
            'user'     => env('RABBITMQ_USER', 'guest'),
            'password' => env('RABBITMQ_PASSWORD', 'guest'),
            'vhost'    => env('RABBITMQ_VHOST', '/'),
        ]],
        'queue' => 'billing-service:' . QueueConnections::RESPONSE->value,
    ],
]
```

Очередь должна иметь префикс сервиса: `auth-clients:request`, `billing-service:request` и т.д.

---

Конфигурация пакета
-------------------

[](#конфигурация-пакета)

Файл `config/agelxnash-queue.php`:

```
return [
    'queue' => [
        // Таймаут ожидания ответа (секунды). -1 = бесконечное ожидание
        'timeout' => env('QUEUE_RESPONSE_TIMEOUT', 60),
    ],

    // Режим маршрутизации RPC-ответов
    'reply' => [
        'mode'            => env('QUEUE_RPC_REPLY_MODE', 'shared'),
        'per_request_ttl' => (int) env('QUEUE_RPC_PER_REQUEST_TTL', 60),
    ],

    // HMAC-подпись correlationId
    'hmac' => [
        'secret'    => env('QUEUE_HMAC_SECRET', ''),
        'algorithm' => 'sha256',
    ],

    // Circuit Breaker для RPC
    'circuit_breaker' => [
        'enabled'           => (bool) env('QUEUE_CIRCUIT_BREAKER_ENABLED', true),
        'failure_threshold' => (int) env('QUEUE_CIRCUIT_BREAKER_FAILURES', 5),
        'reset_timeout'     => (int) env('QUEUE_CIRCUIT_BREAKER_RESET', 30),
    ],

    // Allowlist job-классов
    'allowed_jobs' => [],
];
```

### Режимы маршрутизации ответов

[](#режимы-маршрутизации-ответов)

Режим`QUEUE_RPC_REPLY_MODE`Описание`shared``shared`Общая response-очередь сервиса (по умолчанию, backward compatible)`per_request``per_request`Отдельная временная очередь на каждый RPC-запрос (изоляция)`direct_reply_to``direct_reply_to`**Experimental** — сейчас fallback на `per_request`> **per\_request** требует драйвер `RabbitMQQueue`. Создаёт временную очередь с `x-expires` и `x-message-ttl` (настраивается через `QUEUE_RPC_PER_REQUEST_TTL`).

### Allowlist job-классов

[](#allowlist-job-классов)

Important

По умолчанию разрешён вызов любого алиаса/FQCN из контейнера. В production настройте `allowed_jobs`.

```
'allowed_jobs' => [
    // Маппинг алиаса → FQCN
    'TASK_CHECK_TARIFF' => \App\Jobs\CheckUserTariffJob::class,
    // Разрешить алиас как есть (должен быть забинжен в контейнере)
    'TRIGGER_EVENT'     => null,
],
```

Когда `allowed_jobs` не пуст — разрешены **только** ключи из массива.

### HMAC-подпись

[](#hmac-подпись)

По умолчанию **отключена** (пустой `secret`). Для включения:

```
QUEUE_HMAC_SECRET=your-shared-secret-here
```

Один и тот же секрет должен быть на **всех** RPC-сервисах. Формат подписанного ID: `{correlationId}.{hmac_hex}`.

### Circuit Breaker

[](#circuit-breaker)

После N последовательных таймаутов circuit открывается и RPC-вызовы мгновенно падают с `CircuitBreakerOpenException`. Через `reset_timeout` секунд — переход в half-open (одна пробная попытка).

```
QUEUE_CIRCUIT_BREAKER_ENABLED=true
QUEUE_CIRCUIT_BREAKER_FAILURES=5
QUEUE_CIRCUIT_BREAKER_RESET=30
```

---

Использование
-------------

[](#использование)

### RPC — ожидание ответа

[](#rpc--ожидание-ответа)

```
use AgelxNash\LaravelQueuePayload\Queue\ExternalJob;
use AgelxNash\LaravelQueuePayload\Queue\ExternalMessage;

$response = app(ExternalJob::class)->getResponse(
    message: new ExternalMessage(
        name: 'TASK_CHECK_TARIFF',
        params: ['userId' => 12345]
    ),
    queue: 'billing-service:request'
);

// $response — массив данных от сервиса-получателя
```

**Механика:** `getResponse` генерирует `correlationId`, публикует сообщение, затем через `ResponseWorker` (Fiber) слушает response-очередь до получения ответа с тем же `correlationId` или таймаута.

### Fire-and-Forget — без ожидания

[](#fire-and-forget--без-ожидания)

```
app(ExternalJob::class)->sendMessage(
    message: new ExternalMessage(
        name: 'EVENT_TARIFF_UPGRADED',
        params: ['userId' => 12345, 'tariff' => 'Premium']
    ),
    queue: 'notification-service:request'
);
```

### Fluent Builder

[](#fluent-builder)

```
use AgelxNash\LaravelQueuePayload\Queue\ExternalMessage;

$message = ExternalMessage::make('TASK_CHECK_TARIFF')
    ->param('userId', 12345)
    ->param('region', 'eu')
    ->handler('external')
    ->build();
```

Builder **immutable** — каждый вызов возвращает новый экземпляр.

### Получение задач и отправка ответа

[](#получение-задач-и-отправка-ответа)

На сервисе-получателе связываем алиас с классом в `ServiceProvider`:

```
app()->bind('TASK_CHECK_TARIFF', \App\Jobs\CheckUserTariffJob::class);
```

Job-класс:

```
namespace App\Jobs;

use Illuminate\Contracts\Queue\ShouldQueue;
use AgelxNash\LaravelQueuePayload\Queue\ExternalJob;
use AgelxNash\LaravelQueuePayload\Queue\ResponseMessage;

class CheckUserTariffJob implements ShouldQueue
{
    public function __construct(private readonly int $userId) {}

    public function handle(ExternalJob $externalJob): void
    {
        $tariff = ['id' => 1, 'name' => 'Premium'];

        // Ручная отправка ответа
        $responseQueue = $this->job->payload()['data'][ExternalJob::JOB_RESPONSE] ?? null;

        if (!empty($responseQueue)) {
            $externalJob->sendMessage(
                message: new ResponseMessage(
                    success: true,
                    data: $tariff,
                    metadata: ['process_time' => 0.1]
                ),
                queue: $responseQueue,
                correlationId: $this->job->getJobId()
            );
        }
    }
}
```

### Event Broadcasting

[](#event-broadcasting)

Отправка одного сообщения в несколько очередей (broadcast):

```
$externalJob = app(ExternalJob::class);
$externalJob->addSubscriber('service-a:request');
$externalJob->addSubscriber('service-b:request');

$externalJob->sendEvent(
    new ExternalMessage(
        name: 'EVENT_USER_CREATED',
        params: ['userId' => 42]
    )
);
```

### DTO для параметров

[](#dto-для-параметров)

Пакет поддерживает типизированные DTO через `DtoInterface`:

```
use AgelxNash\LaravelQueuePayload\Contracts\Queue\DtoInterface;

class CheckTariffDto implements DtoInterface
{
    public function __construct(
        public readonly int $userId,
        public readonly ?string $region = null,
    ) {}
}
```

Отправка:

```
$message = ExternalMessage::make('TASK_CHECK_TARIFF')
    ->param('payload', new CheckTariffDto(userId: 12345, region: 'eu'))
    ->build();
```

На стороне получателя DTO автоматически восстанавливается и передаётся в конструктор Job. Поддерживается рекурсивная десериализация вложенных DTO.

### Кастомная сериализация при dispatch()

[](#кастомная-сериализация-при-dispatch)

Для прозрачного вызова `Job::dispatch()` с автоматической конвертацией в кроссплатформенный JSON создайте кастомный коннектор. Подробности — в [docs/usage-rpc.md](docs/usage-rpc.md#%D0%BA%D0%B0%D1%81%D1%82%D0%BE%D0%BC%D0%BD%D0%B0%D1%8F-%D1%81%D0%B5%D1%80%D0%B8%D0%B0%D0%BB%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F-%D0%BF%D1%80%D0%B8-dispatch).

---

Observability
-------------

[](#observability)

Пакет генерирует Laravel-события для мониторинга:

СобытиеКогда`MessageSent`Сообщение опубликовано в очередь`ResponseReceived`Ответ получен (с `waitTime`)`ResponseTimeout`Превышен таймаут ожидания`CircuitBreakerOpened`Circuit Breaker перешёл в openПодписка:

```
Event::listen(\AgelxNash\LaravelQueuePayload\Events\ResponseReceived::class, function ($event) {
    Log::info('RPC response', [
        'correlationId' => $event->correlationId,
        'waitTime'      => $event->waitTime,
        'queue'         => $event->queue,
    ]);
});
```

---

Безопасность
------------

[](#безопасность)

- **Allowlist job** — ограничение списка вызываемых классов
- **HMAC-подпись** — защита correlationId от подделки
- **Валидация параметров** — ответственность вашей Job (используйте type-hinted конструкторы)
- **TLS + ACL RabbitMQ** — рекомендуется для production

Подробнее: [docs/security.md](docs/security.md)

---

Миграция shared → per\_request
------------------------------

[](#миграция-shared--per_request)

1. Убедитесь, что используется драйвер `rabbitmq` (пакет `vladimir-yuldashev/laravel-queue-rabbitmq`)
2. Задайте `QUEUE_RPC_REPLY_MODE=per_request` в `.env`
3. Настройте `QUEUE_RPC_PER_REQUEST_TTL` (по умолчанию 60 сек)
4. Удалите или оставьте пустым соединение `response` в `config/queue.php` — в режиме `per_request` временные очереди создаются динамически

> **direct\_reply\_to** — experimental, сейчас fallback на `per_request`. Не рекомендуется для production.

Подробнее: [docs/migration.md](docs/migration.md)

---

Troubleshooting
---------------

[](#troubleshooting)

### Response timeout exceeded

[](#response-timeout-exceeded)

**Причина:** Сервис-получатель не отправил ответ в течение таймаута.

**Решение:**

1. Увеличьте `QUEUE_RESPONSE_TIMEOUT`
2. Проверьте, что `php artisan queue:work request` запущен
3. Проверьте логи сервиса-получателя

### Job 'X' is not in the allowed jobs list

[](#job-x-is-not-in-the-allowed-jobs-list)

**Причина:** Job не найден в `allowed_jobs`.

**Решение:** Добавьте алиас в конфиг или очистите allowlist (пустой массив).

### per\_request reply mode requires RabbitMQQueue driver

[](#per_request-reply-mode-requires-rabbitmqqueue-driver)

**Причина:** Режим `per_request`/`direct_reply_to` требует драйвер RabbitMQ.

**Решение:** Используйте `driver => 'rabbitmq'` в `config/queue.php` или переключитесь на `shared`.

### Circuit breaker is open

[](#circuit-breaker-is-open)

**Причина:** Превышен порог ошибок RPC-вызовов.

**Решение:** Дождитесь `reset_timeout` или устраните причину таймаутов. Для отключения: `QUEUE_CIRCUIT_BREAKER_ENABLED=false`.

### Response worker shutdown requested

[](#response-worker-shutdown-requested)

**Причина:** Получен сигнал SIGTERM/SIGINT (graceful shutdown).

**Решение:** Ожидаемое поведение. RPC-вызов завершится с `MaxAttemptsQueueException`.

Подробнее: [docs/troubleshooting.md](docs/troubleshooting.md)

---

Документация / Wiki
-------------------

[](#документация--wiki)

ДокументОписание[docs/README.md](docs/README.md)Индекс документации[docs/architecture.md](docs/architecture.md)Архитектура пакета, компоненты, потоки данных[docs/configuration.md](docs/configuration.md)Полное описание всех настроек и ENV-переменных[docs/usage-rpc.md](docs/usage-rpc.md)RPC, Fire-and-Forget, Builder, DTO, кастомная сериализация[docs/usage-events.md](docs/usage-events.md)Event Broadcasting, триггер событий через Job-обёртку[docs/security.md](docs/security.md)HMAC, Allowlist, валидация, рекомендации[docs/observability.md](docs/observability.md)Observability Events, мониторинг, логирование[docs/testing.md](docs/testing.md)Тестирование, Docker Compose, моки[docs/troubleshooting.md](docs/troubleshooting.md)Типичные ошибки и решения[docs/migration.md](docs/migration.md)Миграция shared → per\_request/direct\_reply\_to### Подготовка GitHub Wiki

[](#подготовка-github-wiki)

Для генерации wiki-совместимого набора страниц из `docs/`:

```
bash scripts/sync-wiki.sh
```

По умолчанию результат будет создан в директории `.wiki/`.

Можно указать свой путь:

```
bash scripts/sync-wiki.sh /path/to/wiki-export
```

---

Лицензия
--------

[](#лицензия)

MIT License. Подробнее — [LICENSE](LICENSE).

###  Health Score

19

—

LowBetter than 10% of packages

Maintenance60

Regular maintenance activity

Popularity0

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity11

Early-stage or recently created project

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

### Community

Maintainers

![](https://www.gravatar.com/avatar/cd6eeb152aa8a6a81b05883f09f583f4fafbc50935495d79a2d8a01e0e5f9aa6?d=identicon)[Agel Nash](/maintainers/Agel%20Nash)

---

Top Contributors

[![AgelxNash](https://avatars.githubusercontent.com/u/1748872?v=4)](https://github.com/AgelxNash "AgelxNash (2 commits)")

### Embed Badge

![Health badge](/badges/agelxnash-laravel-queue-payload/health.svg)

```
[![Health](https://phpackages.com/badges/agelxnash-laravel-queue-payload/health.svg)](https://phpackages.com/packages/agelxnash-laravel-queue-payload)
```

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
