Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message
short-url-development
jeffersongoncalves/laravel-short-url
claude-code
short-url
redirects
utm
analytics
custom-domains
ab-testing

Build URL shortening, link analytics, and redirect features using the jeffersongoncalves/laravel-short-url package

Install
php artisan boost:add-skill jeffersongoncalves/laravel-short-url

Save this content to: .claude/skills/short-url-development/SKILL.md

# Laravel Short URL Development

## When to use this skill

Use this skill when implementing short link creation, redirects, click analytics, A/B/split testing on destinations, targeting rules, custom domains, or conversion tracking in a Laravel application using the `jeffersongoncalves/laravel-short-url` package. The package is headless — no Filament dependency, no bundled admin UI — so every feature here is reached through the `ShortUrl` facade, its contracts, or console commands.

## Setup

```bash
composer require jeffersongoncalves/laravel-short-url
php artisan vendor:publish --tag="short-url-config"
php artisan vendor:publish --tag="short-url-migrations"
php artisan vendor:publish --tag="short-url-translations"
php artisan migrate
```

All package tables use the `short_url_` prefix (configurable via `table_prefix`). The redirect route (`GET /{urlKey}`) registers at the application root by default; set `short-url.route.prefix` or `short-url.route.fallback` to change that.

## Creating and resolving links

```php
use JeffersonGoncalves\LaravelShortUrl\Facades\ShortUrl;

// Plain array
$link = ShortUrl::create(['destination_url' => 'https://example.com/product']);

// Fluent builder — ShortUrlBuilder
$link = ShortUrl::destination('https://example.com/product')
    ->key('promo25')                       // explicit key, otherwise Base62-generated
    ->title('Spring promo')
    ->expiresAt(now()->addDays(30))
    ->maxVisits(1000)
    ->singleUse()                          // disables after first visit
    ->password('secret')                   // bcrypt-hashed, never stored plain
    ->redirectStatusCode(301)
    ->forwardQueryParams()
    ->create();

// Resolve by key (used internally by the redirect pipeline too)
$link = ShortUrl::resolve('promo25');

// Ready-to-share URL — custom domain when set, otherwise the app's own host
$link->fullUrl();
```

Redirecting itself needs no controller — any request to `GET /{urlKey}` already flows through the full pipeline. Don't hand-roll a redirect route; extend the pipeline stages instead (see Contracts below) if you need custom behavior.

## Campaign tagging (UTM)

Every `ShortUrl` has its own `utm_source`/`utm_medium`/`utm_campaign`/`utm_term`/`utm_content`, plus a `custom_domain_id`. Both are settable directly or via a reusable `UtmTemplate` (tenant-scoped — the closest thing this package has to a "campaign"):

@verbatim
<code-snippet name="Tagging a link via a UTM template" lang="php">
use JeffersonGoncalves\LaravelShortUrl\Models\UtmTemplate;

$campaign = UtmTemplate::create(['name' => 'Spring SMS', 'utm_medium' => 'sms']);

$link = ShortUrl::destination('https://example.com/product')
    ->utmTemplate($campaign->id)          // fills in unset utm_* fields
    ->utm(['utm_source' => 'agent-42'])   // explicit values always win
    ->customDomain($domain->id)
    ->create();
</code-snippet>
@endverbatim

These values get attached to the destination URL on redirect (`BuildFinalUrl` stage — `strip_utm_from_destination` drops the click's own incoming `utm_*` first if you want the link's tag to be the only one that lands), and become the default attribution on the recorded `Visit` whenever the click's own query string doesn't specify `utm_*` — so a link generated for one specific channel is still correctly attributed even if the person sharing it doesn't append query params.

Set `short-url.utm.required` (e.g. `['utm_medium']`) to make `ShortUrlManager::create()`/`update()` reject a link that doesn't declare those fields — directly, or via a template. This is enforced once, in the manager, so it applies uniformly across the facade, builder, and every importer (`CsvImporterDriver`, `BitlyImporterDriver`) — never re-implement this check at a call site.

## Destination types

`ShortUrl::destination_type` is one of `single`, `split`, or `rules`.

@verbatim
<code-snippet name="Split (weighted A/B) destination" lang="php">
ShortUrl::create([
    'destination_url' => 'https://example.com/base', // fallback
    'destination_type' => 'split',
    'rotation_variants' => [
        ['url' => 'https://a.test', 'weight' => 50, 'label' => 'A'],
        ['url' => 'https://b.test', 'weight' => 50, 'label' => 'B'],
    ],
]);
</code-snippet>
@endverbatim

