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

Nova Package Discovery Laravel Package

tightenco/nova-package-discovery

Automatically discovers and registers Laravel Nova packages installed via Composer, so tools, cards, and resources from third-party packages load without manual registration in your NovaServiceProvider—keeping your Nova setup clean and effortless.

View on GitHub
Deep Wiki
Context7

Getting Started

Gate on compatibility before anything else. The only release is v1.0.0 (2020-09-12), targeting Laravel 7+ and the Nova 2/3 era (PHP 7.2–7.4). Resolve it against your real lockfile in a throwaway branch, never on main:

composer require tightenco/nova-package-discovery --dry-run

If Composer reports conflicts on laravel/nova, laravel/framework, or guzzlehttp/guzzle, stop. You are on Nova 4/5 and the correct move is to treat this repo as a design reference and build the card yourself. If it resolves cleanly, you are on a legacy Nova 2/3 app and can proceed — with a named owner and a sunset date.

Resolve the documentation conflict before wiring anything. The package description claims it auto-discovers installed Nova packages and registers them with Nova (i.e. it replaces NovaServiceProvider boilerplate). The release history and the technical assessments describe a read-only discovery card that surfaces community packages in the dashboard. Those are completely different behaviours and the latter is what the name implies. Open the source and confirm which one you actually installed:

ls vendor/tightenco/nova-package-discovery/src/
cat vendor/tightenco/nova-package-discovery/src/*ServiceProvider.php
grep -rn "Http\|Guzzle\|Cache\|cards\|tools" vendor/tightenco/nova-package-discovery/src/

Everything below assumes the card behaviour. If it turns out to register tools, your first task is auditing what it registers into your admin plane.

Register the card. In app/Providers/NovaServiceProvider.php:

public function cards()
{
    return [
        // ... existing cards
        new \Tighten\NovaPackageDiscovery\NovaPackageDiscovery(),
    ];
}

Publish and clear caches.

php artisan vendor:publish --provider="Tighten\NovaPackageDiscovery\NovaPackageDiscoveryServiceProvider"
php artisan nova:publish
php artisan config:clear
php artisan view:clear

Where to look first, in order:

  1. README.md — install notes and any Nova-version caveat.
  2. src/*ServiceProvider.php — the single source of truth for what is registered, published, and booted.
  3. The card class and its fields()/component() — confirms it is a card and which Vue component it maps to.
  4. resources/js/ — the outbound HTTP call and rendering logic live here.
  5. composer.json — the constraint set you are about to inherit.

First use case. A disposable internal awareness widget behind a feature flag, on a legacy stack, with caching and a kill switch — or, far more likely, a 30-minute read to lift the pattern: external index → cache → Nova card meta. Do not open with "add it as a dependency."

Implementation Patterns

Pattern 1 — Reference implementation, not a dependency. The durable asset is the shape, not the code. Read the card, extract the data contract and the UX, then rebuild it in-house in roughly 200 lines as a laravel/nova card:

// app/Nova/Cards/PackageDiscovery.php
class PackageDiscovery extends Card
{
    public function component() { return 'package-discovery'; }

    public function meta()
    {
        return [
            'packages' => Cache::remember('nova.package-discovery', now()->addHours(6), function () {
                return Http::timeout(5)
                    ->retry(1, 200)
                    ->get('https://packagist.org/search.json', ['q' => 'nova', 'per_page' => 20])
                    ->throw()
                    ->json('results', []);
            }),
            'fetched_at' => now()->toIso8601String(),
            'enabled'    => (bool) config('services.package_discovery.enabled', false),
        ];
    }
}

That single class removes the abandoned dependency, the Composer constraint risk, and the node-toolchain rebuild problem while preserving the product idea.

Pattern 2 — Fork with an explicit alias. If you genuinely must adopt the code (legacy constraint resolves, someone owns it):

{
  "repositories": [
    { "type": "vcs", "url": "[email protected]:yourorg/nova-package-discovery.git" }
  ],
  "require": {
    "tightenco/nova-package-discovery": "dev-main as 1.0.0"
  }
}

Pin to the fork, not upstream. Budget recurring work at every Laravel/Nova upgrade even though the feature itself will never change.

Pattern 3 — Cache at the boundary, degrade gracefully. The most common failure is a dashboard that blocks on a dead endpoint. Never let the card make a per-render network call. Cache with an hours-scale TTL, wrap in try/catch, and render a neutral "unavailable" state instead of throwing:

public function meta()
{
    if (! config('services.package_discovery.enabled')) {
        return ['enabled' => false];
    }

    try {
        $packages = Cache::remember('nova.package-discovery', 21600, fn () => $this->fetch());
    } catch (\Throwable $e) {
        report($e);
        $packages = Cache::get('nova.package-discovery', []); // serve stale, don't die
    }

    return ['enabled' => true, 'packages' => $packages, 'fetched_at' => ...];
}

Pattern 4 — Feature flag and kill switch from day one. Nova cards are registered in PHP, so gate at registration and in meta():

public function cards()
{
    return array_filter([
        config('services.package_discovery.enabled')
            ? new \Tighten\NovaPackageDiscovery\NovaPackageDiscovery()
            : null,
    ]);
}

Pattern 5 — Test the network boundary. If you fork, add Http::fake() coverage before anything else, so the upstream contract is at least documented in code:

Http::fake([
    '*/search.json*' => Http::response(['results' => [['name' => 'acme/nova-widget']]], 200),
]);
Http::fake(['*' => Http::response(null, 500)]); // assert graceful degradation

