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.
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:
README.md — install notes and any Nova-version caveat.src/*ServiceProvider.php — the single source of truth for what is registered, published, and booted.fields()/component() — confirms it is a card and which Vue component it maps to.resources/js/ — the outbound HTTP call and rendering logic live here.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."
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.
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.src/ and confirm the actual behaviour before it touches NovaServiceProvider.fetched_at rather than implying the list is live. 2020-era popularity data is not a current signal.nova:publish actually registers the component; if the card renders blank, compare vendor/.../resources/js against your Nova version's asset expectations.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.app/ so the maintenance is legible to the team.How can I help you explore Laravel packages today?