@verbatim
<code-snippet name="Rules (conditional targeting) destination" lang="php">
ShortUrl::create([
    'destination_url' => 'https://example.com/base', // used when no rule matches
    'destination_type' => 'rules',
    'targeting_rules' => [
        [
            'conditions' => [
                ['type' => 'country', 'value' => 'FR'],
                ['type' => 'device', 'value' => 'mobile'],
            ],
            // conditions default to AND; wrap in ['or' => [...]] for OR groups
            'destination' => 'https://example.com/france-mobile',
        ],
    ],
]);
</code-snippet>
@endverbatim

Condition `type` accepts: `device`, `platform`, `browser`, `country`, `language`, `referer`, `utm_source`/`utm_medium`/`utm_campaign`, a date/time window, `visit_count`, `vpn`, `bot`. A rule's `destination` can itself be a nested `split` array for combined targeting + rotation. `ShortUrl::resolve()` never evaluates rules — that happens inside the redirect pipeline's `ResolveDestination` stage against the live request; call `app(TargetingResolver::class)->resolve($shortUrl, $request)` directly if you need the same resolution outside a request.

## The redirect pipeline

```
ResolveHost → RateLimit → ResolveShortUrl(cache) → DetectBot → DetectVpnProxy
→ CheckAvailability → RequirePassword → ShowWarning → ResolveDestination
→ BuildFinalUrl → RenderInterstitial → Respond → DispatchTracking
```

Each stage is an invokable class under `src/Pipeline/Stages/` and can short-circuit by returning a `Response` directly (wrong password, expired link, blocked VPN, plan limit exceeded). Add a custom stage by extending `RedirectPipeline`'s stage array via the container — do not bypass the pipeline by writing your own redirect controller, since caching, tracking, and every security check live inside it.

## Analytics and tracking

Visits are recorded asynchronously (`TrackShortUrlVisitJob`) after the redirect response is sent — tracking failures never block or slow a redirect. Query aggregated stats through `Contracts\StatsAggregator` or the REST API rather than querying `short_url_visits` directly, since raw rows get pruned by `short-url:aggregate-and-prune` after being folded into `short_url_daily_stats`.

```php
use JeffersonGoncalves\LaravelShortUrl\Contracts\StatsAggregator;

// Single link
$stats = app(StatsAggregator::class)->for($shortUrl)->between(now()->subDays(7), now())->get();

// Across a set of links (a folder, a tag, "everything this tenant owns") —
// for a dashboard's global breakdown. Resolving *which* links belong in the
// set is always the caller's job via ShortUrl's own tenant-scoped query;
// StatsAggregator only does the aggregation math, never link selection.
$ids = ShortUrl::query()->where('folder_id', $folder->id)->pluck('id')->all();
$stats = app(StatsAggregator::class)->forShortUrls($ids)->between(now()->subDays(7), now())->get();
```

Use `forShortUrls()` instead of querying `short_url_visits`/`short_url_daily_stats` yourself for a cross-link breakdown; a plugin or app built on this package should never compute its own aggregation.

External forwarding is config-gated per provider under `short-url.analytics.*` (`enabled` + credentials) — GA4, Plausible, PostHog, Matomo, Umami, Mixpanel, Segment ship built in. Add another provider with `AnalyticsDriverRegistry::extend()` in a service provider's `boot()`, implementing `Contracts\AnalyticsDriver::record(array $visit): void`.

## Conversion tracking

Recording a conversion always persists it locally; `short-url.conversions.driver` (`meta`, `google`, `tiktok`, `linkedin`, or `none`) additionally forwards it server-to-server via `Contracts\ConversionApiDispatcher`. A dispatcher failure never throws back to the caller — it's caught and reported.

## Custom domains

Off by default (`short-url.domains.enabled`) so a plain install never pays for the extra lookup on every redirect. A domain must pass DNS verification (TXT/CNAME/A, via `Contracts\DnsVerifier`) before it routes traffic; `short-url:verify-domains` re-checks periodically and auto-disables a domain after `max_verification_failures` consecutive failures.