Pattern 6 — Treat ownership as a deliverable. Record the decision in the tech radar ("avoid — archived"), name an owner, and set a sunset trigger (next Nova major, or the first blocked composer update). This is cheap and prevents the package resurfacing from a "recommended Nova tools" listicle.

Gotchas and Tips

  • Archived means you own it entirely. Zero upstream maintenance, no issue triage, no security patches, no vendor, and 15 stars/0 dependents means no crowd-sourced fixes either. Every incident routes to your on-call with no escalation path.
  • The highest-severity risk is upgrade drag, not runtime. A trivial admin card can pin guzzlehttp/guzzle or laravel/framework transitively and block the whole platform's next Nova/Laravel upgrade. Always composer why-not laravel/nova after install, and treat any output as a stop signal.
  • Docs are unreliable. The description advertises auto-discovery of installed packages; the assessments describe a discovery card. Do not copy a registration snippet from a blog post — read src/ and confirm the actual behaviour before it touches NovaServiceProvider.
  • Per-render HTTP is the classic silent failure. Endpoint changes, rate limits, or retirement leave an empty card with nobody upstream to fix it. Require caching, a timeout, and a fallback path.
  • Stale data presented as fresh misleads admins. If you keep the card, render an explicit "as of" timestamp from fetched_at rather than implying the list is live. 2020-era popularity data is not a current signal.
  • Published assets will be stale relative to Nova 4/5 conventions. Rebuilding may require an old Node toolchain — itself a real cost. Check whether nova:publish actually registers the component; if the card renders blank, compare vendor/.../resources/js against your Nova version's asset expectations.
  • Egress-locked environments break silently. If outbound HTTP is blocked, the card errors or times out on the dashboard. Test with egress denied and confirm the graceful-degradation path fires.
  • Debugging order: php artisan config:clear && php artisan view:clear → php artisan nova:publish → check storage/logs/laravel.log for HTTP exceptions → php artisan tinker to call the fetch path directly → verify the component name matches in the browser console.
  • Do not add vendor code to git or vendor the package. If you fork, fork properly with a VCS entry and an explicit alias; if you reimplement, keep it in app/ so the maintenance is legible to the team.
  • Extension points are effectively nil. There is one release and no configuration surface worth hooking. Anything you need — different index, different metadata, different card placement — is a fork or a rewrite. Choose the rewrite; it is almost always cheaper than owning unfamiliar, abandoned code across the next Nova major.
  • Don't let it become a roadmap input. The popularity signal stopped moving in September 2020. Use it to validate the hypothesis ("do admins benefit from in-product package discovery?"), never as evidence of current ecosystem momentum.
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.
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package