`custom_domain_id` on `short_url` is `NOT NULL` with sentinel `0` meaning "no custom domain" (not `NULL`) — this is deliberate: `NULL` is never equal to `NULL` in a composite unique index, which would silently break `unique(custom_domain_id, url_key)` for the common root-level case. Never write `custom_domain_id => null` directly; pass `null` through `ShortUrl::create()`/the builder and it's coerced to `0` for you.

## Security

- **Password protection**: `->password($plain)` bcrypt-hashes on the builder; the pipeline's `RequirePassword` stage renders a prompt and unlocks via a signed, session-scoped token.
- **Safe Browsing**: `short-url.security.safe_browsing` — sync (blocks the redirect) or async (`short-url:check-safe-browsing` flags links after the fact) via `Contracts\SafeBrowsingChecker`.
- **VPN/proxy detection**: `Contracts\VpnDetectionDriver` (`ip_api` or `proxycheck_io`), flag-only or 403-block via config.
- **Rate limiting**: per-IP, config-driven, applied in the pipeline's `RateLimit` stage.
- **Audit trail**: every create/update/delete on `ShortUrl` and `CustomDomain` is recorded with before/after diffs via `AuditLogObserver` — never disable this without checking `short-url.audit` first.

## Multi-tenancy

Entirely feature-flagged (`short-url.tenancy.enabled`), off by default and a complete no-op when off. When on, the tenant id resolves via a bound `Contracts\TenantResolver` if the host app has one, else `stancl/tenancy`'s `tenant()` helper if installed, else `short-url.tenancy.current_tenant_id`. Per-plan limits (`links_per_month`, `domains`, `retention_days`) live in `short-url.tenancy.plans.*` and are resolved through `Tenancy\PlanLimits`; a plan is picked by a bound `Contracts\PlanResolver`, defaulting to `"default"` when none is bound. Both are container bindings, not config Closures, because `php artisan config:cache` can't serialize a Closure. `short-url:aggregate-and-prune` applies each tenant's own `retention_days` when tenancy is enabled, falling back to the package-wide `short-url.tracking.retention_days` otherwise.

A host app with its own domain→tenant mapping can bind `Contracts\CustomDomainResolver` (`resolve(string $host): ?CustomDomain`) to skip `short_url_custom_domains` entirely — `Pipeline\Stages\ResolveHost` calls it instead of `CustomDomain::forHost()` when bound. The returned `CustomDomain` doesn't need to be persisted.

## Events

| Event | Properties |
|-------|-----------|
| `ShortUrlVisited` | `$shortUrl`, `$visit` |
| `ConversionRecorded` | `$conversion` |
| `AlertTriggered` | anomaly alert details (Z-score anomaly detection against a 7-day baseline) |

## Extending the package (contracts)

Every swappable piece is an interface under `src/Contracts/`, bound to a default implementation in `LaravelShortUrlServiceProvider`. Rebind in your own service provider to replace one, or use a registry's `extend()` to add alongside the defaults:

```php
VisitRepository, GeoIpDriver, VpnDetectionDriver, AnalyticsDriver,
SafeBrowsingChecker, StatsAggregator, TargetingResolver,
DnsVerifier, SettingsRepository, ImporterDriver,
ConversionApiDispatcher, TenantResolver, PlanResolver, CustomDomainResolver

// Registries (extend() instead of rebind — multiple drivers coexist)
FilterTypeRegistry, AnalyticsDriverRegistry,
PixelProviderRegistry, ImporterDriverRegistry
```

`VisitRepository` also ships a ClickHouse driver (`short-url.tracking.driver = clickhouse`) — same contract, no client library dependency, talks to ClickHouse's native HTTP interface directly. Use it when visit volume outgrows a relational `short_url_visits` table.

## Artisan commands

| Command | Frequency |
| --- | --- |
| `short-url:sync-counters` | every minute (when `tracking.counter_buffering` is on) |
| `short-url:aggregate-and-prune` | daily at 02:00 — folds visits into daily stats, then prunes |
| `short-url:verify-domains` | every 6h |
| `short-url:check-safe-browsing` | daily |
| `short-url:detect-anomalies` | hourly |
| `short-url:send-scheduled-reports` | daily |
| `short-url:import {driver} {source}` | manual — `csv` ships in, `bitly` is the reference per-provider importer |

All self-register with the scheduler in `packageBooted()`, respecting their own config toggles — you don't need to add them to your `Kernel`/`bootstrap/app.php` schedule yourself.

## Testing your own code against this package

Use Pest with `RefreshDatabase` and Orchestra Testbench (see the package's own `tests/TestCase.php` for the pattern). On Postgres specifically, wrap any assertion that expects a `QueryException` (e.g. a uniqueness constraint) in `DB::transaction()` — Postgres aborts the whole enclosing transaction on any uncaught error until a rollback, which poisons whatever query `RefreshDatabase` runs next in the same test; `DB::transaction()` uses a `SAVEPOINT` instead and contains the failure.

Laravel Short URL Development

When to use this skill

Use this skill when implementing short link creation, redirects, click analytics, A/B/split testing on destinations, targeting rules, custom domains, or conversion tracking in a Laravel application using the jeffersongoncalves/laravel-short-url package. The package is headless — no Filament dependency, no bundled admin UI — so every feature here is reached through the ShortUrl facade, its contracts, or console commands.

Setup

composer require jeffersongoncalves/laravel-short-url
php artisan vendor:publish --tag="short-url-config"
php artisan vendor:publish --tag="short-url-migrations"
php artisan vendor:publish --tag="short-url-translations"
php artisan migrate

All package tables use the short_url_ prefix (configurable via table_prefix). The redirect route (GET /{urlKey}) registers at the application root by default; set short-url.route.prefix or short-url.route.fallback to change that.

Creating and resolving links

use JeffersonGoncalves\LaravelShortUrl\Facades\ShortUrl;

// Plain array
$link = ShortUrl::create(['destination_url' => 'https://example.com/product']);

// Fluent builder — ShortUrlBuilder
$link = ShortUrl::destination('https://example.com/product')
    ->key('promo25')                       // explicit key, otherwise Base62-generated
    ->title('Spring promo')
    ->expiresAt(now()->addDays(30))
    ->maxVisits(1000)
    ->singleUse()                          // disables after first visit
    ->password('secret')                   // bcrypt-hashed, never stored plain
    ->redirectStatusCode(301)
    ->forwardQueryParams()
    ->create();

// Resolve by key (used internally by the redirect pipeline too)
$link = ShortUrl::resolve('promo25');

// Ready-to-share URL — custom domain when set, otherwise the app's own host
$link->fullUrl();

Redirecting itself needs no controller — any request to GET /{urlKey} already flows through the full pipeline. Don't hand-roll a redirect route; extend the pipeline stages instead (see Contracts below) if you need custom behavior.

Campaign tagging (UTM)

Every ShortUrl has its own utm_source/utm_medium/utm_campaign/utm_term/utm_content, plus a custom_domain_id. Both are settable directly or via a reusable UtmTemplate (tenant-scoped — the closest thing this package has to a "campaign"):

@verbatim use JeffersonGoncalves\LaravelShortUrl\Models\UtmTemplate;

$campaign = UtmTemplate::create(['name' => 'Spring SMS', 'utm_medium' => 'sms']);

$link = ShortUrl::destination('https://example.com/product') ->utmTemplate($campaign->id) // fills in unset utm_* fields ->utm(['utm_source' => 'agent-42']) // explicit values always win ->customDomain($domain->id) ->create(); @endverbatim

These values get attached to the destination URL on redirect (BuildFinalUrl stage — strip_utm_from_destination drops the click's own incoming utm_* first if you want the link's tag to be the only one that lands), and become the default attribution on the recorded Visit whenever the click's own query string doesn't specify utm_* — so a link generated for one specific channel is still correctly attributed even if the person sharing it doesn't append query params.

Set short-url.utm.required (e.g. ['utm_medium']) to make ShortUrlManager::create()/update() reject a link that doesn't declare those fields — directly, or via a template. This is enforced once, in the manager, so it applies uniformly across the facade, builder, and every importer (CsvImporterDriver, BitlyImporterDriver) — never re-implement this check at a call site.

Destination types

ShortUrl::destination_type is one of single, split, or rules.

@verbatim ShortUrl::create([ 'destination_url' => 'https://example.com/base', // fallback 'destination_type' => 'split', 'rotation_variants' => [ ['url' => 'https://a.test', 'weight' => 50, 'label' => 'A'], ['url' => 'https://b.test', 'weight' => 50, 'label' => 'B'], ], ]); @endverbatim

@verbatim ShortUrl::create([ 'destination_url' => 'https://example.com/base', // used when no rule matches 'destination_type' => 'rules', 'targeting_rules' => [ [ 'conditions' => [ ['type' => 'country', 'value' => 'FR'], ['type' => 'device', 'value' => 'mobile'], ], // conditions default to AND; wrap in ['or' => [...]] for OR groups 'destination' => 'https://example.com/france-mobile', ], ], ]); @endverbatim

Condition type accepts: device, platform, browser, country, language, referer, utm_source/utm_medium/utm_campaign, a date/time window, visit_count, vpn, bot. A rule's destination can itself be a nested split array for combined targeting + rotation. ShortUrl::resolve() never evaluates rules — that happens inside the redirect pipeline's ResolveDestination stage against the live request; call app(TargetingResolver::class)->resolve($shortUrl, $request) directly if you need the same resolution outside a request.

The redirect pipeline

ResolveHost → RateLimit → ResolveShortUrl(cache) → DetectBot → DetectVpnProxy
→ CheckAvailability → RequirePassword → ShowWarning → ResolveDestination
→ BuildFinalUrl → RenderInterstitial → Respond → DispatchTracking

Each stage is an invokable class under src/Pipeline/Stages/ and can short-circuit by returning a Response directly (wrong password, expired link, blocked VPN, plan limit exceeded). Add a custom stage by extending RedirectPipeline's stage array via the container — do not bypass the pipeline by writing your own redirect controller, since caching, tracking, and every security check live inside it.

Analytics and tracking

Visits are recorded asynchronously (TrackShortUrlVisitJob) after the redirect response is sent — tracking failures never block or slow a redirect. Query aggregated stats through Contracts\StatsAggregator or the REST API rather than querying short_url_visits directly, since raw rows get pruned by short-url:aggregate-and-prune after being folded into short_url_daily_stats.

use JeffersonGoncalves\LaravelShortUrl\Contracts\StatsAggregator;

// Single link
$stats = app(StatsAggregator::class)->for($shortUrl)->between(now()->subDays(7), now())->get();

// Across a set of links (a folder, a tag, "everything this tenant owns") —
// for a dashboard's global breakdown. Resolving *which* links belong in the
// set is always the caller's job via ShortUrl's own tenant-scoped query;
// StatsAggregator only does the aggregation math, never link selection.
$ids = ShortUrl::query()->where('folder_id', $folder->id)->pluck('id')->all();
$stats = app(StatsAggregator::class)->forShortUrls($ids)->between(now()->subDays(7), now())->get();

Use forShortUrls() instead of querying short_url_visits/short_url_daily_stats yourself for a cross-link breakdown; a plugin or app built on this package should never compute its own aggregation.

External forwarding is config-gated per provider under short-url.analytics.* (enabled + credentials) — GA4, Plausible, PostHog, Matomo, Umami, Mixpanel, Segment ship built in. Add another provider with AnalyticsDriverRegistry::extend() in a service provider's boot(), implementing Contracts\AnalyticsDriver::record(array $visit): void.

Conversion tracking

Recording a conversion always persists it locally; short-url.conversions.driver (meta, google, tiktok, linkedin, or none) additionally forwards it server-to-server via Contracts\ConversionApiDispatcher. A dispatcher failure never throws back to the caller — it's caught and reported.

Custom domains

Off by default (short-url.domains.enabled) so a plain install never pays for the extra lookup on every redirect. A domain must pass DNS verification (TXT/CNAME/A, via Contracts\DnsVerifier) before it routes traffic; short-url:verify-domains re-checks periodically and auto-disables a domain after max_verification_failures consecutive failures.

custom_domain_id on short_url is NOT NULL with sentinel 0 meaning "no custom domain" (not NULL) — this is deliberate: NULL is never equal to NULL in a composite unique index, which would silently break unique(custom_domain_id, url_key) for the common root-level case. Never write custom_domain_id => null directly; pass null through ShortUrl::create()/the builder and it's coerced to 0 for you.

Security

  • Password protection: ->password($plain) bcrypt-hashes on the builder; the pipeline's RequirePassword stage renders a prompt and unlocks via a signed, session-scoped token.
  • Safe Browsing: short-url.security.safe_browsing — sync (blocks the redirect) or async (short-url:check-safe-browsing flags links after the fact) via Contracts\SafeBrowsingChecker.
  • VPN/proxy detection: Contracts\VpnDetectionDriver (ip_api or proxycheck_io), flag-only or 403-block via config.
  • Rate limiting: per-IP, config-driven, applied in the pipeline's RateLimit stage.
  • Audit trail: every create/update/delete on ShortUrl and CustomDomain is recorded with before/after diffs via AuditLogObserver — never disable this without checking short-url.audit first.

Multi-tenancy

Entirely feature-flagged (short-url.tenancy.enabled), off by default and a complete no-op when off. When on, the tenant id resolves via a bound Contracts\TenantResolver if the host app has one, else stancl/tenancy's tenant() helper if installed, else short-url.tenancy.current_tenant_id. Per-plan limits (links_per_month, domains, retention_days) live in short-url.tenancy.plans.* and are resolved through Tenancy\PlanLimits; a plan is picked by a bound Contracts\PlanResolver, defaulting to "default" when none is bound. Both are container bindings, not config Closures, because php artisan config:cache can't serialize a Closure. short-url:aggregate-and-prune applies each tenant's own retention_days when tenancy is enabled, falling back to the package-wide short-url.tracking.retention_days otherwise.

A host app with its own domain→tenant mapping can bind Contracts\CustomDomainResolver (resolve(string $host): ?CustomDomain) to skip short_url_custom_domains entirely — Pipeline\Stages\ResolveHost calls it instead of CustomDomain::forHost() when bound. The returned CustomDomain doesn't need to be persisted.

Events

Event Properties
ShortUrlVisited $shortUrl, $visit
ConversionRecorded $conversion
AlertTriggered anomaly alert details (Z-score anomaly detection against a 7-day baseline)

Extending the package (contracts)

Every swappable piece is an interface under src/Contracts/, bound to a default implementation in LaravelShortUrlServiceProvider. Rebind in your own service provider to replace one, or use a registry's extend() to add alongside the defaults:

VisitRepository, GeoIpDriver, VpnDetectionDriver, AnalyticsDriver,
SafeBrowsingChecker, StatsAggregator, TargetingResolver,
DnsVerifier, SettingsRepository, ImporterDriver,
ConversionApiDispatcher, TenantResolver, PlanResolver, CustomDomainResolver

// Registries (extend() instead of rebind — multiple drivers coexist)
FilterTypeRegistry, AnalyticsDriverRegistry,
PixelProviderRegistry, ImporterDriverRegistry

VisitRepository also ships a ClickHouse driver (short-url.tracking.driver = clickhouse) — same contract, no client library dependency, talks to ClickHouse's native HTTP interface directly. Use it when visit volume outgrows a relational short_url_visits table.

Artisan commands

Command Frequency
short-url:sync-counters every minute (when tracking.counter_buffering is on)
short-url:aggregate-and-prune daily at 02:00 — folds visits into daily stats, then prunes
short-url:verify-domains every 6h
short-url:check-safe-browsing daily
short-url:detect-anomalies hourly
short-url:send-scheduled-reports daily
short-url:import {driver} {source} manual — csv ships in, bitly is the reference per-provider importer

All self-register with the scheduler in packageBooted(), respecting their own config toggles — you don't need to add them to your Kernel/bootstrap/app.php schedule yourself.

Testing your own code against this package

Use Pest with RefreshDatabase and Orchestra Testbench (see the package's own tests/TestCase.php for the pattern). On Postgres specifically, wrap any assertion that expects a QueryException (e.g. a uniqueness constraint) in DB::transaction() — Postgres aborts the whole enclosing transaction on any uncaught error until a rollback, which poisons whatever query RefreshDatabase runs next in the same test; DB::transaction() uses a SAVEPOINT instead and contains the failure.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
mehedimi/laravel-amp-mail
happones/laravel-vue-i18n-generator
jwerd/inertia-table
siberfx/flysystem-wasabi
hollyit/token-replace
ippanelcom/laravel-sdk
adachsoft/changelog-linter
adachsoft/collection
payerurl/telegrambot-crypto-payment-instant-payout
dskripchenko/laravel-hybrid-search
riorizkyrainey/lara-ced
mehedimi/static-vel
zharp/zharp
nonsapiens/bigquery-model-sync
lgu-bayambang-ict-office/lgu-laravel-development-standards
andydefer/laravel-support
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel